본문 바로가기
Study/Software Engineering

객체지향 소프트웨어 설계의 원칙들

by SeulKom 2009. 8. 25.


Contents
1. 배경
2. 개방-폐쇄 원칙(Open-closed principle, OCP)
3. 단일 책임 원칙(Single Responsibility principle, SRP)
4. 인터페이스 분리 원칙(Interface Segregation Principle, ISP)
5. 리스코프 원칙(Liskov Substitution Principle, LSP)
6. 의존 관계 역전의 원칙(Dependency Inversion Principle, DIP)





1. 배경

소프트웨어 설계에 있어 객체와 객체간의 유기적 관계를 효과적으로 구성하는 것은 유지보수 및 관리에 있어 상당히 중요하다. 하지만, 요구사항 변경과 그로 인한  의존성 관리가 불가피해 짐에 따라 설계가 다음의 네 가지 증상을 보이며 무너지는 것을 볼 수 가 있다.

   - Rigidity (연쇄적 변경으로 인해 변경자체를 허용 하지 않음)
   - Fragility (변경으로 인해 다발적으로 설계가 붕괴)
   - Immobility (재사용을 하려고 하였을 때, 필요 없는 부분이 너무 커 코드를 버리고 재작성)
   - Viscosity (여러 변경 방법 중 현재 설계방법을 유지시켜주는 변경 방법을 찾기가 쉽지 않음)

이런 증상을 원초적으로 해결하기 위해 설계에 대한 원칙과 패턴을 만들 필요가 있게 되었고, 다음 Section 부터 그에 대해 자세하게 설명 할 것이다.






2. 개방-폐쇄 원칙(Open-closed principle, OCP)

소프트웨어 구성 요소(컴포넌트, 클래스, 모듈, 함수)는

확장(Extension) 에 대해서는 개방되어야 하지만,
변경(Change) 에 대해서는 폐쇄되어야 한다.



Dynamic polymorphism
① 변하는(확장되는) 것과 변하지 않는(폐쇄 되어야 하는 것)을 엄격히 구분한다.
② 이 두 모듈이 만나는 지점에 인터페이스를 정의한다.
    인터페이스는 서비스 내용을 추상화 하는 형태로 제공하므로 적당한 추상화 level 선택이 중요하다.

Static polymorphism
- Templates 나 generics 이용










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

하나의 클래스는 하나의 책임만을 가져야 한다.

'변경'의 거북함을 조장하는 요소는 서로 다른 '책임' (= Concern) 이 혼재해 있다는 데 있다.
책임이란 '변경을 위한 이유' 이다. 만약 하나의 클래스에 변경을 위한 두 가지 이상의 이유가 있다면 그 클래스는 한 가지 이상의 책임을 갖고 있는 것이다.

이 원칙을 위반할 경우 Bad smell 이 생길 수 있음 즉,
첫째, 소외되는 메소드가 존재한다.
둘째, 무관한 메소드에 변경이 발생할 경우 불필요한 변경 임팩트가 전달된다. (컴파일, 테스트, 재배포)









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


일반적인 단일 인터페이스보다는, 구체적인 다수의 인터페이스가 낫다

 SRP가 클래스 분리를 통해 변화에의 적응성을 획득하는 반면, ISP에서는 인터페이스 분리를 통해 같은 목표에 도달한다. 만약 어떤 클래스를 이용하는 클라이언트가 여러 개 있고, 이들이 해당 클래스의 특정 부분집합만을 이용한다면 이들을 따로 인터페이스로 빼내어 클라이언트가 기대하는 메시지만을 전달할 수 있도록 하는 것이다.

인터페이스 분리 원칙 (Adapter 패턴 - Class Adapter, Object Adapter)
첫째, 미리 구현된 클라이언트의 변경을 주지 말아야 한다.
둘째, 두 개 이상의 인터페이스가 공유하는 부분은 재사용을 극대화 해야 한다.
셋째, 서로 다른 성격의 인터페이스를 명백히 분리해야 한다.









5. 리스코프 대체 원칙(Liskov Substitution Principle, LSP)


기반 클래스는 서브 클래스로 대체 가능해야 한다.

  상속 관계에서 부모와 자식 간에는 IS-A 관계가 성립해야 한다. 이는 자식이 부모의 메소드 중 일부를 거부하면 안 된다는 것을 의미한다. LSP는 올바른 상속 구조가 갖춰야 할 특성을 가이드해주며 OCP의 기반이 된다. 원칙을 지키지 않으면 Refused Bequest 이라는 bad smell 이 생긴다.

- Design by Contract, DBC

   Derived class가 Base class를 대신하려면 Pre-condition, Post-condition 등을 명료하게 정의해야 한다.
 
   ① Base class 보다 조건이 까다로운 pre-condition이 있으면 안된다.
   ② Base class 보다 조건이 부족한 post-condition이 있으면 안된다.

   즉, derived method 는 base method 와 동일해야 한다.









6. 의존 관계 역전의 원칙(Dependency Inversion Principle, DIP)


클라이언트는 구체 클래스가 아닌 인터페이스나 추상 클래스에 의존해야 한다.


  Concrete 클래스는 변화 가능성이 많기 때문에 의존하게 될 경우 유지비용이 많이 든다. 그래서 상대적으로 변화 가능성이 적은 Abstract 클래스에 의존해야 한다. Abstract 클래스에 의존하는데는 IOC(Inversion of Control)가 중추 역할을 한다. 통제권이 역전 되면 응답을 확인하는 작업에서 자유로워지고, 이로 인해 다른 작업을 할 수 있는 기회비용을 확보 할 수 있다. - 클라이언트의 역할을 단순화


 - 통제권의 역전(Inversion of Control, IOC)
 
  주 통제권이 호출자에게서 프레임워크로 역전. 이 때, Inverse는 통제권에 대한 의존성에 대한 것만 의미하는 것이 아니라 인터페이스 소유권에 대한 것도 의미한다.

  - Hook 메소드

  미리 정의해 둔 인터페이스. Inverse를 위한 매개 포인트가 되며 확장성을 확보하는 기능도 한다.
 












참조자료
[1] "Design Principles and Design Patterns", Robert C. Martin, http://www.objectmentor.com
[2] "다시 보면 크게 보이는 개방-폐쇄 원칙", 최상훈, 마이크로소프트웨어 2005년 1월호
[3] "헤어져서 행복해진 사례연구, 단일 책임 원칙", 최상훈, 마이크로소프트웨어 2005년 2월호
[4] "Design Patterns: Elements of Reusable Object-Oriented Software", Erich Gamma, Richard Helm, John Vissides, Addison Wesley. 1994
[5] "Patterns of Enterprise Application Architecture", Martin Fowler
[6] "상대에 대한 조그만 배려, 인터페이스 분리의 원칙", 최상훈, 마이크로소프트웨어 2005년 3월호
[7] "복잡성과 단순성이 상생하는 미학", 최상훈, 마이크로소프트웨어 2005년 4월호