Design principle 5-6

  • Strive for loosely coupled designs between objects that interact.
    • 서로 상호작용하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
    • 옵저버에서 나온다.
  • Classes should be open for extension, but closed for modification
    • 이것은 Open-Close Principle(OCP)라고 불린다.
    • 클래스는 확장에 대해서는 열려있어야 하지만, 코드 수정에 대해서는 닫혀 있어야 한다.
    • 데코레이터에서 나온다.

Observer (behavioral)

"The observer pattern defines a one-to-many dependency between objects so that when one object changes state, the other objects are notified/updated
automatically."

  • 옵저버 패턴에서는 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다 의존성의 정의합니다.
  • 이러한 패턴은 event-based system에서 자주 볼 수 있다.

Observer example

WeatherObserver, PersonObserver, SwingObserver 등을 보았다.

Loose coupling

느슨한 결합은 다음과 같은 장점이 있다.

  • 객체들 사이의 의존성을 줄여서 변화에 유연한 시스템을 만들 수 있다.

observer pattern에서의 subject와 observer는 loose coupled이다.

  • Subject는 ConcreteObserver가 observer interface를 implement했다는 사실 밖에 모른다.
  • 새로운 observer를 언제든 추가할 수 있다.
  • 새로운 observer를 추가하기 위한 Subject의 변경이 필요 없다.
  • Subject 또는 Observer의 코드를 변경해도 별다른 문제가 없다.

Summary

  • 옵저버 패턴은 객체 간의 일대다 관계를 정의한다.
  • Subject는 공통 인터페이스를 활용하여 Observer를 업데이트한다.
  • Observer와 Subject는 느슨하게 결합되어 있다. 그들은 인터페이스 외에는 서로에 대해 아는 것이 없다.
  • Observer는 Java API를 비롯한 여러 라이브러리/프레임워크에서 찾을 수 있다.

Command (behavioral)

"The Command pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations."

  • 커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할 수 있으며, 매개변수를 써서 여러가지 다른 요구 사항을 집어넣을 수도 있습니다. 또한 요청 내역을 큐에 조정하거나 로그로 기록할 수도 있으며, 작업 취소도 지원 가능합니다.

Client에서는 invoker, reciever, command를 정의하고 할당한다.

Command는 action을 패키징하는 execute를 정의한다. undo를 정의하기도 한다. 그래서 위와 같은 형태를 지닌다.

Invoker는 setCommand 함수가 있고, buttonWasPressed와 같은 함수가 호출되면 execute를 실행하기만 한다. command에 대한 정보는 client가 설정해준다.

Reciever는 그냥 할일 하는 객체이다.

example

  • Remote control project
    • 집에서 사용자, 커맨드(Command;LightOnCommand...), 리모컨(Invoker), 가전제품(Receiver; Light, GarageDoor)으로 비유
  • Diner example
    • 식당에서 고객, 주문서(Commands), 종업원(Invoker), 요리사(Receiver)로 비유
  • StockCommand
  • Logging commands

Command use-cases

  • GUI button, menu items에서 사용된다.
  • (취소 가능한) 게임의 캐릭터의 액션에서 사용된다.
  • 매크로 기록에 사용된다.
  • 네트워크에서 사용된다.
  • Job queue 쓰레드에서 사용된다.
  • 데이터 트랜잭션에서 사용된다.

The NoCommand – a null object

아무것도 하지 않는 command를 말한다. 이는 NullPointerException을 막아준다.

Invoker를 초기화 할 때 할당하는 데 사용된다.

Supporting Undo

Command Interface에 undo() 메서드를 추가한다.

undo 메서드를 구현하는 쪽에서는 execute 메서드와 반대되는 일을 한다.

마지막에 한 일을 저장해놓을 수 있는 instance variable을 정의한다.

Macro Command

여러개의 커맨드를 하나로 저장해놓은 커맨드이다. 이는 확장성에서 유용하다.

Summary

  • 커맨드 패턴은 request를 encapsulate한 객체로 만든다.
    • Request는 Receiver와 action의 집합으로 이루어진다.
  • Invoker는 Command 개체의 execute() 메서드를 호출하여 명령 개체를 활성화하고, 이 메서드는 Receiver에서 작업을 호출합니다.
  • Invoker는 런타임에도 Command을 사용하여 매개 변수를 지정할 수 있습니다.
    • 실행 중에 슬롯의 명령을 바꾸는 예시가 있었다.
  • Command에는 이전 execute() 호출을 취소하는 undo method가 있을 수 있습니다.
  • Macro Command을 사용하면 여러 Command를 실행할 수 있습니다. 실행 취소도 지원할 수 있습니다.
  • Command 객체는 또한 "스마트"할 수 있습니다. 즉, Receiver를 호출하는 대신 요청을 스스로 구현할 수 있습니다.

Decorator (structural)

"The Decorator pattern attaches additional responsibilities to an object dynamically(at runtime + without changing original object). Decorators provide a flexible alternative to subclassing for extending functionality."

  • 데코레이터 패턴에서는 객체에 추가적인 요건을 동적으로 첨가한다. 데코레이터는 서브클래스를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.
    • 데코레이터는 같은 상위 클래스를 가진다.
    • 하나 이상의 데코레이터를 사용할 수 있다.
    • Java I/O에서 사용된다. 게임의 장비, 모양 같은 곳에서 사용될 수 있다.

example

  • Starbuzz Coffee
    • 커피 팔아야되는데 condiments(휘핑이랑 샷 추가 같은 것)에 따라서 가격을 추가해야한다.
      • 그냥 무작정 클래스를 막 만들면 클래스 개수가 너무 많아진다. 그리고 condiments 추가에 따른 수정 또한 힘들어질 것이다.
      • 수퍼 클래스에 인스턴스 변수를 추가하면 상속이 불필요한 경우(Tea와 같은 경우)에도 상속을 받는다. 더블 샷 추가 같은 것을 생각하면 어지러워진다.

Summary

  • 상속을 확장에 사용할 수 있지만 반드시 유연성을 확보하는 최선의 방법은 아닙니다.
  • 기존 코드를 수정하지 않고 동작을 확장할 수 있도록 해야 합니다.
  • Composition 및 delegation을 사용하여 런타임에 새로운 동작을 추가할 수 있습니다.
  • Decorator는 동작을 확장하기 위한 하위 분류에 대한 대안을 제공합니다.
  • Decorator는 특정한 구성을 wrap하는 데 사용되는 일련의 Decoration 클래스를 포함합니다.
  • Decorator 클래스는 랩핑된 구성요소와 동일한 슈퍼타입(슈퍼클래스 또는 인터페이스)에서 가져옵니다.
  • Decorator는 메서드가 구성 요소에 호출되기 전/후에 새로운 기능을 추가하여 구성 요소의 동작을 변경합니다.
  • 구성품을 원하는 수의 Decorator로 포장할 수 있습니다.
  • Decorator는 일반적으로 구성요소의 클라이언트에 투명(transparent)합니다.
  • 데코레이터는 디자인에서 작은 물체를 많이 만들 수 있습니다. 이것은 더 복잡한 클래스 계층을 의미합니다.

Profile

한창헌

https://github.com/HanChangHun