SOLID > OCP (개방-폐쇄 원칙)

본 글은 로버트 C.마틴 저자(송준이 엮음)의 [클린 아키텍처: 소프트웨어 구조와 설계의 원칙] 도서를 참고하였습니다.


1. 의미

  • Open-Closed Principle.
  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
    • 확장에 열려 있다
      • 모듈의 확장성을 보장함
      • 새로운 변경 사항이 발생했을때 유연하게 코드를 추가함으로써 애플리케이션의 기능을 큰 힘을 들이지 않고 확장할 수 있음
    • 변경에 닫혀 있다
      • 객체를 직접적으로 수정하는 것은 제한해야 함
      • 새로운 변경 사항이 발생했을 때 객체를 직접적으로 수정해야 한다면 새로운 변경사항에 대해 유연하게 대응할 수 없는 어플리케이션이라고 함
      • 이는 유지보수 비용의 증가로 이어지게 됨
      • 따라서 객체를 직접 수정하지 않고도 변경사항을 적용할 수 있도록 설계해야 함
  • 따라서 기존 코드를 수정하기 보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 소프트웨어 시스템을 쉽게 변경할 수 있다.

2. 원칙 위반 예제

image

class Animal {
	String type;

    Animal(String type) {
    	this.type = type;
    }
}

// 동물 타입을 받아 각 동물에 맞춰 울음소리를 내게 하는 클래스 모듈
class HelloAnimal {
    void hello(Animal animal) {
        if(animal.type.equals("Cat")) {
            System.out.println("냐옹");
        } else if(animal.type.equals("Dog")) {
            System.out.println("멍멍");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HelloAnimal hello = new HelloAnimal();

        Animal cat = new Animal("Cat");
        Animal dog = new Animal("Dog");

        hello.hello(cat); // 냐옹
        hello.hello(dog); // 멍멍
    }
}
  • 동작 자체는 문제가 없음
  • 하지만 ‘고양이’와 ‘개’ 외에 ‘양’이나 ‘사자’를 추가하게 된다면 HelloAnimal 클래스를 수정해주어야 함
    • 현재 코드에서는 각 객체의 필드 변수에 맞게 if문을 분기하여 구성해주어야 함

3. 어떻게 OCP대로 추상화 설계를 할 수 있을까?

  1. 먼저 변경(확장)될 것과 변하지 않을 것을 엄격히 구분
  2. 이 두 모듈이 만나는 지점에 추상화(추상클래스 or 인터페이스)를 정의함
  3. 구현체에 의존하기보다 정의한 추상화에 의존하도록 코드를 작성하기

4. 수정한 예제

image

// 추상화
abstract class Animal {
    abstract void speak();
}

class Cat extends Animal { // 상속
    void speak() {
        System.out.println("냐옹");
    }
}

class Dog extends Animal { // 상속
    void speak() {
        System.out.println("멍멍");
    }
}

class HelloAnimal {
    void hello(Animal animal) {
        animal.speak();
    }
}

public class Main {
    public static void main(String[] args) {
        HelloAnimal hello = new HelloAnimal();

        Animal cat = new Cat();
        Animal dog = new Dog();

        hello.hello(cat); // 냐옹
        hello.hello(dog); // 멍멍
    }
}
  • 위와 같이 구성하게 되면 기능 추가가 됬을때에도 코드 수정 없이 확장이 가능

Reference