Strategy pattern
동적 가능성이 있는 알고리즘군을 객체에서 분리함 (interface, 서브 클래스 활용)
어떤 행동을 사용할지 서브클래스에 맡김
Observer pattern
Subject에 observer리스트를 저장해두고 data의 변화가 생길 때 observer들에게 알림이 가도록 함
subject에서 data를 push하는 방법과 observer들이 알림을 받았을 때 data를 알아서 pull하는 방법이 있음
Decorator pattern
추상구성요소, 구상구성요소, 추상데코레이터, 구상데코레이터의 구조를 가짐
COP법칙, 변경에 닫혀있고 확장에 열려있어야 함
Factory pattern
인터페이스를 추상생산자에 의지하며 실제 객체 생성은 구상생산자인 서브클래스에 일임 함
인스턴스 생성을 서브클래스에서 결정함
Singleton pattern
유일무이한 객체 생성
Command pattern
요청을 따로 분리하여 기록으로 남길 수 있음
Client, Invoker, Command, Reciever
Adaptor pattern
클래스 간 호환이 가능하도록 함
Pacade pattern
복잡한 클래스의 인터페이스를 단순화 하여 제공함
Template method pattern
가상 클래스에서 알고리즘을 구현하고 관리함
알고리즘의 각 단계는 서브 클래스에서 작성할 수 있음
Hook 메소드도 가능
Iterator pattern
컬렉션 구현 방법을 노출하지 않고, 집합체 내 모든 항목에 접근
IEnumerator<> 타입, Collection.GetEnumerator() 등...
반복자를 반환하고, 반환된 반복자를 사용해서 컬렉션에 접근함
Composite pattern
객체를 트리구조로 구성, 부분-전체 계층 구조 구현
복합 객체(하위자식 존재하는 객체)와 개별 객체(잎 객체)를 동일한 방법으로 다룰 수 있음
Proxy pattern
특정 객체로의 접근을 제어하는 대리인을 제공
Client 객체가 Client 보조객체 (RMI stub) 에게 로컬 메소드 호출하듯 호출하고, 스텁은 서비스 보조객체 (RMI skeleton) 에게 호출을 전달하며 실제 로직 수행은 서비스 객체에서 이뤄짐
Combined pattern
Model - View - Controller 가 대표적
MVC에는 observer, strategy, composite 패턴이 사용됨
헤드퍼스트디자인패턴
헤드 퍼스트 디자인 패턴 책은 자바 언어를 통해 객체지향 프로그래밍에서 다수 사용되는 디자인 패턴에 대해 설명한다.
디자인패턴이란 공통된 개념에 이름을 부여함으로써 타인과 소통을 용이하게 하며 아키텍쳐 구상 능력을 길러주는 것에 효과가 있다. 또한 특정 컨텍스트 내에서 주어진 문제의 해결책을 제시한다. 이때 context란 반복적으로 일어날 수 있는 상황이며, problem은 context 내의 목표와 제약 조건이고, solution은 제약조건 속 목표를 이룰 수 있는 일반적인 디자인을 일컫는다.
디자인 패턴은 여러 카테고리로 나눌 수 있고 여전히 여러 의견이 분분하지만 해당 책에서는 세가지로 구분짓는다. 이는 생성 패턴, 행동 패턴, 구조 패턴이다. 생성 패턴에는 singleton, abstract factory, factory가 존재한다. 행동 패턴에는 template method, iterator, singleton, observer, state, strategy가 존재한다. 구조 패턴에는 decorator, composite, proxy, facade가 존재한다.
자신의 코드에 디자인 패턴을 적용해보려는 것은 분명 훌륭한 시도지만, 그 전에 따져야 할 것은 과연 이것이 구조를 최대한 신경쓴 것인지에 대해서다. 잘 작성된 코드의 객관적인 기준은 따지기 어려운 부분이 존재하지만 한가지 확실한 것은 잘 읽히는 코드는 보통 잘 작성된 코드라는 것이다. 코드의 가독성이 좋기 위해서는 그저 디자인 패턴에만 얽매이지 말고 최대한 단순한 방법을 찾아야 한다. 또, 디자인 패턴은 그 영향과 결과를 따져 확장성이 필요한 경우에 선택적으로 적용하는 것이 좋다. 리팩터링을 통해 구조를 개선해가며 변경될 수 있는 부분을 고려하여 내 코드에 맞게 응용하여 작성하는 것을 권장한다.
객체지향 원칙
객체지향 원칙1. 바뀌는 부분의 확장성을 고려하여 캡슐화 하고, 구성한다. (관련 : Strategy pattern)
객체지향 원칙2. 상호작용하는 객체는 되도록 느슨한 결합loose coupling 이어야 한다. 이때 느슨한 결합이란 객체 간 상호작용이 가능하지만 서로에게 깊게 연관되지 않는 것을 의미한다. (관련 : Observer pattern)
객체지향 원칙3. Open-Closed-Principle. 클래스는 확장에 열려있고 변경에 닫혀있어야 한다. (관련 : Decorator pattern)
객체지향 원칙4. Dependency Inversion Principle. 의존성 뒤집기 원칙은 추상화 된 것에 의존하고 구상 클래스에 의존하지 않아야 한다는 법칙이다. 다시말해 고수준의 클래스가 저수준의 클래스에 의존하면 안 된다는 말이다. (관련 : Factory pattern)
객체지향 원칙5. Principle of least knowledge. 최소 지식 원칙은 데메테르 법칙이라고도 불리며, 객체 사이 상호작용은 복잡하지 않아야 함을 주장한다. 상호작용 가능 사항은 총 네가지가 존재한다. 첫번째, 객체 그 자체. 두번째, 메서드에 매개변수로 전달된 객체. 세번째, 메서드를 생성하거나 인스턴스를 만든 객체. 마지막으로, 객체에 속하는 구성요소다. (관련 : Facade pattern)
객체지향 원칙6. Hollywood principle. 의존성 부패dependency rot 를 방지한다. 의존성 부패는 의존성이 복잡하게 꼬여있는 경우를 뜻하며 저수준의 구성요소는 고수준의 구성요소를 호출해선 안 된다고 주장한다. 저수준의 구성 요소를 실행하는 것은 고수준의 구성 요소가 결정해야 한다. (관련 : Template method pattern)
객체지향 원칙7. 하나의 클래스는 하나의 역할을 맡아야 한다. 이는 변경 가능성이 한가지여야 한다는 뜻이다. 이를 응집도cohesion 를 통해 해당 원칙이 얼마나 잘 지켜지고 있는지 나타낼 수도 있다. 응집도는 클래스와 모듈이 역할 혹은 특정 목적을 얼마나 일관되게 지원하는지를 나타내는 척도이다. (관련 : Iterator pattern)
Strategy pattern
동적 가능성이 있는 알고리즘군을 인스턴스에서 분리하고, 어떤 행동을 사용할지 서브클래스에 맡긴다. 이때 동적 가능성이란 기능 면에서 변경 및 확장될 가능성이 있는 부분을 뜻하며 해당하는 부분을 분리하여 캡슐화한다. 상속보다는 구성composition 을 이용한다.
Observer pattern
데이터의 주체인 subject에 observer리스트를 저장해두고 data의 변화가 생길 때 observer들에게 알림이 가도록 한다. Observer는 data의 변화를 전달 받고 싶다면 subject를 subscribe 해야 하며 반대로 전달을 그만 받고 싶다면 unsubscribe 할 수 있다. 일대다의 의존성을 지녔다고 할 수 있다. 주로 subject에서 data를 push하는 방법과 observer들이 알림을 받았을 때 data를 알아서 pull하는 방법이 있다.
Decorator pattern
객체에 추가적인 요소를 동적 할당한다. 서브클래스를 구현하는 것보다 유연하게 기능 확장이 가능하다. 추상구성요소, 구상구성요소, 추상데코레이터, 구상데코레이터의 구조를 가진다. OCP법칙을 준수하게 따를 수 있는 디자인패턴이며, 따라서 변경에 닫혀있고 확장에 열려있다. 기능이 하나 추가될 때마다 lapper class가 늘어나니 클래스 수가 많아지는 게 보통이다.
Factory Method pattern
인터페이스를 제대로 설계하여 작성된 코드는 어떤 클래스라도 특정 인터페이스만 구현하면 사용이 가능하다는 것이 다형성이다. 팩토리 메소드 패턴은 인터페이스를 추상생산자에 의지하며 실제 객체 생성은 구상생산자인 서브클래스에 일임 한다. 그말은 즉, 인스턴스 생성을 서브클래스에서 결정한다는 뜻이다. 생산과 사용을 분리한다는 면에서 추후 관리할 일이 생길 때 한 곳만 신경을 쓰면 되므로 유용하다. 그렇지만 하나의 클래스가 너무 많은 객체를 직접 생성하여 의존하게 되는 객체 의존성이 존재하기도 한다.
Abstract factory pattern
구상클래스에 의존하지 않고 서로 연관되어 의존적인 객체로 이루어진 인터페이스를 제공한다. 서브클래스에서 구상클래스를 만든다. Client는 abstract factory와 abstract product의 interface만 알면 되고, 실제 객체 생성은 해당 인터페이스 팩토리 클래스를 상속 받은 concrete factory를 통해 이뤄진다.
Singleton pattern
전역 변수의 단점은 객체를 생성하고 사용하지 않으면 메모리의 낭비를 초래한다는 점이다. 해당 문제점을 해결하기 위해 게으른 인스턴스 생성lazy instantiation 을 통해 인스턴스가 필요한 상황에 닥치기 전까지 인스턴스를 생성하지 않는 방법을 택할 수 있다. 싱글톤은 이처럼 인스턴스가 비어있을 시 생성 후 저장하고, 존재할 시 해당 인스턴스를 반환하여 객체를 유일무이하게 존재하도록 한다. 전역 접근을 제공할 수 있다. 단, 다중 스레드라도 사용할 때에 인스턴스 생성이 중복될 가능성이 존재한다. 이때 스레드의 안정성을 보장하기 위한 기법인 Double-Checked Locking 으로 동기화 문제 등을 해결할 수 있다. 가령 C#의 lock 키워드를 사용한다.
Command pattern
요청내역을 따로 분리하여 객체로 캡슐화 하고, 큐에 저장하여 기록으로 남길 수 있다. 로그를 기록하거나 직전 작업을 취소하는 등의 방향으로 활용 가능하다. 따라서 커맨드 패턴은 히스토리를 저장하므로 프로그램이 다운 되더라도 복구가 가능하다는 이점이 존재한다. 구현의 경우 Client, Invoker, Command, Reciever 로 분리된다.
Adaptor pattern
특정 클래스 인터페이스를 클라이언트에서 요구하는 인터페이스로 변환한다. 즉, 인터페이스 간의 호환이 가능하도록 한다. 메서드가 일대일로 대응하지 않는 상황에선 adapter를 완벽하게 적용은 불가하지만, 적당히 exception을 던지는 등의 방법이 존재한다.
Facade pattern
서브 시스템의 일련의 인터페이스를 통합 인터페이스로 묶어준다. 복잡한 클래스의 인터페이스를 단순화 하여 제공한다고도 할 수 있다. 그렇지만 서브 클래스의 모든 기능은 여전히 사용 가능하다. 퍼사드로 감쌀 수 있는 클래스의 갯수 제한은 딱히 존재하지 않는다.
Template method pattern
알고리즘의 골격을 정의하고 알고리즘의 일부 단계를 서브클래스에서 구현과 재정의 한다. 이때 알고리즘 사이에 별 기능이 없는 추가 구상 메서드를 끼워넣을 수도 있는데, 이를 hook 메서드라고 한다. 해당 메서드는 구상 클래스 내에서 override 하여 재정의 할 수도 있고, 안 할 수도 있다.
Iterator pattern
클라이언트에게 어떤 메서드의 반환형 등 내부 구조를 다 드러내어 신경쓰도록 만드는 것은 명백한 단점이다. 컬렉션에 동시 접근하는 멀티스레드 코드를 디자인 할 때에도 특별히 유의하게 된다. 이런 상황에서 반복자 패턴은 다형성을 지킬 수 있도록 컬렉션의 구현 방법을 노출하지 않고, 집합체 내 모든 항목에 접근할 수 있게 해준다. IEnumerator<> 타입, Collection.GetEnumerator() 등의 메서드를 사용하여 반복자를 반환하고, 반환된 반복자를 사용해서 컬렉션에 접근하는 구현 방식을 취한다. 단방향이 아닌 양방향으로 접근이 가능한 반복자도 구현이 가능하다. 실상 모든 collection 클래스는 iterable 하다.
Composite pattern
객체를 트리구조로 구성하여 부분-전체 계층 구조를 취한다. 복합 객체(하위자식 존재하는 객체)와 개별 객체(잎 객체)를 동일한 방법으로 다룰 수 있다. 복합 구조인 동시에 재귀 구조이다. 말단을 구분하기 위해 각 객체의 하위 자식 여부를 검사하거나 간단히 확인 시켜주는 메서드를 추가할 수도 있다. 컴포지트 패턴은 구조상 어쩔 수 없이 트리 구조를 다루는 역할과 실제 기능을 수행하는 역할 두가지를 한 클래스에서 전부 맡게 되는 경우가 생기는데, 이는 단일 역할 원칙을 깨는 부분이지만 대신 투명성transparency 을 확보할 수 있으므로 장단점이 존재한다. 그 밖의 장점으로는 클라이언트를 단순화 할 수 있다는 것과 전체 구조를 대상으로 하는 반복 작업 처리를 간편하게 호출할 수 있다는 것이 있다.
Proxy pattern
프록시는 특정 객체로의 접근을 제어하는 대리인을 제공한다. 마찬가지로 원격 프록시는 원격 객체의 로컬 대변자 역할을 수행한다. 이때 원격 객체란 타 가상 머신의 힙에 생존한 객체로 다른 주소 공간에서 생존 하고 있는 객체라고도 할 수 있으며, 로컬 대변자란 어떤 메소드를 호출했을 때 다른 원격 객체에게 메소드 호출을 전달하는 객체를 뜻한다. 따라서 주소 공간이 로컬과 원격 둘로 존재할 때, client 객체가 client 보조객체 (RMI stub) 에게 로컬 메소드 호출하듯 호출을 하면 스텁은 서비스 보조객체 (RMI skeleton) 에게 호출을 전달한다. 그리고 실제 로직 수행은 서비스 객체에서 이뤄진다. RMI는 Remote method invocation으로 자바의 원격 메소드 호출기이며 C#에서는 Windows Communication Foundation 혹은 gRPC, Web API 등을 사용할 수 있다. 프록시 패턴에는 원격 프록시 외에도 가상 프록시, 보호 프록시 등이 존재한다.
Combined pattern
2개 이상의 패턴이 결합한 경우를 복합 패턴이라고 한다. 대표적으로 Model - View - Controller가 존재한다. MVC에는 observer, strategy, composite 패턴이 사용된다. View와 controller 간에 전략 패턴이 이용되어 view 객체가 사용하는 전략을 controller가 제공한다. 또한 view의 경우 최상위 화면 구성요소에게만 메세지를 전달하는 것으로 모든 구성 요소를 편하게 바꿀 수 있도록 컴포지트 패턴을 사용한다. 마지막으로 model은 view와 controller을 옵저버로 등록하여 데이터 변경 사항을 전달한다.
'코딩 공부 > 디자인패턴' 카테고리의 다른 글
프록시 패턴 (1) | 2024.12.09 |
---|---|
반복자 패턴, 컴포지트 패턴 (0) | 2024.12.05 |
템플릿 메소드 패턴 (0) | 2024.12.05 |
어댑터 패턴, 퍼사드 패턴 (1) | 2024.12.04 |
커맨드 패턴 (0) | 2024.12.04 |