개방 폐쇄 원칙

stoph·2022년 9월 4일
0

확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.

위의 정의를 쉽게 말하면 확장은 쉽게, 변경은 어렵게 설계하자는 것이다.
더 이해하기 쉽게 현실 세계에서의 OCP 적용 사례를 찾아보도록 하자.


현실 세계의 OCP

1. PC와 주변 기기

세상에는 수많은 종류의 PC와 주변기기들 (키보드, 마우스 , 스피커 등등)이 있다.
서로 기능도 다르고 생김새도 다르지만 각 PC와 각 주변기기들이 모두 다 연결이 가능하다.
그게 가능한 이유는 연결 단자 규격(인터페이스) 이 같기 때문이다.
여기서 PC와 주변기기는 '확장-쉽게' 에 해당되고, 연결 단자는 '변경-어렵게' 에 해당한다.
PC와 주변기기의 기능들은 얼마든지 확장하고 추가해도 되지만 연결 단자 규격은 절대 변경하면 안 된다.

2. 휴대폰과 충전기

세상에는 수많은 종류의 휴대폰과 충전기가 있다.
서로 기능도 다르고 생김새도 다르지만 각 휴대폰과 각 충전기가 모두 다 연결이 가능하다.
그게 가능한 이유 또한, 연결 단자 규격(인터페이스) 이 같기 때문이다.
여기서 휴대폰과 충전기는 '확장-쉽게' 에 해당되고, 연결 단자는 '변경-어렵게' 에 해당한다.
휴대폰과 충전기의 기능은 얼마든지 확장하고 추가해도 되지만 연결 단자 규격은 절대 변경하면 안 된다.


코드로 보는 OCP

간단한 소스 코드와 함께 알아보자.

public class Information {

    public String getInformation(Long itemId) {
        if (itemId == 1L) {
            return "앨범명: AAA, 가수: Tom, 가격: 1000";
        } else if (itemId == 2L) {
            return "도서명: BBB, 저자: Bill, 가격: 2000";
        } else if (itemId == 3L) {
            return "영화제목: CCC, 감독: Steve, 가격: 3000";
        }
		
        // ...
    }
}

getInformation() 는 상품에 대한 정보를 반환하는 메서드이다.
위의 소스 코드는 여러 가지 문제점을 가지고 있다.

  • 상품이 추가 될 때마다 if-else 문을 계속해서 추가해야 하므로 점점 코드가 복잡해진다.
  • 코드가 길어지면 추후에 수정이 필요할 때, 수정 해야 할 위치를 찾기 어려워진다.
  • 추가해야 되는 데이터를 실수로 누락할 가능성이 있다.

결론적으로, 위의 소스코드는 OCP를 위반한 코드이다.


OCP 원칙을 적용하여 소스 코드를 리팩토링 해보자.

앨범, 도서, 영화 이 세 가지 품목은 모두 상품이라는 카테고리에 속한다.
그 외의 추가될 다른 품목들 또한 상품이라는 카테고리에 속하게 되므로
상품이라는 큰 틀을 먼저 만들자.

public interface Product {

    String getInformation();
}

상품 정보를 반환하는 메서드를 포함하고 있는 상품 인터페이스를 만들었다.

그리고, 앨범, 도서, 영화 등을 Product 인터페이스를 상속받아 구현한다.

public class Album implements Product{

   private String title;
   private String singer;
   private Integer price;

   public Album(String title, String singer, Integer price) {
      this.title = title;
      this.singer = singer;
      this.price = price;
   }

   @Override
   public String getInformation() {
      String result = String.format("앨범이름: %s, 가수: %s, 가격: %d", this.title, this.singer, this.price);
      return result;
   }
}
public class Book implements Product{

    private String title;
    private String author;
    private Integer price;

    public Book(String title, String author, Integer price) {
        this.title = title;
        this.author = author;
        this.price = price;
    }

    @Override
    public String getInformation() {
        String result = String.format("도서명: %s, 저자: %s, 가격: %d", this.title, this.author, this.price);
        return result;
    }
}
public class DVD implements Product{

    private String title;
    private String director;
    private Integer price;

    public DVD(String title, String director, Integer price) {
        this.title = title;
        this.director = director;
        this.price = price;
    }

    @Override
    public String getInformation() {
        String result = String.format("상품명: %s, 가수: %s, 가격: %d", this.title, this.director, this.price);
        return result;
    }
}

그리고, 아까의 if-else 문을 반복 해야하는 코드를 수정해보자.

public class Information {

    public String getInformation(Product product) {
        return product.getInformation();
    }
}

Product 를 구현한 상품을 인자로 전달 받아 getInformation() 메서드를 실행하여 상품 정보를 반환하도록 소스 코드를 수정하였다.

이제 더 이상 if-else 문을 반복하지 않을 뿐더러, 새로운 상품이 추가 되더라도 기존의 소스 코드를 변경하지 않아도 된다.

Product 인터페이스를 구현하여 새로운 상품을 만들기만 하면 되는 것이다.

위의 예시에서는 인터페이스를 사용하여 OCP를 적용했지만 이 방법 말고도 OCP를 적용할 수 있는 여러가지 방법들이 있다.

정리

OCP 원칙을 지키면서 코드를 작성해야 하는 이유를 요약 해보자.

  • 기존의 소스 코드는 변경하지 않으면서 추가와 확장이 가능하다.
  • 추후에 소스 코드를 유지 보수하기 쉽다.

무언가 같은 코드가 3번 이상 반복된다면, 느낌상 '이건 좀 비효율적인거같은데...' 라는 생각이 든다면 OCP 원칙을 떠올리면서 소스 코드를 유연하게 바꾸는 습관을 들이도록 해보자.

참고

네이버 지식백과 - 쉽게 배우는 소프트웨어 공학
우아한 Tech - 10분 테코톡

0개의 댓글