디자인 원칙
– 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리한다.
– 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
– 상속보다는 구성을 활용한다.
– 서로 상호작용을 하는 객체 사이에서는 가능하면 느슨하게 결한하는 디자인을 사용해야 한다.
– 클래스는 확장에 대해서는 열려 있어야 하지만 코드 변경에 대해서는 닫혀 있어야 한다.
(OCP : Open-Closed Principle)
– 추상화된 것에 의존하도록 만들어라. 구상클래스에 의존하도록 만들지 않도록 한다.
– 최소 지식 원칙 – 정말 친한 친구하고만 얘기하라.
(다음 네 종류의 객체의 메서드만 호출한다.
1. 객체 자체 / 2. 메서드에 매개변수로 전달된 객체 / 3. 그 메서드에서 생성하거나 인스턴스를 만든 객체 / 4. 그 객체에 속하는 구성 요소)
– 헐리우드 원칙 – 먼저 연락하지 마세요. 저희가 연락 드리겠습니다.
– 클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.
+ … 나중에 어떻게 바뀔 것인가?
스트래티지 패턴(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://vincehuston.org/dp/