본문 바로가기

자바(Java)

코드로 보는 SOLID 원칙

반응형

1. 단일 책임 원칙 (Single Responsibility Principle - SRP)

단일 책임 원칙(SRP, Single Responsibility Principle)"클래스는 단 하나의 책임만 가져야 하며, 변경의 이유가 하나여야 한다."는 원칙이다.

즉, 하나의 클래스가 너무 많은 역할을 담당하면 유지보수가 어려워지고, 코드가 변경될 때 예기치 않은 문제가 발생할 가능성이 높아진다. SRP를 준수하면 각 클래스가 하나의 기능만 담당하여 코드의 가독성과 유지보수성이 향상된다.

 

SRP를 위반한 코드 (잘못된 예시)

🚨 잘못된 설계: Employee 클래스가 너무 많은 역할을 담당

class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    // 🔴 급여 계산 기능 (책임 1)
    public double calculateBonus() {
        return salary * 0.1;
    }

    // 🔴 데이터베이스 저장 기능 (책임 2)
    public void saveToDatabase() {
        System.out.println(name + "을(를) 데이터베이스에 저장했습니다.");
    }

    // 🔴 보고서 생성 기능 (책임 3)
    public void generateReport() {
        System.out.println(name + "의 급여 보고서를 생성했습니다.");
    }
}

// 실행 코드
public class Main {
    public static void main(String[] args) {
        Employee emp = new Employee("홍길동", 5000);
        System.out.println("보너스: " + emp.calculateBonus());
        emp.saveToDatabase();
        emp.generateReport();
    }
}

문제점 (SRP 위반)

  • Employee 클래스가 "급여 계산", "데이터베이스 저장", "보고서 생성" 기능을 모두 포함하고 있음. → 책임이 여러 개라서 유지보수가 어려움.
  • 한 기능(saveToDatabase())을 수정할 때, 다른 기능(calculateBonus(), generateReport())에 영향을 미칠 가능성이 있음.
  • 급여 계산 로직이 변경될 경우, Employee 클래스를 직접 수정해야 하므로 OCP(Open-Closed Principle)도 위반할 가능성이 큼.
  • 데이터 저장 방식이 변경되면 Employee 클래스를 수정해야 함. (ex: MySQL → MongoDB 변경)

SRP를 준수한 코드 (올바른 예시)

🛠 개선된 설계: 역할을 분리하여 각각의 클래스가 단일 책임을 가짐

// 직원 정보 관리 (급여 계산 책임 없음)
class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public String getName() {
        return name;
    }
}

// 🔹 급여 계산 기능을 별도의 클래스로 분리
class BonusCalculator {
    public double calculateBonus(Employee employee) {
        return employee.getSalary() * 0.1;
    }
}

// 🔹 데이터 저장 기능을 별도의 클래스로 분리
class EmployeeDatabase {
    public void save(Employee employee) {
        System.out.println(employee.getName() + "을(를) 데이터베이스에 저장했습니다.");
    }
}

// 🔹 보고서 생성 기능을 별도의 클래스로 분리
class EmployeeReport {
    public void generateReport(Employee employee) {
        System.out.println(employee.getName() + "의 급여 보고서를 생성했습니다.");
    }
}

// 실행 코드
public class Main {
    public static void main(String[] args) {
        Employee emp = new Employee("홍길동", 5000);

        BonusCalculator bonusCalculator = new BonusCalculator();
        System.out.println("보너스: " + bonusCalculator.calculateBonus(emp));

        EmployeeDatabase db = new EmployeeDatabase();
        db.save(emp);

        EmployeeReport report = new EmployeeReport();
        report.generateReport(emp);
    }
}

SRP 준수로 개선된 점

  • 각 클래스가 단 하나의 책임만 가짐
    • Employee → 직원 정보만 저장 (급여, 이름 등)
    • BonusCalculator → 급여 보너스 계산
    • EmployeeDatabase → 데이터베이스 저장
    • EmployeeReport → 보고서 생성
  • 변경에 유연함
    • 급여 계산 로직이 변경되더라도 Employee 클래스에는 영향을 미치지 않음.
    • 데이터 저장 방식(MySQL → MongoDB) 변경 시 EmployeeDatabase 클래스만 수정하면 됨.
  • 유지보수가 쉬워짐
    • 특정 기능에 대한 코드만 수정하면 되므로 리팩토링 및 버그 수정이 용이함.

🔥 결론

  • SRP 위반 코드: Employee 클래스가 너무 많은 책임을 가지고 있음. → 코드가 복잡해지고, 유지보수 및 확장성이 떨어짐.
  • SRP 준수 코드: BonusCalculator, EmployeeDatabase, EmployeeReport 클래스로 역할을 분리하여 각 클래스가 단일 책임을 가짐. → 유지보수성이 좋아지고, 코드가 변경될 때 최소한의 수정만 필요함.

SRP를 준수하면 각 클래스가 하나의 역할만 담당하여 코드가 깔끔하고 유지보수가 쉬워진다! 🚀

 

2. 개방-폐쇄 원칙 (Open-Closed Principle - OCP)

개방-폐쇄 원칙(OCP, Open-Closed Principle)"소프트웨어 구성 요소(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하지만, 변경에는 닫혀 있어야 한다."는 원칙이다.

즉, 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있도록 설계해야 한다. 이를 위해 인터페이스나 추상 클래스를 활용하여 유연성을 확보하는 것이 핵심이다.

 

OCP를 위반한 코드 (잘못된 예시)

🚨 잘못된 설계: calculateBonus()가 새로운 직원 유형 추가 시 수정이 필요함

class Employee {
    protected String name;
    protected double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }
}

// 급여 보너스를 계산하는 클래스
class BonusCalculator {
    public double calculateBonus(Employee employee, String employeeType) {
        if (employeeType.equals("Regular")) {
            return employee.getSalary() * 0.1; // 정규직 보너스: 10%
        } else if (employeeType.equals("Manager")) {
            return employee.getSalary() * 0.2; // 매니저 보너스: 20%
        }
        return 0; // 기본값
    }
}

public class Main {
    public static void main(String[] args) {
        Employee emp1 = new Employee("홍길동", 5000);
        BonusCalculator calculator = new BonusCalculator();

        System.out.println("홍길동의 보너스: " + calculator.calculateBonus(emp1, "Regular"));
    }
}

문제점 (OCP 위반)

  • 새로운 직원 유형 추가 시 calculateBonus()를 직접 수정해야 함 → 예: Intern(인턴)의 보너스를 추가하려면 BonusCalculator를 수정해야 함.
  • 변경에 닫혀 있지 않음 → 기존 코드(calculateBonus())를 수정하면 새로운 버그가 발생할 가능성이 있음.
  • 유지보수가 어려움 → if-else 문이 많아질수록 가독성이 떨어지고, 확장성이 낮아짐.

OCP를 준수한 코드 (올바른 예시)

🛠 개선된 설계: 새로운 직원 유형을 추가할 때 기존 코드 수정 없이 확장 가능

// 급여 보너스를 계산하는 인터페이스
interface BonusPolicy {
    double calculateBonus(Employee employee);
}

// 직원의 기본 클래스
class Employee {
    protected String name;
    protected double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }
}

// 정규직 보너스 정책 (10%)
class RegularBonusPolicy implements BonusPolicy {
    @Override
    public double calculateBonus(Employee employee) {
        return employee.getSalary() * 0.1;
    }
}

// 매니저 보너스 정책 (20%)
class ManagerBonusPolicy implements BonusPolicy {
    @Override
    public double calculateBonus(Employee employee) {
        return employee.getSalary() * 0.2;
    }
}

// 새로운 직원 유형 추가 (인턴 보너스 정책: 5%)
class InternBonusPolicy implements BonusPolicy {
    @Override
    public double calculateBonus(Employee employee) {
        return employee.getSalary() * 0.05;
    }
}

// BonusCalculator 클래스가 특정 직원 유형을 알 필요가 없음 (OCP 준수)
class BonusCalculator {
    public double calculateBonus(Employee employee, BonusPolicy policy) {
        return policy.calculateBonus(employee);
    }
}

// 실행 코드
public class Main {
    public static void main(String[] args) {
        Employee emp1 = new Employee("홍길동", 5000);
        Employee emp2 = new Employee("이순신", 7000);
        Employee emp3 = new Employee("유관순", 4000);

        BonusCalculator calculator = new BonusCalculator();

        System.out.println("홍길동의 보너스: " + calculator.calculateBonus(emp1, new RegularBonusPolicy()));
        System.out.println("이순신의 보너스: " + calculator.calculateBonus(emp2, new ManagerBonusPolicy()));
        System.out.println("유관순의 보너스: " + calculator.calculateBonus(emp3, new InternBonusPolicy())); // 새로운 유형 추가!
    }
}

OCP 준수로 개선된 점

  • 새로운 직원 유형(예: 인턴) 추가 시 기존 코드 수정 없이 BonusPolicy 인터페이스를 구현하면 됨
    • InternBonusPolicy 클래스를 추가하면 BonusCalculator 코드를 수정하지 않아도 됨.
    • 기존 BonusCalculator는 BonusPolicy 인터페이스에만 의존하여 확장 가능.
  • 변경에는 닫혀 있고, 확장에는 열려 있음
    • calculateBonus() 내부 로직을 수정할 필요 없이 새로운 보너스 정책을 추가할 수 있음.
    • if-else 조건문을 제거하여 가독성이 향상됨.
  • 유지보수 및 확장성이 뛰어남
    • 새로운 정책을 추가할 때 BonusPolicy 인터페이스만 구현하면 되므로 코드 변경이 최소화됨.
    • 다양한 보너스 정책을 독립적으로 관리할 수 있음.

🔥 결론

  • OCP 위반 코드: calculateBonus() 메서드가 새로운 직원 유형을 추가할 때마다 변경되어야 함. → 변경에 닫혀 있지 않음!
  • OCP 준수 코드: BonusPolicy 인터페이스를 통해 보너스 정책을 분리하여, → 새로운 정책을 추가해도 기존 코드를 수정할 필요 없음!

✅ OCP를 준수하면 코드의 확장성이 좋아지고, 변경이 필요할 때 최소한의 수정으로 대응할 수 있는 구조가 된다. 🚀

 

3. 리스코프 치환 원칙 (Liskov Substitution Principle - LSP)

리스코프 치환 원칙(LSP, Liskov Substitution Principle)"자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다."는 원칙이다.

즉, 상속받은 하위 클래스는 상위 클래스의 동작을 변경시키지 않고, 일관성 있게 동작해야 한다.

 

LSP를 위반한 코드 (잘못된 예시)

🚨 잘못된 설계: TemporaryEmployee가 calculateSalary()를 무의미하게 오버라이드

// 상위 클래스: 모든 직원이 공통적으로 가져야 할 속성과 기능 정의
class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }

    // 정규직 직원은 월급 계산 가능
    public double calculateSalary() {
        return 3000; // 기본 월급
    }
}

// 하위 클래스: 계약직 직원 (급여가 없는 경우)
class TemporaryEmployee extends Employee {
    public TemporaryEmployee(String name) {
        super(name);
    }

    // 계약직 직원은 급여를 계산하지 않음
    @Override
    public double calculateSalary() {
        throw new UnsupportedOperationException("계약직 직원은 급여를 계산할 수 없습니다!"); // LSP 위반
    }
}

// 메인 실행 코드
public class Main {
    public static void main(String[] args) {
        Employee employee = new Employee("홍길동");
        System.out.println(employee.name + "의 급여: " + employee.calculateSalary());

        Employee tempEmployee = new TemporaryEmployee("이순신"); // 부모 타입으로 선언
        System.out.println(tempEmployee.name + "의 급여: " + tempEmployee.calculateSalary()); // 예외 발생!
    }
}

문제점 (LSP 위반)

  • TemporaryEmployee가 Employee를 상속받았지만, calculateSalary()가 정상적으로 동작하지 않음. → Employee를 대체하려고 하니 예외(UnsupportedOperationException)가 발생!
  • 상위 클래스(Employee)를 사용하는 코드가 하위 클래스(TemporaryEmployee)에 의해 망가짐 → 다형성(polymorphism)이 깨지고, 부모 클래스를 대체할 수 없음.

LSP를 준수한 코드 (올바른 예시)

🛠 개선된 설계: SalaryEmployee와 NonSalaryEmployee로 분리

// 상위 클래스: 직원의 기본 개념 정의 (급여 계산 기능을 제거)
abstract class Employee {
    protected String name;

    public Employee(String name) {
        this.name = name;
    }
}

// 급여를 받는 직원 (정규직, 계약직 등)
class SalaryEmployee extends Employee {
    private double salary;

    public SalaryEmployee(String name, double salary) {
        super(name);
        this.salary = salary;
    }

    public double calculateSalary() {
        return salary;
    }
}

// 급여가 없는 직원 (인턴, 자원봉사자 등)
class NonSalaryEmployee extends Employee {
    public NonSalaryEmployee(String name) {
        super(name);
    }
}

// 메인 실행 코드
public class Main {
    public static void main(String[] args) {
        SalaryEmployee regularEmployee = new SalaryEmployee("홍길동", 3000);
        System.out.println(regularEmployee.name + "의 급여: " + regularEmployee.calculateSalary());

        NonSalaryEmployee intern = new NonSalaryEmployee("이순신"); // 급여 계산 X
        System.out.println(intern.name + "은(는) 급여가 없습니다.");
    }
}

LSP 준수로 개선된 점

  • Employee의 개념을 추상화하여, 급여를 받는 직원(SalaryEmployee)과 급여가 없는 직원(NonSalaryEmployee)을 분리함.
  • 하위 클래스가 부모 클래스를 정상적으로 대체 가능
    • SalaryEmployee는 calculateSalary()를 제공하고,
    • NonSalaryEmployee는 급여 계산 기능이 없는 대신, 예외를 발생시키지 않음.
  • 다형성을 유지하면서 안전하게 설계됨
    • SalaryEmployee와 NonSalaryEmployee는 각각의 역할에 맞게 동작하여 calculateSalary()가 필요하지 않은 클래스에서 예외가 발생하지 않음.
    • 유지보수성이 향상됨.

🔥 결론

  • LSP 위반 코드: TemporaryEmployee가 Employee를 상속받았지만, calculateSalary() 메서드를 사용할 수 없음 → 부모 클래스를 대체할 수 없음!
  • LSP 준수 코드: 급여를 받는 직원(SalaryEmployee)과 급여가 없는 직원(NonSalaryEmployee)을 분리하여 각각의 역할에 맞는 동작을 유지부모 클래스를 안전하게 대체 가능!

LSP를 준수하면 안전한 상속 설계가 가능해지고, 다형성이 유지되며, 예기치 않은 예외가 발생하는 문제를 방지할 수 있다. 🚀

 

4. 인터페이스 분리 원칙 (Interface Segregation Principle - ISP)

인터페이스 분리 원칙(ISP)"클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다"는 원칙이다. 즉, 하나의 커다란 인터페이스보다는 작은 인터페이스로 분리하여 필요한 기능만 구현하도록 설계해야 한다. 이를 통해 코드의 유연성과 유지보수성이 향상된다.

 

ISP를 준수하지 않은 코드 (잘못된 예시)

// 하나의 인터페이스가 너무 많은 역할을 가지고 있음 (SRP, ISP 위반)
interface Employee {
    void work();
    void takeBreak();
    void requestLeave();
    void calculateSalary();
    void approveLeave(); // 모든 직원이 이 기능을 가져야 하는가?
}

// 일반 직원 클래스
class RegularEmployee implements Employee {
    @Override
    public void work() {
        System.out.println("일반 직원이 업무를 수행합니다.");
    }

    @Override
    public void takeBreak() {
        System.out.println("일반 직원이 휴식을 취합니다.");
    }

    @Override
    public void requestLeave() {
        System.out.println("일반 직원이 휴가를 요청합니다.");
    }

    @Override
    public void calculateSalary() {
        System.out.println("급여를 계산합니다.");
    }

    @Override
    public void approveLeave() {
        // 일반 직원은 휴가를 승인할 수 없음! (ISP 위반)
        throw new UnsupportedOperationException("일반 직원은 휴가를 승인할 수 없습니다!");
    }
}

// 매니저 클래스
class Manager implements Employee {
    @Override
    public void work() {
        System.out.println("매니저가 업무를 수행합니다.");
    }

    @Override
    public void takeBreak() {
        System.out.println("매니저가 휴식을 취합니다.");
    }

    @Override
    public void requestLeave() {
        System.out.println("매니저가 휴가를 요청합니다.");
    }

    @Override
    public void calculateSalary() {
        System.out.println("급여를 계산합니다.");
    }

    @Override
    public void approveLeave() {
        System.out.println("매니저가 휴가를 승인합니다.");
    }
}

문제점 (ISP 위반)

  • Employee 인터페이스가 너무 많은 기능을 포함하고 있음.
  • 일반 직원(RegularEmployee)은 approveLeave() 기능을 사용할 수 없으므로, 불필요한 메서드를 구현해야 함. 이는 "클라이언트가 사용하지 않는 메서드에 의존하면 안 된다"는 ISP 원칙을 위반함.
  • 새로운 역할(예: 인턴)이 추가될 경우 Employee 인터페이스의 불필요한 메서드를 계속 구현해야 하는 문제 발생.

ISP를 준수한 코드 (올바른 예시)

// 업무 관련 인터페이스 (모든 직원이 수행)
interface Workable {
    void work();
    void takeBreak();
}

// 급여 관련 인터페이스 (정규직 직원만 해당)
interface Payable {
    void calculateSalary();
}

// 휴가 요청 인터페이스 (모든 직원이 해당)
interface LeaveRequestable {
    void requestLeave();
}

// 휴가 승인 인터페이스 (매니저만 해당)
interface LeaveApprovable {
    void approveLeave();
}

// 일반 직원 (정규직)
class RegularEmployee implements Workable, Payable, LeaveRequestable {
    @Override
    public void work() {
        System.out.println("일반 직원이 업무를 수행합니다.");
    }

    @Override
    public void takeBreak() {
        System.out.println("일반 직원이 휴식을 취합니다.");
    }

    @Override
    public void requestLeave() {
        System.out.println("일반 직원이 휴가를 요청합니다.");
    }

    @Override
    public void calculateSalary() {
        System.out.println("급여를 계산합니다.");
    }
}

// 매니저 (정규직)
class Manager implements Workable, Payable, LeaveRequestable, LeaveApprovable {
    @Override
    public void work() {
        System.out.println("매니저가 업무를 수행합니다.");
    }

    @Override
    public void takeBreak() {
        System.out.println("매니저가 휴식을 취합니다.");
    }

    @Override
    public void requestLeave() {
        System.out.println("매니저가 휴가를 요청합니다.");
    }

    @Override
    public void calculateSalary() {
        System.out.println("급여를 계산합니다.");
    }

    @Override
    public void approveLeave() {
        System.out.println("매니저가 휴가를 승인합니다.");
    }
}

// 인턴 (급여 계산 제외)
class Intern implements Workable, LeaveRequestable {
    @Override
    public void work() {
        System.out.println("인턴이 업무를 수행합니다.");
    }

    @Override
    public void takeBreak() {
        System.out.println("인턴이 휴식을 취합니다.");
    }

    @Override
    public void requestLeave() {
        System.out.println("인턴이 휴가를 요청합니다.");
    }
}

ISP 준수로 개선된 점

  • 각 역할별로 인터페이스를 분리하여, 필요한 기능만 구현하도록 만듦.
  • RegularEmployee는 approveLeave()를 구현할 필요가 없음.
  • 새로운 역할(예: Intern)이 추가될 때, 필요하지 않은 메서드를 구현할 필요가 없음.
  • 클래스 간 의존성이 줄어들고, 유지보수성이 향상됨.

🔥 결론

  • ISP 위반 코드: 하나의 인터페이스가 너무 많은 기능을 포함하여, 모든 클래스가 불필요한 메서드를 구현해야 하는 문제가 발생.
  • ISP 준수 코드: 인터페이스를 역할별로 나누어, 각 클래스가 필요한 기능만 구현하도록 설계 → 유지보수성이 향상되고, 새로운 요구사항 변경이 용이해짐.

이처럼 ISP를 준수하면 "필요한 기능만 구현하는 유연한 설계"가 가능해지며, 불필요한 코드 구현을 줄이고, 확장성이 높아지는 장점이 있다. 🚀

 

5. 의존 역전 원칙 (Dependency Inversion Principle - DIP)

의존성 역전 원칙(DIP)"상위 수준 모듈(High-level Module)이 하위 수준 모듈(Low-level Module)에 직접 의존해서는 안 된다. 둘 다 추상화(인터페이스)에 의존해야 한다."는 원칙이다.

즉, 구체적인 클래스가 아니라 인터페이스나 추상 클래스에 의존하여 유연성을 높여야 한다.

 

DIP를 위반한 코드 (잘못된 예시)

// EmployeeManager 클래스가 직접 MySQLDatabase에 의존 (강한 결합, DIP 위반)
class MySQLDatabase {
    public void save(String employeeName) {
        System.out.println(employeeName + " 정보를 MySQL 데이터베이스에 저장했습니다.");
    }
}

class EmployeeManager {
    private MySQLDatabase database;

    public EmployeeManager() {
        this.database = new MySQLDatabase(); // 직접 객체 생성 (하드코딩)
    }

    public void addEmployee(String employeeName) {
        database.save(employeeName);
    }
}

public class Main {
    public static void main(String[] args) {
        EmployeeManager manager = new EmployeeManager();
        manager.addEmployee("홍길동");
    }
}

문제점 (DIP 위반)

  • 강한 결합 (Tight Coupling)
    • EmployeeManager 클래스는 MySQLDatabase에 직접 의존하고 있음.
    • 데이터베이스가 바뀌면 EmployeeManager도 수정해야 함 (예: PostgreSQLDatabase로 변경 시 코드 수정 필요).
  • 확장성 부족
    • 데이터베이스 종류를 쉽게 변경할 수 없음.
    • 다른 데이터베이스(MySQL → PostgreSQL 등)로 변경할 경우 새로운 클래스를 만들어야 하고, 기존 코드도 수정해야 함.

DIP를 준수한 코드 (올바른 예시)

// 1. 추상화 계층(인터페이스) 생성
interface Database {
    void save(String employeeName);
}

// 2. MySQLDatabase 클래스 (인터페이스 구현)
class MySQLDatabase implements Database {
    @Override
    public void save(String employeeName) {
        System.out.println(employeeName + " 정보를 MySQL 데이터베이스에 저장했습니다.");
    }
}

// 3. PostgreSQLDatabase 클래스 (새로운 DB 추가 가능)
class PostgreSQLDatabase implements Database {
    @Override
    public void save(String employeeName) {
        System.out.println(employeeName + " 정보를 PostgreSQL 데이터베이스에 저장했습니다.");
    }
}

// 4. EmployeeManager 클래스가 구체적인 DB 클래스가 아니라 인터페이스(Database)에 의존 (DIP 준수)
class EmployeeManager {
    private Database database;

    // 생성자를 통해 의존성 주입 (Dependency Injection)
    public EmployeeManager(Database database) {
        this.database = database;
    }

    public void addEmployee(String employeeName) {
        database.save(employeeName);
    }
}

// 5. Main 클래스에서 원하는 DB 구현체를 주입하여 사용
public class Main {
    public static void main(String[] args) {
        Database mySQLDatabase = new MySQLDatabase();
        EmployeeManager manager1 = new EmployeeManager(mySQLDatabase);
        manager1.addEmployee("홍길동");

        Database postgreSQLDatabase = new PostgreSQLDatabase();
        EmployeeManager manager2 = new EmployeeManager(postgreSQLDatabase);
        manager2.addEmployee("이순신");
    }
}

 

DIP 준수로 개선된 점

  • 느슨한 결합 (Loose Coupling)
    • EmployeeManager는 Database 인터페이스에 의존하므로, 특정 데이터베이스(MySQL, PostgreSQL 등)에 직접 의존하지 않음.
    • EmployeeManager 코드를 수정하지 않고도 다양한 데이터베이스를 사용할 수 있음.
  • 확장성 증가
    • 새로운 데이터베이스(OracleDatabase, MongoDBDatabase 등)를 추가할 때 기존 코드 수정 없이 Database 인터페이스를 구현하기만 하면 됨.
  • 의존성 주입 (Dependency Injection) 적용
    • 생성자를 통해 의존성을 주입하여 변경이 유연해짐.
    • 객체 간의 강한 결합이 사라지고, 유지보수가 용이해짐.

🔥 결론

  • DIP 위반 코드: 상위 수준 모듈(EmployeeManager)이 하위 수준 모듈(MySQLDatabase)에 직접 의존 → 강한 결합 발생, 확장 어려움
  • DIP 준수 코드: 상위 수준 모듈(EmployeeManager)이 추상화(Database)에 의존 → 유연성과 확장성이 증가하고, 유지보수가 쉬워짐!

DIP를 적용하면 코드의 재사용성이 높아지고, 변경이 필요할 때 최소한의 수정으로 대응할 수 있는 구조가 된다. 🚀

반응형

'자바(Java)' 카테고리의 다른 글

Java - equals, hashCode  (2) 2024.11.08
Java - 엑셀 업로드 후 DB 저장  (0) 2023.08.25
Java - Stream  (0) 2023.08.17
Java - 메소드 참조와 Optional 클래스  (0) 2023.08.11
Java - 람다식  (0) 2023.08.11