복합 패턴(12장)
기본적인 패턴 적용 과정
전략패턴을 사용해서 다형성 있게 오리를 구현
인터페이스
public interface Quackable {
public void quack();
}구현 오리들
public class MallardDuck implements Quackable {
public void quack() {
// 꽥꽥
}
}
public class RedheadDuck implements Quackable {
public void quack() {
// 꽉꽥꽥
}
}어댑터 패턴을 사용해서 거위도 호환되도록 확장
타겟 인터페이스를 구현해야 한다.
public class GooseAdapter implements Quackable {
Goose goose;
// 생성자를 통해서 변환을 시켜 줄 Goose 객체를 인자로 받는다.
public GooseAdpater(Goose goose) {
this.goose = goose;
}
public void quack(){
// 클라이언트가 요청한 인터페이스에 맞게 변환해준다
goose.honk();
}
}호출 하는 부분
Quackable gooseDuck = new GooseAdpater(new Goose());데코레이터 패턴을 사용해서 오리 호출 횟수를 알아본다.
오리의 울음 횟수를 알기 위해서 아래와 같은 클래스를 만든다.
public class QuackCounter implements Quackable {
Quckable duck;
static int numberOfQuacks; // 모든 객체에서 발생한 횟수를 기록해야 하므로 정적 변수를 사용해서 선언한다.
public QuackCounter (Quackable duck) {
this.duck = duck;
}
public void quack() {
duck.quack();
numberOfQuacks+=1;
}
public static int getQuacks() {
return numberOfQuacks;
}
}적용 하고 테스트 하는 코드
void simulate() {
// 오리로서 호출되는 경우만 QuackCounter를 붙여준다.
Quackable mallardDuck = new QuackCounter(new MallardDuck());
Quackable redheadDuck = new QuackCounter(new RedheadDuck());
Quackable duckCall = new QuackCounter(new DuckCall());
Quackable rubberDuck = new QuackCounter(new RubberDuck());
Quackable gooseDuck = new GooseAdapter(new Goose());
simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
simulate(gooseDuck);
System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
}
void simulate(Quackable duck) {
duck.quack();
}오리 생성 부분을 캡슐화 해보자
추상 팩토리 패턴을 사용해서 여러 종류의 오리를 생성
public abstract class AbstractDuckFactory {
public abstract Quackable createMallardDuck();
... // 다양한 오리들을 만든다.
public abstract Quackabe createRubberDuck();
}데코레이터가 없는 오리의 생성
public class DuckFactory extends AbstractDuckFactory {
public Quackable createMallardDuck() {
return new MallardDuck();
}
...
public Quackable createRubberDuck() {
return new RubberDuck();
}
}Counting을 적용할 오리의 생성
public class CountingDuckFacktory extends AbstractDuckFactory {
public Quackable createMallardDuck() {
return new QuackCounter(new MallardDuck());
}
...
public Quackable createRubberDuck() {
return new QuackCounter(new RubberDuck());
}
}추상 팩토리를 적용 후 실행 코드
public static void main(String[] args){
DuckSimulator simulator = new DuckSimulator();
AbstractDuckFactory duckFactory = new CountingDuckFacktory();
simulator.simulate(duckFactory);
// 책의 예제에는 실행문이 simulate 안에 있지만
// 내 생각에는 여기에 있어야 맞을 것 같아서 이동
System.out.println("The ducks quacked " +
QuackCounter.getQuacks() +
" times");
}
// 어떤 오리가 넘어 올지는 위에서 정한다.
void simulate(AbstractDuckFactory duckFactory) {
Quackable mallardDuck = duckFactory.createMallardDuck();
...
Quackable rubberDuck = duckFactory.createRubberDuck();
Quackable gooseDuck = new GooseAdapter(new Goose());
simulate(mallardDuck);
...
simulate(gooseDuck);
}
거위 생성하는 추상 클래스
public abstract class AbstractDuckFactory {
public abstract Quackable createMallardDuck();
... // 다양한 오리들을 만든다.
// 거위 만들기 => 간단한 메소드 추가 해주면 된다.
public abstract Quackable createGooseDuck();
}단일 개체로 돌아다니는 오리를 컬렉션으로 보관
// 복합 객체와 잎 원소는 같은 인터페이스를 구현해야 한다.
public class Flock implements Quackable {
List<Quackable> quackers = new ArrayList<Quackable>();
public void add(Quackable quacker) {
quackers.add(quacker);
}
public void quack() {
// 반복자 패턴도 추가
Iterator<Quackable> iterator = quackders.iterator();
while(iterator.hasNext()) {
Quackable quacker = iterator.next();
quacker.quack();
}
}
}개별 오리의 행동을 파악하기 위한 옵저버 패턴
오리들의 관찰하기 위한 옵저버 인터페이스를 추가한다.
public interface QuackObservable{
// 옵저버를 등록하는 메소드
public void registerObserver(Observer observer);
// 옵저버에게 연락을 돌리는 메소드
public void notifyObserver();
}인터페이스를 구현한 클래스
public class Observable implements QuackObservable {
private List<Observer> observers = new ArrayList<>();
private QuackObservable duck;
public Observable(QuackObservable duck) {
this.duck = duck;
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers() {
Iterator iterator = observers.iterator();
while(iterator.hasNext()) {
Observer observer = iterator.next();
observer.update(duck);
}
}
}자바 버전 9 이후에는
java.util.Observer가 deprecate 되어서 인터페이스를 직접 구현
하거나 Flow API를 사용해야 한다.
interface Observer {
void update(QuackObservable duck);
}
public class Quackologist implements observer {
public void update(QuackObservable duck) {
// 누가 오리 소리를 내었는가
}
}Duck 객체에서 Observer에서 넘어온 알림을 처리할 수 있는 메소드를 구현해야 한다.
public class MallardDuck implements Quackable {
Observable observable
public MallardDuck() {
observable = new Observable(this);
}
public void quack() {
// 꽥괙
notifyObservers(); // quack 메소드가 호출되었음을 알려줘야 한다.
}
// QuackObservable에서 정의된 메소드로 바로 보조 객체로 전달해준다.
public void registerObserver(Observer observer) {
observable.registerObserver(observer);
}
// QuackObservable에서 정의된 메소드로 바로 보조 객체로 전달해준다.
public void notifyObservers() {
observable.notifyObservers();
}
}실행할 쓰레드에서 입력받은 오리들에 대해서 알림을 등록해준다.
// 오리 군체 생성
Flock flockOfMallards = new Flock();
... // 오리들을 군체에 넣어 준다.
// 1안 Observer 생성
Quackologist quackologist = new Quackologist();
// 2안 이렇게 생성을 해도 된다.
Observer quackologist = new Quackologist();
// 오리들에 Observer을 넣어준다.
flockOfDucks.registerObserver(quackologist);적용 과정 정리
- 어댑터 패턴을 사용해서 오리(Quackable) 인터페이스에 맞춰서 거위 클래스를 변환해준다.
- 데코레이터 패턴을 사용해서
quack()이 호출된 회수를 확인할 수 있게 한다. - 추상 팩토리 패턴을 사용해서 원하는 기능의 객체와 기본 오리 객체의 생성을 분리한다.
- 오리 및 거위와 같은 Quackable 객체들을 단일로 관리하기가 어려워졌으므로 무리 단위로 관리하기 위해서 컴포즈 및 반복자 패턴을 적용하였다.
- 오리가 울 때 마다 알림을 받고 싶어서 옵저버 패턴을 적용하였다.
MVC 패턴에서 디자인 패턴 적용 알아보기
MVC의 기본
- 모델
- 모든 데이터의 상태와 애플리케이션 로직이 들어있다.
- 뷰와 컨트롤러에서 모델의 상태를 조작하거나 갖고 갈때 필요한 인터페이스를 제공한다.
- 컨트롤러
- 사용자로부터 입력을 받으며 입력받은 내용이 모델에게 어떤 의미가 있는지 파악한다.
- 뷰
- 모델을 표현하는 방법을 제공한다.
- 일반적으로 화면에 표시할 때 필요한 상태 및 데이터는 모델에서 직접 갖고 온다.
여기서 적용되는 디자인 패턴
전략패턴
- 뷰와 컨트롤러는 전략 패턴으로 되어있다.
- 뷰는 애플리케이션의 겉모습에만 신경을 쓰고, 인터페이스의 행동 결정은 모두 컨트롤러에게 맡긴다.
- 이를 사용하면 뷰를 모델로부터 분리하는데 도움이 된다.
컴포지트 패턴
- 디스플레이는 여러 단계로 겹쳐있는 윈도우, 패널, 버튼 등등으로 구성되어 있다.
옵저버 패턴
- 모델은 상태가 변경이 되었을 때 옵저버 패턴을 사용해서 연관된 객체들에게 전달할 수 있다.
실전 디자인 패턴(13장)
디자인 패턴에서의 패턴의 정의
- 패턴(Pattern)은 특정 컨텍스트 내에서 주어진 문제의 해결책이다.
구성요소
- 컨텍스트
- 패턴이 적용되는 상황
- 반복적으로 일어날 수 있는 상황
- 문제
- 컨텍스트 내에서 이뤄야 하는 목표
- 해결책
- 제약조건 속에서 목표를 이룰 수 있는 일반적인 디자인
지금까지 했던 디자인 패턴 정리
| 범주 | 패턴 이름 | 설명 | 틀린 유무 |
|---|---|---|---|
| 구조 | 데코레이터 (Decorator) | 객체를 감싸서 새로운 행동을 제공한다. | |
| 퍼사드 (Facade) | 일련의 클래스에 간단한 인터페이스를 제공한다. | ||
| 프록시 (Proxy) | 객체를 감싸서 그 객체로의 접근을 제어한다. | ||
| 어댑터 (Adapter) | 객체를 감싸서 다른 인터페이스를 제공한다. | O | |
| 컴포지트 (Composite) | 클라이언트에서 객체 컬렉션과 개별 객체를 똑같이 다룰 수 있게 한다. | ||
| 행동 | 상태 (State) | 상태를 기반으로 하는 행동을 캡슐화한 다음 객체가 내부 상태 변화에 따라 행동을 변경할 수 있도록 한다. | |
| 반복자 (Iterator) | 컬렉션이 어떤 식으로 구현되었는지 드러내지 않으면서도 컬렉션 내에 있는 모든 객체를 대상으로 반복 작업을 처리할 수 있도록 한다. | O | |
| 전략 (Strategy) | 교환 가능한 행동을 캡슐화 하고 위임으로 어떤 행동을 사용할지 결정한다. (클라이언트가 런타임에 적절한 전략을 선택하여 사용할 수 있도록 한다) | O | |
| 옵저버 (Observer) | 상태가 변경 되면 다른 객체들에게 연락을 돌릴 수 있게 한다. | ||
| 템플릿 메소드 (Template Method) | 알고리즘의 전체 구조는 유지하면서 특정 단계를 서브클래스에서 재정의합니다 | O | |
| 커맨드 패턴 (Command Pattern) | 요청을 객체로 감싼다. (요청 자체를 객체로 캡슐화 해서 요청의 실행, 취소 등을 처리한다.) | ||
| 생성 | 팩토리 메소드 (Factory Method) | 서브클래스에서 생성할 구상 클래스를 결정한다. 객체 생성 로직을 서브클래스로 위임하여 클라이언트 코드가 구체적 클래스에 의존하지 않게 합니다. | O |
| 싱글톤 (Singleton) | 딱 한 개의 객체만 생성되도록 한다. | ||
| 추상 팩토리 (Abstract Factory) | 클라이언트에서 구상 클래스를 지정하지 않으면서도 객체군을 생성할 수 있게 해준다. 똑같은 메소드를 호출해도 객체에 따라 다른 객체가 생성된다. | O |
범주란
- 생성 패턴
- 객체 인스턴스를 생성하는 패턴
- 클라이언트와 그 클라이언트가 생성해야 하는 객체 인스턴스 사이의 연결을 끊어주는 패턴
- 행동 패턴
- 클래스와 객체들이 상호작용하는 방법과 역할을 분담하는 방법을 다루는 패턴
- 구조 패턴
- 클래스와 객체를 더 큰 구조로 만들 수 있게 구상을 사용하는 패턴
안티패턴
- 어떤 문제의 나쁜 해결책에 이르는 길
- 안티 패턴은 나중에 어떤 이유로 나쁜 해결책에 유혹이 되는 지 알 수 있다.