flagtags / OOP-study

0 stars 0 forks source link

12장. 복합패턴 #30

Open kkirico opened 3 months ago

kkirico commented 3 months ago

2개이상의 패턴을 결합해서 여러가지 문제의 일반적인 해결법을 제시해야한다.

오리 시뮬레이션 게임

Quackable 인터페이스

를 구현하는 구상 오리 클래스

거위를 Quackable 인터페이스에 맞추는 GooseAdapter를 활용한다.

오리가 우는 횟수를 세는 데코레이터 추가

class QuackCounter implements Quackable {
    duck: Quackable;
    static numberOfQuacks: number

    constructor(duck: Quackable){
        this.duck = duck
    }

    quack() {
        duck.quack();
        numberOfQuacks ++;
    }

 static getQuacks() {
     return numberOfQuacks;
 }
}

데코레이터로 감싼 오리를 생산하는 팩토리 만들기(팩토리 패턴)

*안정성과 투명상 모두 잘 고려해야합니다.

위와 같이 여러 패턴을 함께 사용할 수 있다.

복합 패턴의 왕

mvc 패턴

모델 - 뷰 - 컨트롤러

모델: 데이터, 상태, 애플리케이션 로직. 뷰와 컨트롤러에 관심없음

뷰: 모델을 표현하는 방법을 제공, 필요한 정보를 모델에서 직접 가져온다.

컨트롤러: 사용자로부터 입력을 받는다.

모델은 옵저버 패턴을 사용해서 상태가 바뀔 때 마다 뷰와 컨트롤러에 연락한다.

뷰와 컨트롤러는 전략패턴을 사용한다.

컨트롤러는 뷰의 행동에 해당한다.

뷰 내부적으로 컴포지트 패턴을 써서 윈도우, 버튼과 같은 다양한 구성요소를 관리한다.

j03y14 commented 3 months ago
interface Quackable {
    quack(): void;
}

class MallardDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class RedheadDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class DuckCall implements Quackable {
    quack() {
        console.log('꽥꽥');
    }
}

class RubberDuck implements Quackable {
    quack() {
        console.log('삑삑');
    }
}

class Goose {
    honk() {
        console.log('끽끽');
    }
}

class GooseAdapter implements Quackable {
    constructor(private goose: Goose) {}

    quack() {
        this.goose.honk();
    }
}

function simulate(duck: Quackable) {
    duck.quack();

}

simulate(new MallardDuck());
simulate(new RedheadDuck());
simulate(new DuckCall());
simulate(new RubberDuck());
simulate(new GooseAdapter(new Goose()));

만약 오리 클래스는 그대로 두면서 오리가 꽥꽥 소리낸 횟수를 세려면 어떤 패턴을 써야될까?

interface Quackable {
    quack(): void;
}

class MallardDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class RedheadDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class DuckCall implements Quackable {
    quack() {
        console.log('꽥꽥');
    }
}

class RubberDuck implements Quackable {
    quack() {
        console.log('삑삑');
    }
}

class Goose {
    honk() {
        console.log('끽끽');
    }
}

class GooseAdapter implements Quackable {
    constructor(private goose: Goose) {}

    quack() {
        this.goose.honk();
    }
}

function simulate(duck: Quackable) {
    duck.quack();

}

class QuackCounter implements Quackable {
    duck: Quackable;
    static numberOfQuacks = 0;

    constructor(duck: Quackable) {
        this.duck = duck;
    }

    quack() {
        this.duck.quack();
        QuackCounter.numberOfQuacks++;
    }
}

// simulate(new MallardDuck());
// simulate(new RedheadDuck());
// simulate(new DuckCall());
// simulate(new RubberDuck());
// simulate(new GooseAdapter(new Goose()));

const mallardDuck = new MallardDuck();
const redheadDuck = new RedheadDuck();
const duckCall = new DuckCall();
const rubberDuck = new RubberDuck();
const gooseAdapter = new GooseAdapter(new Goose());

const mallardDuckCounter = new QuackCounter(mallardDuck);
const redheadDuckCounter = new QuackCounter(redheadDuck);
const duckCallCounter = new QuackCounter(duckCall);
const rubberDuckCounter = new QuackCounter(rubberDuck);
const gooseAdapterCounter = new QuackCounter(gooseAdapter);

simulate(mallardDuckCounter);
simulate(redheadDuckCounter);
simulate(duckCallCounter);
simulate(rubberDuckCounter);
simulate(gooseAdapterCounter);

console.log(QuackCounter.numberOfQuacks);

어쩌다 counter를 붙이는걸 까먹는 경우가 있다. 오리 생성을 캡슐화 해서 빠지지 않도록 해준다.

interface Quackable {
    quack(): void;
}

class MallardDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class RedheadDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class DuckCall implements Quackable {
    quack() {
        console.log('꽥꽥');
    }
}

class RubberDuck implements Quackable {
    quack() {
        console.log('삑삑');
    }
}

class Goose {
    honk() {
        console.log('끽끽');
    }
}

class GooseAdapter implements Quackable {
    constructor(private goose: Goose) {}

    quack() {
        this.goose.honk();
    }
}

function simulate(duck: Quackable) {
    duck.quack();

}

class QuackCounter implements Quackable {
    duck: Quackable;
    static numberOfQuacks = 0;

    constructor(duck: Quackable) {
        this.duck = duck;
    }

    quack() {
        this.duck.quack();
        QuackCounter.numberOfQuacks++;
    }
}

abstract class AbstractDuckFactory {
    abstract createMallardDuck(): Quackable;
    abstract createRedheadDuck(): Quackable;
    abstract createDuckCall(): Quackable;
    abstract createRubberDuck(): Quackable;
}

class CountingDuckFactory extends AbstractDuckFactory {
    createDuckCall(): Quackable {
        return new QuackCounter(new DuckCall());
    }

    createMallardDuck(): Quackable {
        return new QuackCounter(new MallardDuck());
    }

    createRedheadDuck(): Quackable {
        return new QuackCounter(new RedheadDuck());
    }

    createRubberDuck(): Quackable {
        return new QuackCounter(new RubberDuck());
    }
}

const duckFactory = new CountingDuckFactory();

const mallardDuck = duckFactory.createMallardDuck();
const redheadDuck = duckFactory.createRedheadDuck();
const duckCall = duckFactory.createDuckCall();
const rubberDuck = duckFactory.createRubberDuck();

simulate(mallardDuck);
simulate(redheadDuck);
simulate(duckCall);
simulate(rubberDuck);
simulate(new GooseAdapter(new Goose()));

console.log(QuackCounter.numberOfQuacks);

오리들을 한 번에 관리하고 싶다.

interface Quackable {
    quack(): void;
}

class MallardDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class RedheadDuck implements Quackable {
    quack() {
        console.log('꽉꽉');
    }
}

class DuckCall implements Quackable {
    quack() {
        console.log('꽥꽥');
    }
}

class RubberDuck implements Quackable {
    quack() {
        console.log('삑삑');
    }
}

class Goose {
    honk() {
        console.log('끽끽');
    }
}

class GooseAdapter implements Quackable {
    constructor(private goose: Goose) {}

    quack() {
        this.goose.honk();
    }
}

function simulate(duck: Quackable) {
    duck.quack();

}

class QuackCounter implements Quackable {
    duck: Quackable;
    static numberOfQuacks = 0;

    constructor(duck: Quackable) {
        this.duck = duck;
    }

    quack() {
        this.duck.quack();
        QuackCounter.numberOfQuacks++;
    }
}

abstract class AbstractDuckFactory {
    abstract createMallardDuck(): Quackable;
    abstract createRedheadDuck(): Quackable;
    abstract createDuckCall(): Quackable;
    abstract createRubberDuck(): Quackable;
}

class CountingDuckFactory extends AbstractDuckFactory {
    createDuckCall(): Quackable {
        return new QuackCounter(new DuckCall());
    }

    createMallardDuck(): Quackable {
        return new QuackCounter(new MallardDuck());
    }

    createRedheadDuck(): Quackable {
        return new QuackCounter(new RedheadDuck());
    }

    createRubberDuck(): Quackable {
        return new QuackCounter(new RubberDuck());
    }

    createGooseAdapter(): Quackable {
        return new GooseAdapter(new Goose());
    }
}

class Flock implements Quackable {
    ducks: Quackable[] = [];

    add(duck: Quackable) {
        this.ducks.push(duck);
    }

    quack() {
        this.ducks.forEach(duck => duck.quack());
    }
}

const duckFactory = new CountingDuckFactory();

const mallardDuck = duckFactory.createMallardDuck();
const redheadDuck = duckFactory.createRedheadDuck();
const duckCall = duckFactory.createDuckCall();
const rubberDuck = duckFactory.createRubberDuck();

const flockOfDucks = new Flock();
flockOfDucks.add(mallardDuck);
flockOfDucks.add(redheadDuck);
flockOfDucks.add(duckCall);
flockOfDucks.add(rubberDuck);

simulate(flockOfDucks);
console.log(QuackCounter.numberOfQuacks);

오리를 관찰하고 싶음.

interface Observer {
    update(duck: QuackObservable): void;
}

interface QuackObservable {
    registerObserver(observer: Observer): void;
    notifyObservers(): void;
}

interface Quackable extends QuackObservable {
    quack(): void;
}

class Observable implements QuackObservable {
    observers: Observer[] = [];
    quackObservable: QuackObservable;

    constructor(quackObservable: QuackObservable) {
        this.quackObservable = quackObservable;
    }

    registerObserver(observer: Observer) {
        this.observers.push(observer);
    }

    notifyObservers() {
        this.observers.forEach(observer => observer.update(this.quackObservable));
    }
}

class MallardDuck implements Quackable {
    observable: Observable;

    constructor() {
        this.observable = new Observable(this);
    }

    quack() {
        console.log('꽉꽉');
        this.notifyObservers();
    }

    registerObserver(observer: Observer) {
        this.observable.registerObserver(observer);
    }

    notifyObservers() {
        this.observable.notifyObservers();
    }
}

class RedheadDuck implements Quackable {
    observable: Observable;

    constructor() {
        this.observable = new Observable(this);
    }

    quack() {
        console.log('꽉꽉');
        this.notifyObservers();
    }

    registerObserver(observer: Observer) {
        this.observable.registerObserver(observer);
    }

    notifyObservers() {
        this.observable.notifyObservers();
    }
}

class DuckCall implements Quackable {
    observable: Observable;

    constructor() {
        this.observable = new Observable(this);
    }

    quack() {
        console.log('꽥꽥');
        this.notifyObservers();
    }

    registerObserver(observer: Observer) {
        this.observable.registerObserver(observer);
    }

    notifyObservers() {
        this.observable.notifyObservers();
    }
}

class RubberDuck implements Quackable {
    observalbe: Observable;

    constructor() {
        this.observalbe = new Observable(this);
    }

    quack() {
        console.log('삑삑');
    }

    registerObserver(observer: Observer) {
        this.observalbe.registerObserver(observer);
    }

    notifyObservers() {
        this.observalbe.notifyObservers();
    }
}

class Goose {
    honk() {
        console.log('끽끽');
    }
}

class GooseAdapter implements Quackable {
    observalbe: Observable;

    constructor(private goose: Goose) {
        this.observalbe = new Observable(this);
    }

    quack() {
        this.goose.honk();
        this.notifyObservers();
    }

    registerObserver(observer: Observer) {
        this.observalbe.registerObserver(observer);
    }

    notifyObservers() {
        this.observalbe.notifyObservers();
    }
}

function simulate(duck: Quackable) {
    duck.quack();

}

class QuackCounter implements Quackable {
    duck: Quackable;
    static numberOfQuacks = 0;

    constructor(duck: Quackable) {
        this.duck = duck;
    }

    quack() {
        this.duck.quack();
        QuackCounter.numberOfQuacks++;
    }

    registerObserver(observer: Observer) {
        this.duck.registerObserver(observer);
    }

    notifyObservers() {
        this.duck.notifyObservers();
    }
}

abstract class AbstractDuckFactory {
    abstract createMallardDuck(): Quackable;
    abstract createRedheadDuck(): Quackable;
    abstract createDuckCall(): Quackable;
    abstract createRubberDuck(): Quackable;
}

class CountingDuckFactory extends AbstractDuckFactory {
    createDuckCall(): Quackable {
        return new QuackCounter(new DuckCall());
    }

    createMallardDuck(): Quackable {
        return new QuackCounter(new MallardDuck());
    }

    createRedheadDuck(): Quackable {
        return new QuackCounter(new RedheadDuck());
    }

    createRubberDuck(): Quackable {
        return new QuackCounter(new RubberDuck());
    }

    createGooseAdapter(): Quackable {
        return new GooseAdapter(new Goose());
    }
}

class Flock implements Quackable {
    ducks: Quackable[] = [];

    add(duck: Quackable) {
        this.ducks.push(duck);
    }

    quack() {
        this.ducks.forEach(duck => duck.quack());
    }

    registerObserver(observer: Observer) {
        this.ducks.forEach(duck => duck.registerObserver(observer));
    }

    notifyObservers() {
        this.ducks.forEach(duck => duck.notifyObservers());
    }
}

class Quackologist implements Observer {
    update(duck: QuackObservable) {
        console.log('Quackologist: ', duck);
    }

}

const duckFactory = new CountingDuckFactory();

const mallardDuck = duckFactory.createMallardDuck();
const redheadDuck = duckFactory.createRedheadDuck();
const duckCall = duckFactory.createDuckCall();
const rubberDuck = duckFactory.createRubberDuck();

const flockOfDucks = new Flock();
flockOfDucks.add(mallardDuck);
flockOfDucks.add(redheadDuck);
flockOfDucks.add(duckCall);
flockOfDucks.add(rubberDuck);

const quackologist = new Quackologist();
flockOfDucks.registerObserver(quackologist);

simulate(flockOfDucks);
console.log(QuackCounter.numberOfQuacks);