개발/자바

OCP(Open-Closed Principle), SRP(Single Responsibility Principle)

피터JK 2025. 2. 19. 17:24
728x90

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

**OCP(Open-Closed Principle)**는 **"소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다"**는 원칙이다.

  • 확장에는 열려 있어야 한다(Open for extension): 새로운 기능을 추가할 때 기존 코드를 변경하지 않고 확장할 수 있어야 한다.
  • 수정에는 닫혀 있어야 한다(Closed for modification): 기존 코드가 변경되지 않도록 설계하여 안정성을 유지해야 한다.

📌 OCP를 지키는 방법

  1. 추상화(Abstraction) 활용: 인터페이스나 추상 클래스를 사용하여 새로운 기능을 추가할 때 기존 코드의 변경을 최소화한다.
  2. 전략 패턴(Strategy Pattern): 실행할 알고리즘을 인터페이스로 분리하고, 필요에 따라 새로운 구현체를 추가하는 방식.
  3. 템플릿 메서드 패턴(Template Method Pattern): 상위 클래스에 공통 로직을 정의하고, 하위 클래스에서 특정 동작을 오버라이드하여 확장.

✅ OCP 적용 예제 (Java)

// OCP를 적용한 예제: 확장을 쉽게 하면서 기존 코드 변경을 최소화
interface DiscountPolicy {
    double applyDiscount(double price);
}

// 기존 할인 정책
class PercentageDiscount implements DiscountPolicy {
    private final double percentage;

    public PercentageDiscount(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double applyDiscount(double price) {
        return price * (1 - percentage / 100);
    }
}

// 새로운 할인 정책 추가 (OCP 적용: 기존 코드를 변경하지 않음)
class FixedAmountDiscount implements DiscountPolicy {
    private final double discountAmount;

    public FixedAmountDiscount(double discountAmount) {
        this.discountAmount = discountAmount;
    }

    @Override
    public double applyDiscount(double price) {
        return price - discountAmount;
    }
}

// 할인 적용 클래스
class Order {
    private final DiscountPolicy discountPolicy;

    public Order(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }

    public double calculateFinalPrice(double price) {
        return discountPolicy.applyDiscount(price);
    }
}

// 사용 예
public class Main {
    public static void main(String[] args) {
        Order order1 = new Order(new PercentageDiscount(10)); // 10% 할인 적용
        Order order2 = new Order(new FixedAmountDiscount(5000)); // 5000원 할인 적용

        System.out.println(order1.calculateFinalPrice(10000)); // 9000.0
        System.out.println(order2.calculateFinalPrice(10000)); // 5000.0
    }
}

➡ 기존 코드(Order 클래스)는 변경되지 않고, 새로운 할인 정책을 추가하여 확장 가능(OCP 만족).


SRP(Single Responsibility Principle, 단일 책임 원칙)

**SRP(Single Responsibility Principle)**는 **"클래스는 하나의 책임만 가져야 한다"**는 원칙이다.

  • 하나의 클래스가 여러 가지 역할을 담당하면 변경 사항이 많아지고 유지보수가 어려워진다.
  • 하나의 책임만 가지도록 클래스를 분리하면 변경이 필요할 때 해당 기능만 수정하면 되므로 유지보수가 용이하다.

📌 SRP를 지키는 방법

  1. 하나의 클래스는 하나의 책임만 가지도록 분리한다.
  2. 단일 변경 이유(Single Reason to Change)를 가진다: 클래스가 변경되는 이유가 여러 개라면, 그만큼 역할이 많다는 뜻이므로 분리해야 한다.
  3. 높은 응집도(High Cohesion), 낮은 결합도(Low Coupling)를 유지한다.

❌ SRP를 위반한 코드 (나쁜 예)

class Employee {
    private String name;
    private double salary;

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

    // 급여 계산 (비즈니스 로직)
    public double calculatePay() {
        return salary * 0.9; // 세금 공제 후 급여
    }

    // 급여 명세서 출력 (출력 관련 기능)
    public void printPaySlip() {
        System.out.println("Employee: " + name + ", Salary: " + calculatePay());
    }

    // 데이터 저장 (DB 관련 기능)
    public void saveToDatabase() {
        System.out.println("Saving employee data to database...");
    }
}

➡ Employee 클래스는 급여 계산, 출력, DB 저장이라는 3가지 책임을 가지고 있어 SRP를 위반함.


✅ SRP 적용 예제 (좋은 예)

// 급여 계산 클래스 (비즈니스 로직)
class Payroll {
    public double calculatePay(Employee employee) {
        return employee.getSalary() * 0.9;
    }
}

// 급여 명세서 출력 클래스 (출력 관련 책임)
class PaySlipPrinter {
    public void printPaySlip(Employee employee, double salary) {
        System.out.println("Employee: " + employee.getName() + ", Salary: " + salary);
    }
}

// 데이터 저장 클래스 (DB 관련 책임)
class EmployeeRepository {
    public void save(Employee employee) {
        System.out.println("Saving employee data to database...");
    }
}

// 사용 예
public class Main {
    public static void main(String[] args) {
        Employee emp = new Employee("John Doe", 5000);
        Payroll payroll = new Payroll();
        PaySlipPrinter printer = new PaySlipPrinter();
        EmployeeRepository repository = new EmployeeRepository();

        double salary = payroll.calculatePay(emp);
        printer.printPaySlip(emp, salary);
        repository.save(emp);
    }
}

➡ Employee 클래스의 책임을 3개의 클래스로 분리하여 SRP를 만족.


✅ OCP & SRP의 차이점 정리

원칙 개념 목표 적용 방법
OCP (개방-폐쇄 원칙) 기존 코드를 변경하지 않고 확장 가능해야 함 유연성과 확장성을 높이고 유지보수성을 강화 인터페이스, 상속, 전략 패턴, 템플릿 메서드 패턴 활용
SRP (단일 책임 원칙) 클래스는 하나의 책임만 가져야 함 코드 응집도를 높이고, 변경에 유연하게 대응 역할을 분리하고 책임별 클래스를 나누기

둘 다 유지보수성과 확장성을 높이는 객체지향 설계 원칙으로, **OCP는 "변경 없이 확장", SRP는 "책임 분리"**에 초점을 맞춘다.

728x90