[이노캠] 디자인 패턴 - 1. 생성패턴

Chanyoung Park·2024년 6월 8일
0

들어가며...

  • 이노캠 필수과정 1일차, 기술매니저님이 Next, TS에 관해 여쭤보신 뒤 디자인 패턴에 대해 공부를 제시해주셨다.
  • 안그래도, 사이드프로젝트를 진행하면서 왠지모르게 마음에 들지 않는 코드의 구조와 더 좋은 방법이 없나.. 라는 생각이 들던 참이었다.

디자인패턴


디자인 패턴이란

디자인 패턴은 반복되는 문제에 대한 해결책이며, 특정 문제를 해결하는 방법에 대한 지침이다.

디자인 패턴은 문제의 해결책이며, 문제를 찾는 해결책은 아니다. (...?)

디자인 패턴 종류

  1. 생성 (Creational)
  2. 구조 (Structural)
  3. 행동 (Behavioral)

생성 (Creational) 패턴

객체 또는 관련된 객체 그룹을 인스턴스화하는 방법에 초점을 맞춘다.

1. 심플 팩토리

심플 팩토리는 클라이언트에게 인스턴스화 로직을 노출하지 않고 클라이언트용 인스턴스를 생성한다.

interface Door {
  getWidth(): number;
  getHeight(): number;
}

class WoodenDoor implements Door {
  protected width: number;
  protected height: number;

  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }

  getWidth(): number {
    return this.width;
  }

  getHeight(): number {
    return this.height;
  }
}

class DoorFactory {
  public static makeDoor(width: number, height: number): Door {
    return new WoodenDoor(width, height);
  }
}

// 위와 같은 팩토리가 있을 때, 클라이언트에서 인스턴스를 생성하는 법은 아래와 같이 하면 된다.

const door = DoorFatory.makeDoor(100,100);

👍 객체를 생성할 때, 일부 로직을 포함해야 할 경우 위와 같은 팩토리 패턴을 사용하면 좋다.
예를 들어, makeDoor에서 width와 height로 계산 로직이 들어갈 수도 있다.

2. 팩토리 메소드

자식 클래스에게 인스턴스화 로직을 위임하는 방식이다.

  • 이게 무슨 소리인고하니, 예를 들어

    • 인사담당자라고 한다면,
    • 개발관련 면접관이 있을 것이고,
    • 소통관련 면접관이 있을 것이다.
  • 이처럼, 여러종류의 면접관이 많을 것인데, 이것은 면접자에 따라 달라진다.
    (클라이언트에서 사용될 때(런타임)에 따라 특정 클래스에서 필요한 인스턴스를 받아야 한다.

  • (팩토리 메소드, 해당 글을 보고 이해했다)

  • 면접관 Class

    • 개발 면접관
    • 인사 면접관
    • 소통 면접관
    • 인성 면접관
      등등 면접자에 따라 면접관 종류가 달라질 것이다.
  • 이를 위해서, 면접관 Class에서 종류에 따라 필요한 면접관을 할당(인스턴스화)하려면 모든 조건에 따라 분기처리를 해주어야 한다. (이는 개방/폐쇄 원칙에 좋지 않다)

👍 필요한 인스턴스가 런타임에 동적으로 결정될 때 유용하다.

3. 추상 팩토리

구체적인 내용을 지정하지 않고, 관련된 것들을 그룹화한다.

interface Door {
	getDesc() : void; 
}

class ADoor implements Door {
 	getDesc() {
  		console.log("ADoor");    
    }
}

class BDoor implements Door {
 	getDesc() {
  		console.log("BDoor");    
    }
}

----

interface DoorMan {
 	fix() : void;
}

class ADoorMan implements DoorMan {
 	fix() {
		console.log('난 15달러');
    }	
}

class BDoorMan implements DoorMan {
 	fix() {
		console.log('난 4달라');
    }	
}

---
  
interface DoorFactory {
 	getDesc(): Door;
  	fix(): DoorMan;
}


class ADoorFactory implements DoorFactory {
 	getDesc(): Door {
     	 return new ADoor();
    }
  
  	fix(): DoorMan {
     	return new ADoorMan(); 
    }
}

class BDoorFactory implements DoorFactory {
 	getDesc(): Door {
     	 return new BDoor();
    }
  
  	fix(): DoorMan {
     	return new BDoorMan(); 
    }
}

이처럼, Factory안에서는 구현내용없이, 관련된 내용을 그룹화하고 return하는 내용밖에 없다.

👍 상호 의존성이 있고, 복잡한 생성로직이 있는 경우 유용하다.

4. 생성자 (Builder)

  • contructor의 오염을 방지하면서, 다양한 타입의 객체를 생성하기에 유용하다.
  • 서브웨이에서 주문하는 방법과 비슷하다.
    1. 빵은 미디움으로 주시고요.
    2. 야채는 빼주시고,
    3. 치킨, 계란 넣어주시고,
    4. 15cm로 하고,
    5. 결제하고, 빵을 받는다.
// 생성자를 이용한 방식
class Burger {
  protected size: number;
  protected cheese: boolean = false;
  protected pepperoni: boolean = false;
  protected lettuce: boolean = false;
  protected tomato: boolean = false;

  constructor(builder: BurgerBuilder) {
    this.size = builder.size;
    this.cheese = builder.cheese;
    this.pepperoni = builder.pepperoni;
    this.lettuce = builder.lettuce;
    this.tomato = builder.tomato;
  }
}

class BurgerBuilder {
  public size: number;
  public cheese: boolean = false;
  public pepperoni: boolean = false;
  public lettuce: boolean = false;
  public tomato: boolean = false;

  constructor(size: number) {
    this.size = size;
  }

  addPepperoni(): BurgerBuilder {
    this.pepperoni = true;
    return this;
  }

  addLettuce(): BurgerBuilder {
    this.lettuce = true;
    return this;
  }

  addCheese(): BurgerBuilder {
    this.cheese = true;
    return this;
  }

  addTomato(): BurgerBuilder {
    this.tomato = true;
    return this;
  }

  build(): Burger {
    return new Burger(this);
  }
}

// 활용
const burger = new BurgerBuilder(14)
  .addPepperoni()
  .addLettuce()
  .addTomato()
  .build();

👍 객체의 생성이 여러단계에 걸쳐야 할 때 유용하다.

5. 프로토타입

객체를 복사하여, 새로운 객체를 생성한다.

  • Object.create로 간단하게 수행가능하다.
const original: Sheep = new Sheep("A");
// original
//   name : "A"
//   category : "sheep"

const clone: Sheep = Object.create(original);
clone.setName("B")
// clone
//   name : "B"
//   category : "sheep"

👍 기존 객체와 유사한 객체가 필요하거나, 생성 비용이 많이 드는 경우 유용하다.

6. 싱글톤

특정 클래스의 객체가 한번만 생성되도록 한다.
⚠️ 싱글톤은 안티패턴으로 분류된다. 일부사례에서 유용하긴 하나, 주의해서 사용해야 한다.

  • 생성자, 복제, 확장비활성화하고, 특정 메소드로만 인스턴스 get이 가능하다.
// 대통령을 예시로 들어보겠습니다. (대통령은 1명 뿐이니)

class President {
 	private static instance: President;
  
	private constructor() {}

	private clone(){}
	private wakeup(){}

	public static getInstance: President {
     	if(!President.instance) {
         	President.instance = new Presiden(); 
        }
      
      return President.instance;
    }
}

const president1: President = President.getInstance();
const president2: President = President.getInstance();

console.log(president1 === president2); // true

구조패턴은 다음편에...

profile
더 나은 개발경험을 생각하는, 프론트엔드 개발자입니다.

0개의 댓글