디자인 원리 정리

디자인 원리는 코드를 좀 더 유지보수하기 쉽고, 유연하고 확장히기 쉽게 만들기 위해, 코드의 작성이나 디자인에 적용되는 기본 도구 또는 기법입니다.

지금까지 배운 디자인 원리(객체지향 원리)

  • 변하는 것을 캡슐화하라.
  • 구현에 의존하기보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것이다.

 

원리1. 개방 - 폐쇄의 원리(Open - Closed Principle, OCP)

클래스의 확장에는 열려있고, 수정에는 닫혀 있어야 한다. OCP는 기존 코드를 변경하지 않으면서 코드의 수정을 혀용하는 것에 관한 원리 입니다.

새라는 클래스에 fly()라는 함수를 정의 한다. 참새, 독수리, 매 등의 서브 클래스에서는 fly() 라는 함수를 Overriding 해서 각각의 나는(fly) 방식을 따로 구현한다.

여기서 fly()메소드는 부모 클래스에 정의되어 변경되지 않습니다. 하지만 모든 서브 클래스들이 fly() 메소드의 행동을 변경할 수 있기 때문에 새 클래스는 확장에는 열려 있습니다.

상속만이 유일한 방법은 아닙니다. 구성(Composition)을 사용 할때도 좋은 방법이 있습니다. 클래스에 private 메소드가 여러개 있고, 그 private 메소드를 여러 방법으로 호출하는 public 메소드를 추가 할 수도 있습니다.

원리2. 반복 금지의 원리(Don't Repeat Yourself, DRY)

공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라. DRY는 하나의 요구 사항은 한 곳에 두어야 한다는 원리 입니다. 시스템의 각 정보와 기능을 말이 되는 하나의 장소에 두는 것을 의미합니다.

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

시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다. 각 객체가 변화의 요인이 하나뿐이라면, 여러분은 단일 책임의 원리를 정확히 구현한 것입니다.

응집도는 사실 SRP의 다른 이름이라 할 수 있습니다. 여러분이 응집도가 높은 프로그램을 작성한다면, 이는 곧 SRP를 잘 적용하고 있는 것을 의미합니다.

여러개의 책임 찾아내기
대부분의 경우 간단한 테스트를 통해 SRP를 사용하지 않은 클래스들을 찾을 수 있습니다.

  1. 아래의 ___ 에 class 이름을 기입합니다.
    _____________ 클래스에 대한 SRP 분석
  2. 아래의 ____1___에 클래스를 기입하고 ___2___ 에 해당 메소드를 기입하세요, 메소드의 인자값도 같이 넣어서 읽어보세요.
    ____1___ (이)가 자신을 ___2___ 한다.
  3. 모든 메소드를 확인해보고 말이 되지 않으면, 그 메소드에서 SRP 원리를 위반하고 있을 것입니다.

 

원리4. 리스코프 치환원리(Liskov Substitution Principle, LSP)

자식 타입들은 부모 타입들이 사용되는 곳에 대체될 수 있어야 한다. 부모 클래스를 상속할 때, 부모 클래스가 사용되는 곳은 아무 문제없이 자식 클래스도 사용할 수 있어야 합니다. 그렇지 않으면, 상속을 잘못 사용하고 있는 것입니다.

상속을 오용하는 코드들은 이해하기 어렵습니다. 그러면 상속 외에 어떠한 방법들이 있을까요?

위임(Delegation)
특정 일의 책임을 다른 클래스나 메소드에 맡길 때를 지칭 합니다. 클래스의 행동을 변경하고 싶지 않고 그 행동을 스스로 구현하는 것이 그 클래스의 책임이 아닌 경우에는 그 행동을 다른 클래스에 위임하세요.

언제 위임을 하나?
다른 클래스의 기능을 그 클래스 행동의 변경없이 그대로 사용하고 싶을 때, 가장 잘 사용되는 것입니다.
다른 클래스의 기능을 사용해야 하지만 그 기능을 변경하고 싶지 않다면, 상속 대신 위임의 사용을 고려하세요.

구성(Composition)을 사용하여 다른 클래스들의 행동을 조합하기.
구성을 통해 하나의 인터페이스를 구현한 여러 클래스들의 기능을 사용할 수 있고 실행 중에 그 클래스를 바꾸어 기능을 변경할 수 있습니다. 구성을 사용하여 하나 또는 여러개의 클래스, 특히 비슷한 종류의 여러 클래스들로부터 행동을 재사용할 수 있습니다. 여러분의 객체가 다른 객체를 완전히 소유하고 있는 형태이며, 구성관계로 연결된 객체는 여러분 객체의 외부에 독립적으로 존재할 수 없습니다.

언제 구성을 사용할까요?
구성은 우리가 인터페이스에 정의된 기능을 사용하고, 그래서 컴파일 중이나 실행 중에 그 인터페이스의 여러 구현 클래스 중에 선택하기를 원할 때 위력을 발휘합니다.

집합(Aggregation)
한 클래스가 다른 클래스의 부분으로 사용되지만 다른 클래스의 외부에서도 여전히 존재하는 경우를 지칭합니다.

언제 구성을 사용하고, 언제 집합을 사용하나요?
내가 사용하고 싶은 행동을 가지고 있는 객체가 그 행동을 사용하는 객체의 외부에서도 존재하나?
만약 객체가 독립적으로 존재하는 것도 의미가 있다면, 집합(Aggregation)을 그렇지 않다면 구성(Composition)을 사용하세요.

위임(Delegation)과 구성(Composition), 그리고 집합(Aggregation)을 상속보다 선호하면, 대개의 경우 여러분의 소프트웨어는 더 유연하고, 유지보수성, 확장성, 그리고 재사용성이 좀 더 좋아집니다.

이 세 가지(위임, 구성, 집합) 객체 지향 기법들을 이용해 LSP를 위반하지 않으면서 행동의 재사용이 가능해 집니다.

 

 

OOA&D 도구 상자

요구사항

  • 좋은 요구 사항은 시스템이 고객이 기대한 대로 동작하도록 합니다.
  • 여러분의 요구 사항이 시스템의 유스케이스의 모든 단계들을 담고 있는지 확인하세요.
  • 유스케이스를 사용해서 고객이 잊고 얘기하지 않았던 것들을 찾아내세요.
  • 유스케이스는 시스템에 추가되어야 할 불완전하거나 빠진 요구 사항들을 분명하게 합니다.
  • 요구 사항은 항상 끊임없이 변화합니다.(그리고 성장합니다.).

분석과 설계

  • 잘 디자인된 소프트웨어는 변경과 확장이 쉽다.
  • 기본적인 캡슐화와 상속 같은 객체지향 원리를 사용하여 소프트웨어를 좀 더 유연하게 만드세요.
  • 디자인이 유연하지 않으면, 변경하세요. 변경해야 하는 것이 여러분의 디자인이더라도, 결코 나쁜 디자인을 고수하지는 마세요.
  • 여러분의 각 클래스의 응집도를 높게 하세요. 여러분의 클래스 각각은 하나의 일을 정말 잘하는 것에 중점을 두어야 합니다.
  • 소프트웨어 디자인을 진행하면서, 항상 높은 응집도를 위해 노력하세요.

큰 문제들 해결하기 정리

  1. 고객의 말에 귀 기울여, 여러분이 만들어 주길 바라는 것이 무엇인지 알아낸다.
  2. 특징 리스트를 고객이 이해하는 용어를 사용하여 작성한다.
  3. 작성한 특징 리스트가 고객이 실제로 원하는 것인지 확인한다.
  4. 유스케이스 다이어그램을 사용하여 시스템의 청사진을 만든다.
  5. 큰 시스템을 여러 개의 작은 섹션으로 나눈다.
  6. 디자인 패턴을 시스템의 작은 섹션에 적용한다.

객체지향 원리

  • 변하는 것을 캡슐화 하라.
  • 구현에 의존하기 보다는 인터페이스에 의존하도록 코딩하라.
  • 각 클래스는 변경 요인이 오직 하나이어야 한다.
  • 클래스는 행동과 기능에 관한 것입니다.
  • 클래스는 확장에는 열려있고, 수정에는 닫현 있어야 한다.(OCP)
  • 공통되는 부분을 추출하여 추상화하고 한 곳에 두어 중복 코드를 피하라(DRY원리).
  • 시스템의 모든 객체는 하나의 책임만을 가지며, 객체가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다.(SRP)
  • 서브 클래스들은 부모 클래스들이 사용되는 곳에 대체될 수 있어야 한다.(LSP)

 

핵심 정리

  • 개방-폐쇄 원리는 클래스들을 확장에는 열려 있고 변경에는 닫혀 있게 함으로써, 여러분의 소프트웨어를 재사용 가능하지만, 여전히 유연하게 유지합니다.
  • 클래스가 단일 책임의 원리를 통해 한 가지 일만 하게 함으로써, OCP를 여러분의 코드에 적용하는 것을 훨씬 더 쉽게 합니다.
  • 여러분은 어떤 메소드가 클래스의 책임이 맞는지 결정하려면, 이 특정한 일을 하는 것이 이 클래스의 일인지 자문해 보세요. 그렇지 않다면, 그 메소드를 다른 클래스로 옮겨야 합니다.
  • 객체지향 코드를 거의 환성한 후에, DRY를 사용하세요. 중복 코드를 피할 수 있고, 코드 안의 각 기능이 한 곳에 있도록 할 수 있습니다.
  • DRY는 코드뿐만 아니라 요구 사항에도 적용됩니다.: 각 특징과 요구 사항을 한 곳에서 구현되게 해야 합니다.
  • 리스코프 치환 원리는 서브 클래스가 부모 클래스 대신 사용될 수 있게 함으로써 상속을 올바르게 사용할 수 있게 합니다.
  • LSP를 위반하는 코드를 발견하면, 위임, 구성, 또는 집합을 사용하여 상속을 이용하지 않고 다른 클래스의 행동을 사용하는 것을 고려해 보세요.
  • 다른 클래스의 행동이 필요한데 그 행동을 바꿀 필요가 없으면, 원하는 행동을 사용하기 위해 그 클래스에게 위임할 수 있습니다.
  • 구성은 인터페이스의 여러 구현을 통해 비슷한 여러 행동으로부터 하나의 행동을 선택할 수 있게 합니다.
  • 구성을 사용하면 구성하는 객체가 자신이 사용하는 행동을 소유하고, 구성하는 객체가 사라지면 구성에 참여한 객체도 사라집니다.
  • 집합은 다른 클래스의 생존에 영향을 미치지 않으면서 그 행동을 사용할 수 있게 합니다.
  • 집합에 참여한 클래스의 행동은 자신을 사용했던 집합 클래스가 사라진 후에도 계속 존재합니다.

 

 

 

 

 

 

 

 

 

'Programs > OOAD' 카테고리의 다른 글

10. OOA&D 생명주기  (2) 2012.09.30
9. 반복하기, 테스팅하기  (1) 2012.09.28
7. 아키텍처  (0) 2012.09.25
6. 큰 문제들 해결하기  (3) 2012.09.22
5-2. 좋은 디자인 = 유연한 소프트웨어  (0) 2012.09.22
Posted by outliers
,