2015년 6월 16일 화요일

Design Pattern 정리

1. 디자인 패턴이 필요한 이유 
- 특정 클래스에서 객체 생성
 객체를 생성할 때 클래스 이름을 명시하면 어떤 특정 인터페이스가 아닌 어떤 특정 구현에 종속됩니다. 이런 종속은 앞으로의 변화를 수용하지 못합니다. 이를 방지하려면 객체를 직접 생성해서는 안됩니다.
(디자인 패턴 : 추상 팩토리, 팩토리 메서드, 원형)

-  특정 연산에 대한 의존성
 특정한 연산을 사용하면, 요청을 만족하는 한 가지 방법에만 매이게 됩니다. 요청의 처리 방법을 직접 코딩하는 방법을 피하면, 컴파일 시점과 런타임 모두를 만좃하면서 요청 처리 방법을 쉽게 변경할 수 있습니다.
(디자인 패턴 : 책임 연쇄, 명령)

-  하드웨어와 소프트웨어 플랫폼에 대한 의존성
 기존에 존재하는 시스템 인터페이스와 응용프로그래램 프로그래밍 인터페이스는 소프트웨어 및 하드웨어 플랫폼마다 모두 다릅니다. 특정 플랫폼에 종속된 소프트웨어는 다른 플랫폼에 이식하기도 어렵고요. 또한 본래의 플랫폼에서도 버전의 변경을 따라가기 어려울 수 도 있습니다. 이런 플랫폼 종속성을 제거하는 것은 시스템 설계에 있어 매우 중요합니다,.
(디자인 패턴 : 추상 팩토리, 가교)

- 객체의 표현이나 구현에 대한 의존성
 사용자가 객체의 표현 방법, 저장 방법, 구현 방법, 존재의 위치에 대한 모든 방법을 알고 있다면 객체를 변경할 때 사용자도 함"께 변경해야 합니다. 이런 정보를 사용자에게 감춤으로써 변화의 파급을 막을 수 있습니다.
(디자인 패턴 : 추상 팩토리, 가교, 메멘토, 프록시)

- 알고리즘 의존성
 알고리즘 자체를 확장할 수도, 최적화할 수도, 다른 것으로 대체할 수도 있는데, 알고리즘에 종속된 객체라면 알고리즘이 변할 때마다 객체도 변경해야 합니다. 그러므로 변경이 가능한 알고리즘은 분리해 내는 것이 바람직합니다.
(디자인 패턴 : 빌더, 반복자, 전략, 템플리 메서드, 방문자)

- 높은 결합도
 높은 결합도를 갖는 클래스들은 독립적으로 재사용하기 어렵습니다. 높은 결합도를 갖데 되면 하나의 커다란 시스템이 되어 버립니다. 이렇게 되면 클래스 하나르 수정하기 위해서 전체를 이해해야 하고 다른 많은 클래스도 변경해야 합니다. 또한 시스템은 배우기도 힘들고, 이식은 커녕 유지보수하기 조차도 어려운 공룡이 되어 버립니다.
 약한 결합도는 클래스 자체의 재사용을 가능하게 하고 시스템의 이해와 수정, 확장이 용이해서 이식성을 증대시킵니다. 추상 클래스 수준에서 결합도를 정희한다거나 계층화시키는 방법으로 디자인 패턴은 낮은 결합도의 시스템을 만들도록 합니다.
(디자인 패턴 : 추상 팩토리, 가교, 책임 연쇄, 명령, 퍼사드, 중재자, 감시자)

- 서브 클래싱을 통한 기능 확장
 객체 합성과 위임은 행동 조합을 위한 상속보다 훨씬 유연한 방법입니다. 기존 객체들을 새로운 방식으로 조합함으로써 새로운 서브클래스를 정의하지 않고도 응용프로그램에 새로운 기능성을 추가할 수 있습니다. 한편, 객체 합성을 많이 사용한 시스템은 이해하기가 어려워집니다. 많은 디자인 패턴에서는 그냥 서브클래스를 정의하고 다른 인스턴스와 새로 정의한 클래스의 인스턴스를 합성해서 기능을 재정의하는 방법을 도입합니다.
(디자인 패턴 : 가교, 책임 연쇄, 장식자, 감시자 , 전력)

- 클래스 변경이 편하지 못한 점
 가끔 클래스를 변경하는 작업이 그렇게 단순하지 않을 때가 많습니다. 소스 코드가 필요한데 없다고 가정해 봅시다. 또한 어떤 변경을 하면 기존 서브클래스의 다수를 수정해야 한다고 가정합시다. 디자인 패턴은 이런 환경에서 클래스를 수정하는 방법을 제시합니다.
(디자인 패턴 : 적응자, 장식자, 방문자)

2. 디자인 패턴을 고르는 방법
- 패턴이 어떻게 문제를 해결하는지 파악합시다.
- 패턴의 의도 부분을 봅시다.
- 패턴들 간의 관련성을 파악합시다.
- 비슷한 목적의 패턴들을 모사어 공부합시다.
- 재설꼐의 원인을 파악합시다.
- 설계에서 가변성을 가져야 하는 부분이 무엇인지 파악합시다.

4. 디자인 패턴을 통해 다양화 할 수 있는 설계 측면

 목적
디자인 패턴  
이 패턴을 통해 다양화 할 수 있는 부분
생성
 추상 팩토리 (Abstract Factory)
 제품 객체군
 빌더(Builder)
 복합 객체 생성 방법
 팩초리 메서드(Factory Method)
 인스턴스화 될 객체의 서브클래스
 원형(Prototype)
 인스턴스화 될 객체의 클래스
 단일체(Singleton)
 클래스의 인스턴스가 하나일 때
구조
 적응자(Adapter)
 객체에 대한 인터페이스
 가교(Bridge)
 객체 구현
 복합체(Composite)
 객체의 합성과 구조
 장식자(Decorator)
 서브클래싱 없이 객체의 책임성
 퍼사드(Facade)
 서브시스템에 대한 인터페이스
 플라이급(Flyweight)
 객체의 저장 비용
 프록시(Proxy)
 객체 접근 비용
행동
 책임 연쇄(Chain of Responsibility)
 요청을 처리하는 객체
 명령(Command)
 요청을 처리 시점과 처리 방법  
 해석자(Interpreter)
 언어의 문법과 해석 방법
 반복자(Iterator)
 집합 객체 요소들의 접근 방법 및 순회 방법
 중재자(Mediator)
 어떤 객체들이 어떻게 상호작용하는지
 메멘토(Memento)
 어제 어떤 정보를 객체의 외부에 저장하는지
 감시자(Observer)
 다른 객체의 종속적인 객체 수
 종속적인 객체들의 상태 변경 방법
 상태(State)
 객체의 상태
 전략(Strategy)
 알고리즘
 템플릿 메소드(Template Method)
 알고리즘 단계   
 방문자(Visitor)
 클래스의 변경 없이 객체에 적용할 수 있는 연산

5. 패턴 대략적 설명

스트래티지 패턴(Strategy Pattern)
    - 알고리즘군을 정의하고 각각을 캐슐화하여 교환해서 사용할 수 있도록 만든다.
    - 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.
    - 구성을 사용한다.
    - 일반적으로 서브클래스를 만드는 방법을 대신하여 유연성을 극대화하기 위한 용도로 쓰인다.
    - 예: QuarkBehavior & FlyBehavior
    

옵저버 패턴(Observer Pattern)
    - 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고
       자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.
    - 주제(Subject) & 옵저버(Observer)
    - Observable & Observer:
         Observable 에 register, remove, notify 가 있고, 
         Observer 에 update 가 있다. (notify 에서 update 를 호출)
    - 예: 신문 구독 서비스, 기상관측 시스템
    

데코레이터 패턴(Decorator Pattern)
    - 객체에 추가적인 요건을 동적으로 첨가한다.
    - 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.    
    - 예: 스타버즈 커피
    
    
팩토리 패턴(Factory Pattern)    
    - 팩토리 메서드 패턴 :
       객체를 생성하기 위한 인터페이스를 정의하는데, 어떤 클래스의 인스턴스를 만들지는 서브클래스에서 결정하게 만든다.
       클래스의 인스턴스를 만드는 일을 서브클래스에 맡긴다.
    - 제품을 생산하는 부분과 사용하는 부분을 분리시킬 수 있다.
    - 추상 팩토리 패턴 :
       인터페이스를 이용하여 서로 연관된, 또는 의존하는 객체를 구상 클래스를 지정하지 않고 생성한다.
       구상 클래스는 서브 클래스에 의해 만들어진다.


싱글턴 패턴(Singleton Pattern)
    - 해당 클래스의 인스턴스가 하나만 만들어지고,
       어디서든지 그 인스턴스에 접근할 수 있도록(전역 접근) 하기 위한 패턴
       
       
커맨드 패턴(Command Pattern)
    - 요구 사항을 객체로 캡슐화 할 수 잇으며, 매개변수를 써서 여러 가지 다른 요구 사항을 집어넣을 수 있다.
       또한 요청 내역을 큐에 저장하거나 로그로 기록할 수도 잇으며, 작업취소 기능도 지원 가능하다.
    - 예: 리모콘
    - 서블릿의 doGet(), doPost() 또는 스트럿츠의 Action() 메서드도 커맨드 패턴이지 않을까?
    

어댑터 패턴(Adapter Pattern)
    - 한 클래스의 인터페이스를 클라이언트에서 사용하고자 하는 다른 인터페이스로 변환한다.
       어댑터를 이용하면 인터페이스 호환성 문제 때문에 같이 쓸 수 없는 클래스들을 연결해서 쓸 수 있다.  
       

퍼사드 패턴(Facade Pattern)
    - 어떤 서브시스템의 일련의 인터페이스에 대한 통합된 인터페이스를 제공한다.
       퍼사드에서 고수준 인터페이스를 정의하기 때문에 서브시스템을 더 쉽게 사용할 수 있다.
    - 서브시스템의 호출을 퍼사드에서 처리해준다. (기본 명령 호출 정도랄까...)
    - 일련의 클래스들에 대한 인터페이스를 단순화 시킨다.
    
    - 각 패턴별 차이점:
       데코레이터 패턴 : 인터페이스는 바꾸지 않고 책임(기능)만 추가
       어댑터 패턴 : 한 인터페이스를 다른 인터페이스로 변환
       퍼사드 패턴 : 인터페이스를 간단하게 바꿈


템플릿 메서드 패턴(Template Method Pattern)
    - 메서드에서 알고리즘의 골격을 정의한다.
       알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있다.
       템플릿 메서드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의할 수 있다.
    - 스트래티지 패턴과 다른 점:
       템플릿 메서드 패턴은 알고리즘의 개요를 정의한다. 실제 작업 중 일부는 서브클래스에서 처리.
       스트래티지 패턴은 객체 구성을 통해서 알고리즘을 캡슐화 및 구현
    - 예) Arrays.sort(배열); --- compareTo() 를 구현하도록 되어 있다.
            Applet , init(), start(), stop(), destory()
            그렇다면 서블릿에도 템플릿 메서드가 쓰이는 거구나. init() - service() - destory()

            
이터레이터 패턴(Iterator Pattern)
    - 컬렉션 구현 방법을 노출시키지 않으면서도
       그 잡합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공한다.
    - 컬렉션의 구현을 드러내지 않으면서 컬렉셔네 있는 모든 객체들에 대해 반복작업할 수 있다.
    

컴포지트 패턴(Composite Pattern)
    - 객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들 수 있다.
       이 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된
       복합 객체(composite)를 똑같은 방법으로 다룰 수 있다.
    - 클라이언트에서 객체 컬렉션과 개별 객체를 똑같은 식으로 처리할 수 있다.
    - 예) 트리 구조의 패턴, 디렉토리 구조
    - 예) XMLObject 객체가 컴포지트 패턴을 구현한 게 아닐까
    

스테이트 패턴(State Pattern)
    - 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있다.
       마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있다.
    - 상태 전환의 흐름을 결정하는 코드를 어느 쪽에 집어넣는지 잘 고려해야 한다.
       (상태 객체인지, Context 객체인지)
    - 각 상태를 클래스로 캡슐화함으로써 나중에 변경시켜야 하는 내용을 국지화시킬 수 있다.
    - 스트래티지 패턴:
          어떤 클래스의 인스턴스를 만들고 그 인스턴스에게 어떤 행동을 구현하는 전략 객체를 건내준다.
       스테이트 패턴:
          컨텍스트 객체를 생성할 때 초기 상태를 지정해주는 경우 이후로는 컨텍스트 객체가 알아서 상태를 변경.
          
          
프록시 패턴(Proxy Pattern)
    - 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴
    - 다른 객체를 대변한느 객체를 만들어서 주 객체에 대한 접근을 제어할 수 있다.
    - 원격프록시(remote proxy): 원격 객체에 대한 접근 제어
                                                클라이언트와 원격 객체 사이에서 데이터 전달을 관리
       가상프록시(virtual proxy): 생성하기 힘든(인스턴스를 만드는 데 많은 비용이 드는) 자원에 대한 접근 제어
       보호프록시(protection proxy): 접근 권한이 필요한 자원에 대한 접근 제어
                                                     호출하는 쪽의 권한에 따라서 객체에 있는 메소드에 대한 접근 제어
       방화벽 프록시: 일련의 네트워크 자원에 대한 접근 제어
       스마트 레퍼런스 프록시: 주 객체가 참조될 때마나 추가 행동을 제공. 객체에 대한 레퍼런스 개수를 세는 등
       캐싱 프록시: 비용이 많이 드는 작업의 결과를 임시로 저장
                          웹 서버 프록시 또는 컨텐츠 관리 및 퍼블리싱 시스템 등에서 사용
       동기화 프록시: 여러 스레드에서 주 객체에 접근하는 경우 안전하게 작업을 처리할 목적(분산 환경 등에서 사용)
       복잡도 숨김 프록시: 복잡한 클래스들의 집합에 대한 접근을 제어하고 복잡도를 숨겨줌
                                     퍼사드 프록시라고도 함.
                                     프록시에서는 접근을 제어하지만 퍼사드 패턴에서는 대체 인터페이스만 제공
       지연 복사 프록시: 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사 제어
       
    - 아래 객체들은 모두 클라이언트와 객체 사이에 끼여들어서 요청을 전달한다.
         데코레이터 패턴: 클래스에 새로운 행동을 추가하기 위한 용도
         어댑터 패턴: 다른 객체의 인터페이스를 바꿔주기 위한 용도
         프록시 패턴: 어떤 클래스에 대한 접근을 제어하기 위한 용도
    - java.reflect.Proxy 에 기능이 내장되어 있다.
    
    
디자인 패턴 정의
    - 패턴이란 특정 컨텍스트 내에서 주어진 문제에 대한 해결책이다.
    - 어떤 컨텍스트 내에서 일련의 제약조건에 의해 영향을 받을 수 있는 문제에 봉착했다면,
       그 제약조건 내에서 목적을 달성하기 위한 해결책을 찾아낼 수 있는 디자인을 적용하면 된다.    
      

주의점 및 추가 사항      
    - 디자인 패턴의 과다한 사용은 불필요하게 복잡한 코드를 초래할 수 있다.
       항상 가장 간단한 해결책으로 목적을 달성할 수 있도록 하고, 반드시 필요할 때만 디자인 패턴을 적용하자.      
    - 코딩할 때 어떤 패턴을 사용하고 있는지 주석으로 적어주자.
       클래스와 메서드 이름을 만들 때도 사용 중인 패턴이 분명하게 드러날 수 있도록 해보자.
       다른 개발자들이 그 코드를 볼 때 무엇을 어떻게 구현했는지 훨씬 빠르게 이해할 수 있다.


참조: 
- http://ohgyun.com/279
- http://bleedmin.tistory.com/entry/Chapter1-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80

댓글 없음:

댓글 쓰기