구조 패턴은 객체의
구성
과 관련되며, Entity가 어떻게 사용될 수 있는지에 관한 것입니다.
어댑터 패턴을 사용하면 호환되지 않은 객체를 다른 클래스와 호환되도록 할 수 있다.
class Hunter {
hunt(lion: Lion): void {
lion.roar();
}
}
// 이제 Hunter클래스는 Lion객체만 사냥할 수 있다.
interface Lion {
roar(): void;
}
class ALion implements Lion {
roar(): void {
console.log("사자 죽어요");
}
}
// 여기서, Hunter가 다른 동물도 사냥하고 싶다면 어떻게 해야 할까?
class Wolf {
bark() {
console.log("늑대 죽네");
}
}
class WolfAdapter implements Lion {
protected wolf: Wolf;
constructor(wolf: Wolf) {
this.wolf = wolf;
}
roar() {
this.wolf.bark();
}
}
// Usage
const hunter = new Hunter();
const lion = new ALion();
const wolf = new Wolf();
const wolfAdapter = new WolfAdapter(wolf);
hunter.hunt(lion);
hunter.hunt(wolfAdapter);
Lion
객체만 사냥할 수 있다.Wolf
를 사냥할 수 있도록 Lion
객체를 모방한 WolfAdapter
를 통해, 사냥꾼을 변경하지 않고도 wolf를 사냥할 수 있게 만들 수 있다./**
* @desc 브릿지 패턴
*
* 구현체에서 속성을 분리한다.
*
* 여기서는 [테마]에 따른 웹사이트 색상을 예시로 들어볼 것이다.
*
* 예를 들어, 페이지 A,B,C가 있고, 테마 1,2,3이 있다면 어떻게 할 것인가
* 무식하게 한다면, A(1,2,3), B(1,2,3), C(1,2,3)을 모두 구현할 것이다.
*
* 여기서 브릿지 패턴을 적용한다면, 테마 1,2,3을 별도 분리하여, 활용할 때 가져다 쓰게 할 수 있다.
*/
// WebPage속성에서 Theme은 별도의 Type으로 빼놓는다.
type WebPage = {
theme: Theme;
getContent(): string;
};
class About implements WebPage {
theme: Theme;
constructor(theme: Theme) {
this.theme = theme;
}
getContent(): string {
return "About Page in " + this.theme.getColor();
}
}
type Theme = {
getColor(): string;
};
class DarkTheme implements Theme {
getColor(): string {
return "Dark";
}
}
class WhiteTheme implements Theme {
getColor(): string {
return "White";
}
}
// Usage
const dark = new DarkTheme();
const white = new WhiteTheme();
const aboutPage_Black = new About(dark);
const aboutPage_White = new About(white);
console.log(aboutPage_Black.getContent());
console.log(aboutPage_White.getContent());
Property를 별도분리하여, 활용할 때 조립하는 방식
추상화<->구현체를 분리
복합체 패턴, 복합객체와 단일객체를 동일한 컴포넌트로 취급하여, 클라이언트에게 이 둘을 구분하지 않고 동일한 인터페이스를 사용하도록 하는 구조 패턴 (...?, 명확히 이해가 되지 않는다)
Age
를 가진다 -> 20대가 몇명이지? 라고 구해볼 수 있는 것같다.객체를 Wrapping함으로써, 동적으로 객체를 변경할 수 있게 한다.
type Coffee = {
cost: number;
};
class BasicCoffee implements Coffee {
cost = 0;
}
class Americano implements Coffee {
cost: number;
constructor(coffee: Coffee) {
this.cost = 20;
}
}
class Latte implements Coffee {
cost: number;
constructor(coffee: Coffee) {
this.cost = 40;
}
}
let coffee = new BasicCoffee();
coffee = new Americano(coffee);
console.log(coffee.cost);
coffee = new Latte(coffee);
console.log(coffee.cost);
속성이 동일한 인스턴스에 한해서, 객체의 변경을 객체를 Wrap하여 바꾼다.
마치
물위의 백조
와 같다
class Computer {
전원공급() {
console.log("전원!");
}
소리내기() {
console.log("소리!");
}
화면켜기() {
console.log("화면!");
}
준비완료() {
console.log("준비!");
}
BIOS세팅() {
console.log("BIOS!");
}
OS() {
console.log("OS!");
}
}
class ComputerFacade {
computer: Computer;
constructor(computer: Computer) {
this.computer = computer;
}
turnOn() {
this.computer.BIOS세팅();
this.computer.OS();
this.computer.소리내기();
this.computer.전원공급();
this.computer.화면켜기();
this.computer.준비완료();
}
}
const computer = new Computer();
const user = new ComputerFacade(computer);
user.turnOn();
공유!
유사한 객체를 공유하여 메모리, 비용을 최소화
class Coffee {
진함 = 0;
}
class CoffeeMaker {
makedCoffee: Map<string, Coffee> = new Map();
make(key: string): Coffee {
// 만들어진 커피가 없다면, 커피를 만들어둔다.
if (!this.makedCoffee.has(key)) {
this.makedCoffee.set(key, new Coffee());
}
return this.makedCoffee.get(key)!;
}
}
const coffeeMaker = new CoffeeMaker();
const coffeeList = [
coffeeMaker.make("아메리카노"),
coffeeMaker.make("라떼"),
coffeeMaker.make("아메리카노"),
coffeeMaker.make("아메리카노"),
];
// 하지만 객체공유 이기에, 다른 객체까지 영향이 간다.
coffeeList[0].진함 = 2;
console.log(coffeeList);
⚠️ 비용 최소화 관점에서 공유하는 것은 좋으나, 객체 변경에 따라 다른 객체까지 변경되므로 주의해서 사용해야 할 것 같다.
하나의 클래스가 다른 클래스의 기능을 나타내는 것
👍 이런 경우에 활용할 수 있다.
- 특정 객체의 조건 확인
- 추가기능 제공
type Door = {
open(): void;
close(): void;
};
class HomeDoor implements Door {
open() {
console.log("Home Door Open");
}
close() {
console.log("Home Door Closed");
}
}
class SecureDoor implements Door {
isLock = true;
door: Door;
constructor(door: Door) {
this.door = door;
}
unLock(password: string) {
if (password == "secret") this.isLock = false;
}
open() {
// 프록시 패턴 : 조건 확인
if (this.isLock) {
console.log("Door is Locked");
} else {
this.door.open();
}
}
close() {
this.door.close();
}
}
// 집에 문을 달았다.
const door = new HomeDoor();
door.open();
door.close();
// 하지만, 우리집은 누구나 열수 있기에, 도어락을 설치했다.
const secureDoor = new SecureDoor(door);
secureDoor.open();
// 이제 우리집은 암호를 풀어야만 열 수 있다.
secureDoor.unLock("secret");
secureDoor.open();