Design Pattern(1) - 생성 패턴

황승우·2022년 12월 30일
0

design-pattern

목록 보기
1/3

디자인 패턴이란

  • 과거의 소프트웨어 개발 과정에서 발견된 설계의 노하우를 축적하여 그 방법에 이름을 붙여서 이후에 재사용하기 좋은 형태로 특정 규약을 만들어서 정리한 것
  • "효율적인 코드를 만들기 위한 방법론”
  • 복잡한 구조를 한 단어로 정의함으로서 개발자들이 협업을 할 때에 의사소통을 효율적으로 할 수 있음.
  • 기존 코드의 문제점에 대해서 검증된 방법으로 해결 방안을 찾을 수 있어 효율적으로 코드를 개선 가능

디자인 패턴 종류

  1. 생성(Creational) 패턴
  • 객체 생성에 관련된 패턴
  • 객체의 생성과 조합을 캡슐화해 특정 객체가 생성되거나 변경되어도 프로그램 구조에 영향을 크게 받지 않도록 유연성을 제공한다.
  1. 구조(Structural) 패턴
  • 클래스나 객체를 조합해 더 큰 구조를 만드는 패턴
  1. 행위(Behavioral)
  • 객체나 클래스 사이의 알고리즘이나 책임 분배에 관련된 패턴
  • 한 객체가 혼자 수행할 수 없는 작업을 여러 개의 객체로 어떻게 분배하는지, 또 그렇게 하면서도 객체 사이의 결합도를 최소화하는 것에 중점을 둔다.

생성 패턴

싱글턴 패턴(**Singleton Pattern)**

  • 오직 한 개의 인스턴스 만을 갖도록 하며, 이에 대해 전역적인 접근을 허용
  • 일반적으로 특정 클래스의 인스턴스가 반드시 하나여야 하나 여러 곳에서 사용하는 경우에 사용
  • 인스턴스를 딱 1개만 생성하기에 불필요한 메모리 낭비를 최소화한다.
const Database = (function () {
  let instance;

  function createDatabaseInstance() {
    return new Object('Database Instance');
  }

  function getDatabaseInstance() {
    if (!instance) {
      instance = createDatabaseInstance();
    }

    return instance;
  }

  return { getDatabaseInstance }
})();

const databaseInstance1 = Database.getDatabaseInstance();
const databaseInstance2 = Database.getDatabaseInstance();

console.log(databaseInstance1 === databaseInstance2);
  • 싱글턴 패턴의 문제점
  1. 싱글턴은 프로그램 전체에서 하나의 객체만을 공통으로 사용하고 있기 때문에 각 객체간의 결합도가 높아지고 변경에 유연하게 대처할 수 없다.(싱글톤 객체가 변경되면 이를 참조하고 있는 모든 값들이 변경되어야 하기 때문)
  2. 여러 쓰레드가 공유되고 있는 상황에서는 아래의 블럭에서 조건문이 동시에 두번 돌 수 있기때문에 하나의 인스턴스가 아닌 여러개의 인스턴스가 발생 할 위험이 있다.

추상 팩토리 패턴(Abstract Factory Pattern)

  • 비슷한 속성의 객체들을 인터페이스로 규격화된 팩토리에서 일관된 방식으로 생성하고, 생성된 객체끼리는 쉽게 교체될 수 있도록 고안한 패턴
  • 추상 팩토리 패턴은 상세화된 서브클래스를 정의하지 않고도 서로 관련성이 있거나 독립적인 여러 객체의 군을 생성하기 위한 인터페이스를 제공
const coffeeFactory = (function() {
  const types = {};
  return {
    addType: function(type, Coffee) {
      if (Coffee.property.order) {
        types[type] = Coffee;
      }
    },
    create: function(type, options) {
      const Coffee = types[type];
      return (Coffee ? new Coffee(options) : undefined);
    }
  }
})

const Coffee = (function() {
  function Coffee(options) {
    this.name = options.name;
    this.price = options.price;
  }

  Coffee.prototype.order = function() {
    console.log(`${this.name}를 ${price}에 주문합니다`);
  }

  Coffee.prototype.orderCancel = function(reason) {
    console.log(`${this.name}를 ${reason}의 사유로 주문을 취소합니다. ${this.price}를 환불합니다.`);
  }
})

coffeeFactory.addType('Ameracano', Coffee);
coffeeFactory.addType('ColdBrew', Coffee);
coffeeFactory.addType('Latte', Coffee);

const IceAmeracano = coffeeFactory.create('Ameracano', { name: 'Ice Americano', price: 3500 });
const HotAmeracano = coffeeFactory.create('Ameracano', { name: 'Hot Americano', price: 3000 });
const BanilaColdBrew = coffeeFactory.create('ColdBrew', { name: 'Banila Cold Brew', price: 4000 });
const ChocoColdBrew = coffeeFactory.create('ColdBrew', { name: 'Choco Cold Brew', price: 4500 });

팩토리(메소드) 패턴(**Factory Pattern)**

  • 템플릿 메서드 패턴(Template Method Pattern)에서 파생
    • 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서는 추상 메서드를 통해 구체적인 내용을 결정하도록 하는 디자인 패턴
    • 상위 클래스에서 로직을 공통화할 수 있다는 장점
  • 객체를 생성하기 위해 인터페이스를 정의하지만 어떤 클래스의 인스턴스를 생성할 지에 대한 결정은 서브클래스가 내리도록 할 때 유용하게 사용
  • 어떤 클래스가 자신이 생성해야 하는 객체의 클래스를 예측할 수 없을때 사용
function animalFactory() {
  this.createAnimal = function(animalType) {
    let animal;

    switch(animalType) {
      case 'dog':
        animal = new Dog();
        break;
      case 'cat':
        animal = new Cat();
        break;
      case 'horse':
        animal = new Horse();
        break;
      default:
        animal = new Monkey();
        break;
    }

    return animal;
  }
}

const Dog = function() {
  this.makeSound = () => {
    console.log('woof woof!');
  }
}

const Cat = function() {
  this.makeSound = () => {
    console.log('prrr prrr meow!');
  }
}

const Horse = function() {
  this.makeSound = () => {
    console.log('neeeighhh!')
  }
}

const Monkey = function() {
  this.makeSound = () => {
    console.log('ooooh ahh oooh oooh!');
  }
}

const factory = new animalFactory();

const jojo = factory.createAnimal('dog');
jojo.makeSound();

const smokey = factory.createAnimal('cat');
smokey.makeSound();

const princess = factory.createAnimal('horse');
princess.makeSound();

const kong = factory.createAnimal('monkey');
kong.makeSound();

생성자 / 빌더 패턴(**Constructor / Builder Pattern)**

  • 복잡한 객체를 생성하는 방법을 정의하는 클래스와 표현하는 방법을 정의하는 클래스를 별도로 분리하여, 서로 다른 표현이라도 이를 생성할 수 있는 동일한 절차를 제공하는 패턴
  • 빌더 패턴은 많은 Optional한 멤버 변수(혹은 파라미터)나 지속성 없는 상태 값들에 대해 처리해야 하는 문제들을 해결
  • 예를 들어, 팩토리 패턴이나 추상 팩토리 패턴에서는 생성해야하는 클래스에 대한 속성 값이 많을 때 이슈
    • 클라이언트 프로그램으로부터 팩토리 클래스로 많은 파라미터를 넘겨줄 때 타입, 순서 등에 대한 관리가 어려워져 에러가 발생할 확률이 높아짐.
    • 경우에 따라 필요 없는 파라미터들에 대해서 팩토리 클래스에 일일이 null 값을 넘겨줘야 함.
    • 생성해야 하는 sub class가 무거워지고 복잡해짐에 따라 팩토리 클래스 또한 복잡해짐
    • 빌더 패턴은 이러한 문제들을 해결하기 위해 별도의 Builder 클래스를 만들어 필수 값에 대해서는 생성자를 통해, 선택적인 값들에 대해서는 메소드를 통해 step-by-step으로 값을 입력받은 후에 build() 메소드를 통해 최종적으로 하나의 인스턴스를 리턴하는 방식.
class Person {
  constructor(name, age, mother) {
    this.name = name;
    this.age = age;
    this.mother = mother;
  }
}

const tim = new Person('Tim', 31, null);
const tina = new Person('Tina', 57, null);

tim.mother = tina;

const grandma = new Person('Sherry', 80, null);

tina.mother = grandma;

// DagymServiceLibrary
export default class GymServiceMap {
  id: string;
  ...
  businessLicenseCopy: string;

  constructor(gym: GymDoc) {
    this.id = String(gym._id);
    ...
	}
}

async findOneById(_id: string): Promise<GymServiceMap | null> {
    if (!_id) {
      return null;
    }

    const gymDoc: GymDoc = await this.Collection.findOne({
      _id,
    });

    if (!gymDoc) {
      return null;
    } else {
      **return new GymServiceMap(gymDoc);**
    }
  }

프로토타입 패턴(Prototype Pattern)

  • 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다.
  • 객체를 복사하여 생성하는 방식이므로 다수의 객체를 생성해야 할 경우 객체 생성의 비용을 효과적으로 감소시킬 수 있다
  • 프로토 타입이 존재하고 복제만 해서 객체를 생성하게 되므로 서브 클래스의 수를 줄일 수 있다는 장점도 있다.
function CustomerPrototype(proto) {
    this.proto = proto;

    this.clone = function () {
        var customer = new Customer();

        customer.first = proto.first;
        customer.last = proto.last;
        customer.status = proto.status;

        return customer;
    };
}

function Customer(first, last, status) {

    this.first = first;
    this.last = last;
    this.status = status;

    this.say = function () {
        console.log("name: " + this.first + " " + this.last +
            ", status: " + this.status);
    };
}

function run() {

    var proto = new Customer("n/a", "n/a", "pending");
    var prototype = new CustomerPrototype(proto);

    var customer = prototype.clone();
    customer.say();
}

참고

https://coding-factory.tistory.com/708

https://www.hanbit.co.kr/channel/categor/category_view.html?cms_code=CMS8616098823

https://effortguy.tistory.com/182

https://www.hanbit.co.kr/channel/category/category_view.html?cms_code=CMS6705039023&cate_cd=

https://devfarming.tistory.com/4

devh.kr/2021/Design-Patterns-In-JavaScript/

https://tech-people.github.io/2020/01/08/java-design-pattern-creational/

https://velog.io/@ha0kim/Design-Pattern-생성-패턴Creational-Patterns

https://tech-people.github.io/2020/01/08/java-design-pattern-creational/

https://gmlwjd9405.github.io/2018/07/06/design-pattern.html

https://kyungyeon.dev/posts/40

profile
백엔드 개발자

0개의 댓글