복합 패턴(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

범주란

  • 생성 패턴
    • 객체 인스턴스를 생성하는 패턴
    • 클라이언트와 그 클라이언트가 생성해야 하는 객체 인스턴스 사이의 연결을 끊어주는 패턴
  • 행동 패턴
    • 클래스와 객체들이 상호작용하는 방법과 역할을 분담하는 방법을 다루는 패턴
  • 구조 패턴
    • 클래스와 객체를 더 큰 구조로 만들 수 있게 구상을 사용하는 패턴

안티패턴

  • 어떤 문제의 나쁜 해결책에 이르는 길
  • 안티 패턴은 나중에 어떤 이유로 나쁜 해결책에 유혹이 되는 지 알 수 있다.