The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.

 

프록시 패턴은 다른 개체에 대한 대체 또는 자리 표시자를 제공할 수 있는 구조적 디자인 패턴이다. 

프록시는 원래 개체에 대한 액세스를 제어하므로 요청이 원래 개체에 전달되기 전이나 후에 수행할 수 있다.

 

 

 

실제 작업은 RealSubject 에서 처리되며, Proxy 는 RealSubject 객체를 대신하면서 접근을 제어한다.

Proxy 에는 RealSubject 에 대한 레퍼런스가 들어 있고, 클라이언트는 항상 Proxy 를 통해 RealSubject 과 데이터를 주고 받는다.

 

 

 

 

장점

  • 클라이언트가 알지 못하는 상태에서 서비스 개체를 제어할 수 있다
  • 클라이언트가 신경 쓰지 않을 때 서비스 개체의 수명 주기를 관리할 수 있다 (프록시는 서비스 개체가 준비되지 않았거나 사용할 수 없는 경우에도 작동함)
  • 개방/폐쇄 원칙 => 서비스나 클라이언트를 변경하지 않고 새 프록시를 도입할 수 있다

단점

  • 많은 새 클래스를 도입해야 하므로 코드가 더 복잡해질 수 있다
  • 서비스의 응답이 지연될 수 있다

왜사용하는가?

Proxy 패턴은 다양한 방식으로 변형 가능하다

 

  • 동적 생성 프록시 => 실제 요청(action() 메소드 호출)이 들어 왔을 때 실제 객체를 생성
    • 실제 객체의 생성에 많은 자원이 소모 되지만 사용 빈도는 낮은 경우

 

  • 지연 초기화(가상 프록시) => ex. 항상 가동되어 시스템 리소스를 낭비하는 무거운 서비스 개체가 있는 경우
    • 앱이 시작될 때 객체를 생성하는 대신 객체 초기화가 실제로 필요한 시점으로 지연될 수 있다
    • 생성하기 힘든 자원에 대한 접근 제한

 

  • 액세스 제어(보호 프록시) => 특정 클라이언트만 서비스 개체를 사용할 수 있도록 하는 경우
    • ex. 접근 권한이 필요한 자원에 대한 접근 제어 
    • 프록시는 클라이언트의 자격 증명이 일부 기준과 일치하는 경우에만 서비스 개체에 요청을 전달할 수 있다
    • ex. 개체가 운영 체제의 중요한 부분이고 클라이언트가 다양한 실행 응용 프로그램(악의적인 응용 프로그램 포함)인 경우

 

  • 원격 서비스의 로컬 실행(원격 프록시) => 서비스 개체가 원격 서버에 있는 경우
    • 프록시는 네트워크를 통해 클라이언트 요청을 전달하여 네트워크 작업의 모든 세부 사항을 처리

 

  • 로깅 요청(로깅 프록시) => 서비스 개체에 대한 요청 기록을 유지하려는 경우
    • 프록시는 서비스에 전달하기 전에 각 요청을 기록할 수 있다

 

  • 캐싱 요청 결과(캐싱 프록시) => 클라이언트 요청의 결과를 캐시하고, 이 캐시의 수명 주기를 관리해야 하는 경우
    • 프록시는 항상 동일한 결과를 생성하는 반복 요청에 대해 캐싱을 구현할 수 있다
    • 프록시는 요청의 매개변수를 캐시 키로 사용할 수 있다

 

    • 클라이언트가 없을 때 알아서 해제해야 하는 경우 => ex. 무거운 서비스 개체인 경우
      • 프록시는 서비스 개체 또는 해당 결과에 대한 참조를 얻은 클라이언트를 추적할 수 있다. 즉, 프록시는 클라이언트를 통해 클라이언트가 여전히 활성 상태인지 확인할 수 있다. 클라이언트 목록이 비어 있으면 프록시가 서비스 개체를 닫고 기본 시스템 리소스를 해제할 수 있다
      • 프록시는 클라이언트가 서비스 개체를 수정했는지 여부도 추적할 수 있다. 그리고 변경되지 않은 개체는 다른 클라이언트에서 재사용할 수 있다

'책을 읽자 > Design Patterns' 카테고리의 다른 글

Decorator Pattern  (0) 2021.08.18
Template Method Pattern  (0) 2021.08.02
Strategy Pattern  (0) 2021.08.02
Flyweight Pattern  (0) 2021.01.19
Bridge Pattern  (0) 2021.01.19

The Decorator Pattern attaches additional responsibilities to an object dynamically

Decorators provide a flexible alternative to subclassing for extending functionality.

 

Decorator Pattern 은 동일한 타입의 객체를 품고 있는 패턴으로, 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행할 수 있다.

 

 

아래는 커피를 주문할 때 주문시스템을 구현하는 방식을 데코레이터 패턴으로 나타낸 것이다.

  • 한 객체를 여러 개의 데코레이터로 감쌀 수 있는데, 이때 decorator 의 super class 는 자신이 감싸고 있는 객체의 super class 와 같다 => 따라서 싸여져 있는 객체(ex. darkroast) 대신 다른 프록시를 넣어도 됨
  • 데코레이터는 자신이 장식하고 있는 객체에게 어떤 행동을 위임하는 것 외에 원하는 추가적인 작업을 수행할 수 있다
  • 상속을 통해 형식을 맞춘다 (상속을 통해 행동을 물려받음X)
  • 팩토리/빌터 패턴과 함께 사용한다.

 

 

 

장점

  • 새 하위 클래스를 만들지 않고 개체의 동작을 확장할 수 있다
  • 런타임에 개체에서 책임을 추가하거나 제거할 수 있다
  • 개체를 여러 데코레이터로 래핑하여 여러 동작을 결합할 수 있다
  • 단일 책임 원칙 => 많은 동작 변형을 구현하는 모놀리식 클래스를 여러 개의 작은 클래스로 나눌 수 있다.

단점

  • 래퍼 스택에서 특정 래퍼를 제거하는 것은 어렵다
  • 동작이 데코레이터 스택의 순서에 의존하지 않는 방식으로 데코레이터를 구현하는 것은 어렵다
  • 구성요소를 초기화하는 데 필요한 코드가 복잡해진다
    • java i/o library `InputStream` => InputStream in = LineNumerInputStream(new BufferedInputStream(new FileInputStream(file)))

언제사용하는가?

  • 개체를 사용하는 코드를 손상시키지 않고 런타임에 개체에 추가 동작을 할당할 수 있어야 하는 경우
  •  상속을 사용하여 개체의 동작을 확장하는 것이 어색하거나 불가능할 때
    • final 클래스의 추가 확장이 필요한 경우 - 기존 동작을 재사용하는 유일한 방법은 데코레이터 패턴을 사용하여 클래스를 자체 래퍼로 래핑하는 방법밖에 없다

'책을 읽자 > Design Patterns' 카테고리의 다른 글

Proxy pattern  (0) 2021.08.18
Template Method Pattern  (0) 2021.08.02
Strategy Pattern  (0) 2021.08.02
Flyweight Pattern  (0) 2021.01.19
Bridge Pattern  (0) 2021.01.19

The Template Method defines the steps of an algorithm and allows subclasses to provide the implementation for one or more steps.

 

Template Method 패턴은 실행 순서에 대한 골격을 제공하고, 알고리즘의 특정 단계를 재정의 함으로써 다양화 할 수 있는 패턴이다.

 

 

 

커피와 차를 제공하는 프로세스를 보면 겹치는 부분이 많다. 그리고 순서도 같다.

커피와 차를 만드는 프로세스의 중복을 제거하기 위해서는 추상화가 필요하므로, 아래와 같이 템플릿 메소드 패턴을 사용한다.

 

 

주의할 점은 서브클래스에서 CaffeineBeverage를 만드는 프로세스를 재정의 할 수 없도록 템플릿 메소드를 final로 선언해야 한다.

세부 메소드 중 변하는 알고리즘은 하위 클래스들이 구현할 수 있도록 추상 메소드로 선언한다.

마지막으로 세부 메소드 중 변하지 않는 알고리즘은 상위 클래스가 이를 구현하고 하위 클래스들은 아예 볼 수 없도록 private 메소드로 선언하자.

(+ 전체 알고리즘 골격의 스텝을 나눌 때, 필수적이지 않은 부분은 hook로 빼서 구현한다.)

 

 

템플릿 메소드 패턴 vs 스트래티지 패턴

템플릿 메소드 패턴 스트래티지 패턴
상속을 기반으로 한다. 하위 클래스에서 해당 부분을 확장하여 알고리즘의 일부를 변경할 수 있다.
=> 알고리즘의 순서를 정의하고, 실제 작업 중 일부는 서브클래스에서 구현함으로써 각 단계마다 다른 구현을 사용하면서도 알고리즘 구조를 유지할 수 있다.
구성을 기반으로 한다. 해당 동작에 해당하는 다른 전략을 제공하여 개체 동작의 일부를 변경할 수 있다. 

=> 일련의 알고리즘군을 정의하고, 클라이언트에서는 그 알고리즘들을 서로 바꿔가며 사용할 수 있다.
클래스 수준에서 작동하므로 정적이다. 개체 수준에서 작동하므로 런타임에 동작을 전환할 수 있다.
코드 중복이 거의 없음 객체 구성을 사용하므로 유연하다 & 의존성이 없다

 

 

장점

  • 클라이언트가 전체 알고리즘의 특정 부분만 재정의 => 알고리즘의 다른 부분에 발생하는 변경 사항의 영향을 줄일 수 있다
  • 중복 제거

단점

  • 알고리즘의 제공된 골격이 제한적이다
  • 하위 클래스를 통해 기본 단계 구현을 억제 하여 리스코프 치환 원칙을 위반할 수 있다
  • 하위 클래스에서 코드를 구현하기때문에 의존적일 수 있음

언제사용하는가? 

  • 클라이언트가 알고리즘의 특정 단계만 확장하고 전체 알고리즘이나 해당 구조는 확장하지 않도록 할 때
  • 약간의 차이가 있지만 거의 동일한 알고리즘을 포함하는 여러 클래스가 있는 경우

'책을 읽자 > Design Patterns' 카테고리의 다른 글

Proxy pattern  (0) 2021.08.18
Decorator Pattern  (0) 2021.08.18
Strategy Pattern  (0) 2021.08.02
Flyweight Pattern  (0) 2021.01.19
Bridge Pattern  (0) 2021.01.19

The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. 

Strategy lets the algorithm vary independently from clients that use it.

 

Strategy Pattern 은 알고리즘군을 정의하고, 그 알고리즘들을 서로 바꿔가면서 쓸 수 있게 하는 패턴이다.

각 알고리즘은 캡슐화되어 있기 때문에 클라이언트에서 손쉽게 서로 다른 알고리즘을 사용할 수 있다. 이때, 클라이언트들에게 객체 구성을 통해 알고리즘 구현을 선택할 수 있게 해준다. (유연하다)

 

 

 

Duck class에 fly 와 quack 기능을 추가한다고 하자. 이때, 어떤 상속 클래스들은 날 수 없을 수도 있고, 어떤 클래스들은 꽥꽥 울 수 없다.

만약 단순하게 Child class 에서 fly 와 quack 메소드를 오버라이드 하게 되면 변경이 있을 때마다 모든 서브클래스를 살펴봐야 한다.

 

 

 

이러한 경우, 아래와 같이 오리마다 달라지는 부분과 그렇지 않은 부분을 분리하고,

오리를 정의할 때 동적으로 flyBehavior/quackBehavior 알고리즘을 선택할 수 있도록 하는 것이 Strategy 패턴이다.

Q. Behavior을 만들 때 왜 인터페이스를 쓰는 것일까?
= 상위 형식에 맞추어 프로그래밍
= 실제 실행 시에 쓰이는 객체가 코드에 의해 고정되지 않는다 (다형성)
= 클라이언트측에서 사용할 때, 실제 객체 형식을 몰라도 된다
Q. 다형성이란?

같은 모양의 코드가 다른 행위를 하는 것 (OCP, DIP 와 관련) => 중복을 줄이며 변경 확장에 유연하다
방법 1) Overriding
- 슈퍼클래스를 상속받은 서브클래스에서 슈퍼클래스의 (추상)메소드를 같은 이름, 리턴값, 인자이나 새로운 로직으로 정의
- 런타임 시점 선택

방법 2) Overloading
- 하나의 클래스에서 같은 이름의 다른 인자를 가진 메소드들을 여러개 정의
- 컴파일 시점 선택
- ex) 생성자 오버로딩

방법 3) 함수형 인터페이스
- 람다식을 사용하기 위한 API로, 자바에서 인터페이스에 구현할 메소드가 1개일 때를 말함
- ex) enum 계산기

출처 - https://brunch.co.kr/@kd4/4

 

 

장점

  • 런타임에 개체 내부에서 사용되는 알고리즘을 교환할 수 있다.
  • 알고리즘을 사용하는 코드에서 알고리즘의 구현 세부 정보를 분리할 수 있다.
  • 상속을 구성으로 대체할 수 있다.
  • 개방/폐쇄 원칙 => 컨텍스트를 변경하지 않고도 새로운 전략을 도입할 수 있다.

단점

  • 알고리즘이 몇 개만 있고 거의 변경되지 않는 경우 오히려 복잡해진다.

언제 사용하는가? 

  • 객체 내에서 다양한 알고리즘 변형을 사용하고 런타임 중에 한 알고리즘에서 다른 알고리즘으로 전환할 수 있도록 하려는 경우 
  • 일부 동작을 실행하는 방식만 다른 유사한 클래스가 많이 있는 경우 (c.f. 템플릿 메소드 패턴)
  • 클래스에 동일한 알고리즘의 다른 변형 간에 전환하는 대규모 조건부 연산자가 있는 경우

'책을 읽자 > Design Patterns' 카테고리의 다른 글

Proxy pattern  (0) 2021.08.18
Decorator Pattern  (0) 2021.08.18
Template Method Pattern  (0) 2021.08.02
Flyweight Pattern  (0) 2021.01.19
Bridge Pattern  (0) 2021.01.19

Use the Flyweight Pattern when one instance of a class can be used to provide many virtual instances

 

Flyweight 패턴은 비용이 큰 자원을 공통으로 사용할 수 있도록 만드는 패턴이다.
자원에 대한 비용은 크게 두가지로 나눠 볼 수 있다.

1. 중복 생성될 가능성이 높은 경우.
중복 생성될 가능성이 높다는 것은 동일한 자원이 자주 사용될 가능성이 매우 높다는 것을 의미한다. 이런 자원은 공통 자원 형태로 관리하고 있다가 요청이 있을 때 제공해 주는 편이 좋다.

2. 자원 생성 비용은 큰데 사용 빈도가 낮은 경우.
이런 자원을 항상 미리 생성해 두는 것은 낭비이다. 따라서 요청이 있을 때에 생성해서 제공해 주는 편이 좋다.

이 두가지 목적을 위해서 Flyweight 패턴은 자원 생성과 제공을 책임진다. 자원의 생성을 담당하는 Factory 역할과 관리 역할을 분리하는 것이 좋을 수 있으나, 일반적으로는 두 역할의 크기가 그리 크지 않아서 하나의 클래스가 담당하도록 구현한다.

출처 - https://effectiveprogramming.tistory.com/entry/Flyweight-%ED%8C%A8%ED%84%B4

 

예를 들어 새로운 조경 설계 애플리케이션에서 나무 객체를 추가하려고 한다.

애플리케이션에서 나무는 X, Y 위치를 가지면서 나이에 따라 동적인 크기로 그릴 수 있다고 하자.

 

 

이 경우 나무를 나타나는 것은 문제없지만,

대규모의 큰 나무 숲을 만들 경우, 각각의 나무에 대해 위치 정보와 나이 대한 정보를 관리하며 

Tree 인스턴스를 생성하고 제거하면서 프로그램이 느려질 수 있다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이러한 경우 수천 개의 Tree 오브젝트가있는 대신 Tree 인스턴스는 하나만 존재하고, 모든 트리의 상태를 유지하는 클라이언트 오브젝트가 있도록 시스템을 설계 하는 것이 바로 Flyweight 패턴이다.

 

HeadFirst Design Pattern - Flyweight Pattern

 

 

위 클래스 다이어그램에서 보는 바와 같이 Tree 객체와 이 자원을 관리하는 매니저 클래스 TreeManager 클래스로 바꿀 수 있다.

Tree의 경우 여러 경우를 미리 생성해두고 이를 Manager에서 관리하게 된다.

 

이를 코드로 구현해보면 아래와 같다.

.

 

장점

  • 런타임시 객체 인스턴스 수를 줄여 메모리를 절약한다.
  • 많은 virtual 개체의 상태를 단일 위치로 중앙 집중화한다.

단점

  • 일단 구현하면 클래스의 단일 논리적 인스턴스가 다른 인스턴스와 독립적으로 동작 할 수 없다.

언제 사용하는가?

  • 클래스의 많은 인스턴스가 필요할 때 사용되며, 그리고 그 객체들을 모두 동일하게 제어할 때 사용한다.

'책을 읽자 > Design Patterns' 카테고리의 다른 글

Proxy pattern  (0) 2021.08.18
Decorator Pattern  (0) 2021.08.18
Template Method Pattern  (0) 2021.08.02
Strategy Pattern  (0) 2021.08.02
Bridge Pattern  (0) 2021.01.19

Use the Bridge Pattern to vary not only your implementations, but also your abstractions.

 

브리지 패턴은 추상층을 분리하여 각자 독립적으로 변형 및 확장이 가능하도록 만드는 패턴이다.

즉, 기능과 구현에 대해 두 개의 별도의 클래스로 구현한다.

 

예를 들어 TV 리모컨용 코드를 작성한다고 하자. 일단 각 리모컨으로 할 수 있는 여러가지 기능들이 있다.

이 기능들 중에 RCAControl, SonyControl이라는 두가지 기능이 있다고 해보자. 그런데 이 리모컨을 사용하는 여러가지 TV 모델들(LG, Samsung)도 있을 수 있다.

 

 

 

단순하게 생각했을 때, 왼쪽과 같이 리모컨이라는 동일한 추상화를 기반으로 TV의 각 모델마다 서로 다른 기능을 구현해 볼 수 있을 것이다.

 

하지만, 문제는 리모컨의 기능과 TV 모델들이 바뀔 수 있다는 점이다. 여러 TV에서 구현을 다양화할 수 있도록 리모컨은 이미 추상화했지만, 리모컨의 기능이 개선될때마다 TV 모델들의 구현 또한 변경되어야 할 수 있다.

 

뿐만 아니라, 리모컨의 기능이 추가된다면 해당 리모컨 기능에 대한 구현을 해야 한다.

 

 

 

 

 

 

 

 

 

 

 

이러한 경우에 Bridge 패턴을 이용하면 두 가지를 별도의 클래스 계층 구조에 배치하여 구현과 추상화를 다양화할 수 있다.

즉, 구체적인 클래스들 간의 의존성을 배제시킬 수 있다.

 

HeadFirst Design Pattern - Bridge Pattern

위 클래스 다이어그램에서 보는 바와 같이 RemoteControl과 TV는 각각 인터페이스이다.

그리고 구체적인 의존 관계는 이 인터페이스들 간에만 존재하기 때문에 이렇게 브리지를 사용하면 두 계층 구조의 양쪽을 독립적으로 변경할 수 있다.

 

이를 코드로 구현해보면 아래와 같다.

.

 

장점

  • 인터페이스에 (의존하지 않도록) 구현을 분리한다.
  • 추상화와 구현은 독립적으로 확장 될 수 있다.
  • 구체적인 추상화 클래스에 대한 변경 사항은 클라이언트에 영향을주지 않는다.

단점

  • 복잡성이 증가한다.

언제 사용하는가?

  • 여러 플랫폼에서 실행해야하는 그래픽 및 윈도우 시스템에 유용하다.
  • 다른 방식으로 인터페이스와 구현을 변경해야 할 때 유용하다.

 



'책을 읽자 > Design Patterns' 카테고리의 다른 글

Proxy pattern  (0) 2021.08.18
Decorator Pattern  (0) 2021.08.18
Template Method Pattern  (0) 2021.08.02
Strategy Pattern  (0) 2021.08.02
Flyweight Pattern  (0) 2021.01.19

+ Recent posts