본문 바로가기

STUDY/디자인 패턴

1. SOLID - 객체 지향의 다섯가지 원칙

Design Smells

 

Design Smells은 나쁜 설계의 증상들을 말하며 그 내용은 아래와 같다.

증상 내용
경직성 시스템 변경이 어렵다. 하나를 바꾸려고 할 때마다, 다른 것들도 끝없이 바꾸어야 한다.
취약성 한 부분의 수정이 다른 많은 부분들, 연관이 없는 부분에 까지 영향을 준다. 
부동성 시스템을 재사용하기 위한 요소로 분리하기 어렵다.
점착성 시스템 설계나 개발환경에 맞추기 위해 실제 개발이 어려워진다.
불필요한 복잡성 당장 필요하지 않은 많은 코드다 들어가 있다.
불필요한 반복 같은 코드가 중복되어 쓰이고 있다.
불투명성 쉽게 의도를 알기 어려운 코드가 존재한다.

 

 


SOLID

 

SOLID는 객체 지향의 5가지 원칙으로 그 내용은 아래와 같다.

  • Single-Responsibility Principle (SRP): 단일 책임 원칙, 한 클래스는 하나의 책임을 가져야 한다.
  • Open-Closed Principle (OCP): 개방-폐쇄 원칙, 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
  • Liskov Substitution Principle (LSP): 리스코프 치환 원칙, 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
  • Interface Segregation Principle (ISP): 인터페이스 분리 원칙, 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
  • Dependency Inversion Principle (DIP): 의존관계 역전 원칙, 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.

 


단일 책임 원칙(Single Responsibility Principle) 사례

 

  • 학교 시스템은 학생들은 관리하고, 이에 따라 학생 클래스가 존재한다.
  • 학생은 이름이나 번호로 정렬이 필요하다. 그래서 학생클래스에 Comparable을 상속시켰다.
class Student implements Comparable {
	...
	int compareTo(Object o) { … }
	...
}

 

위 클래스는 시스템에 필요한 학생 클래스를 구현하였고, 또 정렬 요구사항도 만족시켰다. 따라서 문제가 없는 코드로 보일지도 모르나, 단일 책임 원칙을 위반하였다. 학생 클래스는 학생의 속성과 동작 이외에, 정렬에 대한 책임까지 가지게 되었다. 만약 정렬 기준이 변경되면 학생의 속성과 동작에는 변화가 없지만 Student 코드는 수정되어야 한다.

 

 


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

 

  • 인사 시스템은 직원의 급여를 관리한다. 직원은 사무직, 현장직으로 나누어진다.
  • 사무직, 현장직은 각각의 급여 인상 로직이 존재한다.
void incAll(Employee[] emps) {
    for (int i = 0; i < emps.size(); i++) {
        if (emps[i].empType == OFFICE)
        	incOfficeSalary((Faculty)emps[i]);
        else if (emps[i].empType == FIELD)
        	incFieldSalary((Staff)emps[i]);
    }
}

 

위 메소드는 모든 직원들의 급여를 구분에 따라 인상하는 동작을 수행한다. 위 코드는 직원 구분이 추가될 때 마다 변경되어야하는 경직되 코드이다. 확장에는 열려 있어야 한다는 개방-폐쇄 원칙에 위배된다. 아래와 같이 Employee 클래스에 급여 인상 메소드를 넣고, 이를 호출하는 방식으로 변경에 열려있는 구조를 달성할 수 있다.

 

void incAll(Employee[] emps) {
    for (int i = 0; i < emps.size(); i++)
    	emps[i].incSalary();
}

 

 


리스코프 치환 원칙(Interface Segregation Principle)

 

위와 같이 MyList를 구현한 두가지 구조가 있다. 왼쪽의 경우 List를 상속받아서 MyList를 구현하였다. 그러나 부모 클래스의 find() 메소드를 구현하지 않아서, List 타입으로 사용될 시 문제가 발생한다. 이는 리스코프 치환 원칙 위반이다. 오른쪽 처럼 List를 속성으로 포함하는 방식으로 구현하면 SOLID 원칙을 지킬 수 있다.

 

 


의존관계 역전 원칙(Dependency Inversion Principle)

 

Encoder를 사용하는 Service가 두개 있다. 왼쪽의 Service는 UTF8Encoder를 바로 사용하고 있는데, 다른 Encoder를 사용해야하는 경우 유연하게 대응할 수 없다. 이는 추상화 되지않은 객체에 의존하기 때문에 생기는 문제이다. 오른쪽의 Service처럼 추상화된 객체에 의존하게 수정하면, 더 나은 구조를 만들 수 있다.

728x90