본문 바로가기
까망 동네/디자인 패턴

[디자인패턴] 프로토타입 (Prototype Pattern)

by 까망 하르방 2023. 10. 31.
반응형

🎈 프로토타입 패턴 (Prototype Pattern) 

• 객체 생성을 위해 다른 객체 복제

 → 새로운 객체 생성보다 기존 객체 복제하는 것이 효율적인 경우

 → 공통된 상태값은 유지, 필욯나 값만 변경

• 복잡한 과정으로 생성된 객체를 복사 생성할 때 유용

 → 재사용성 증가

• 별도로 원형 관리자를 도입해볼 수 있다. 

 그렇기에 객체 상태값을 접근/변경할 수 있도록 미리 구현 필요

• 디자인 패턴에서 생성 패턴 중 하나이다.

 

💻 디자인 패턴(Design Pattern)이란?

👨‍💻 디자인 패턴(Design Pattern)이란? • SW 개발 방법 중에서도 구조적인 문제 해결에 목적을 둔다. • 알고리즘과 같이 특정 문제를 해결하는 Logic 형태보다는 특정 상황에 적용할 수 있는 방

zoosso.tistory.com

 

 

[예제 코드] 프로토타입 패턴

#include <iostream>

class Prototype {
public:
    virtual Prototype* clone() = 0;
    virtual void print() = 0;
    virtual void setState(const std::string& state) = 0;
};

class ConcretePrototype : public Prototype {
private:
    std::string state;

public:
    ConcretePrototype() : state("") {}

    ConcretePrototype(const ConcretePrototype& other) {
        state = other.state;
    }

    Prototype* clone() override {
        return new ConcretePrototype(*this);
    }

    void print() override {
        std::cout << "상태: " << state << std::endl;
    }

    void setState(const std::string& newState) override {
        state = newState;
    }
};

void main() {
    // 프로토타입 객체 생성
    ConcretePrototype original;

    // 원본 객체 상태 설정
    original.setState("Original State");

    // 프로토타입 객체 복제를 통해 새로운 객체 생성
    Prototype* clone = original.clone();

    // 복제 객체의 상태 설정
    dynamic_cast<ConcretePrototype*>(clone)->setState("Cloned...");

    original.print();
    clone->print();

    // 메모리 정리
    delete clone;
}

 

프로토타입 예시코드 결과

예시에서는 하나의 상태만을 가지고 있지만

같은 상태로 복제되어 다른 속성 값을 가질 수 있는 것을 볼 수 있다.


 

[예제 코드] - 프로토타입 적용 X

#include <iostream>
#include <string>

class Knight {
private:
    std::string name;
    int health;

public:
    Knight(const std::string& name, int health) : name(name), health(health) {}

    void setName(std::string _name) {
        name = _name;
    }

    void info() {
        std::cout << "Knight: " << name << ", Health: " << health << std::endl;
    }
};

void main() {
    Knight k1("gga mang", 100);
    Knight k2 = k1;
    k2.setName("harbang");

    k1.info();
    k2.info();
}

 

프로토타입 예시코드 결과

C++의 복사생성자를 이용해서

프로토타입 패턴이 아니더라도 비슷하게 흉내낼 수 있다.

하지만 복제할 때, 복사생성자가 호출되는데

상태값이 바뀌는 런타임 중에 초기값과 이미 달라져 있을 수 있다.

 

 

[예제 코드] - 프로토타입 패턴 O

#include <iostream>
#include <string>

class Knight {
private:
    std::string name;
    int health;

public:
    Knight(const std::string& name, int health) : name(name), health(health) {}

    Knight* clone() {
        return new Knight(*this);
    }

    void setName(std::string _name) {
        name = _name;
    }

    void info() {
        std::cout << "Knight: " << name << ", Health: " << health << std::endl;
    }
};


void main() {
    Knight originalKnight("gga mang", 100);
    Knight* k1 = originalKnight.clone();
    Knight* k2 = originalKnight.clone();
    k2->setName("harbang");

    k1->info();
    k2->info();
}

 

 

🎈 프로토타입 패턴 장단점

+ 복잡한 객체 초기화 로직 처리에 유용하다.

+ 객체 생성을 위한 클래스의 수를 줄일 수 있다.

+ 유사한 구조나 초기 상태가 유사한 경우 좋다.

+ 객체 상태를 저장/복원해야 할 때, "스냅샷 용도"로도 좋다.

 

- 객체가 복제되는 것이기에 메모리 관리 필요

- 객체가 다른 객체와 상호 의존성을 가질 경우 복잡해질 수 있다.


 

[예제 코드]

#include <iostream>
#include <map>
#include <string>


class Unit {
public:
    virtual Unit* clone() = 0;
    virtual void render() = 0;
    virtual void setState(const std::string& state) = 0;
};


class Knight : public Unit {
private:
    std::string state;


public:
    Knight(const std::string& initialState) : state(initialState) {}


    Unit* clone() override {
        return new Knight(*this);
    }


    Knight(const Knight& other) {
        state = other.state;
    }


    void render() override {
        std::cout << "Knight State: " << state << std::endl;
    }


    void setState(const std::string& newState) override {
        state = newState;
    }
};


class UnitFactory {
private:
    std::map<std::string, Unit*> prototypes;


public:
    UnitFactory() {
        prototypes["Knight"] = new Knight("Default State");
    }


    Unit* createUnit(const std::string& type) {
        return prototypes[type]->clone();
    }
};


void main() {
    UnitFactory factory;


    Unit* knight1 = factory.createUnit("Knight");
    Unit* knight2 = factory.createUnit("Knight");


    // 주소 비교
    if (knight1 != knight2) {
        std::cout << "knight1과 knight2는 서로 다른 객체입니다." << std::endl;
    }


    // 상태 설정
    dynamic_cast<Knight*>(knight1)->setState("1 First");
    dynamic_cast<Knight*>(knight2)->setState("2 Second");


    knight1->render();
    knight2->render();
}

 

프로토타입 예시코드 결과

 서로 다른 Knight가 생성된 것을 확인할 수 있다.

 필요에 따라 속성 값을 변경할 수 있다.


 

🎈 다른 디자인 패턴과 비교

• 데코레이터 패턴

데코레이터 패턴은 객체에 동적으로 새로운 기능을 추가할 때 주료 사용한다.

상속을 사용하지 않고 기능 확장하는 것이 특징이다

 

 

• 싱글턴 (Singleton)

싱글톤 패턴는 인스턴스를 오직 하나만 생성하고 전역 접근 지점을 제공한다.

즉, 하나의 객체를 다른 곳에서 공유할 때 사용된다.

반면에 프로토타입 패턴은 유사한 복사본을 만드는 것으로 여러 인스턴스를 복제 생성한다.

 

[디자인패턴] 싱글턴 패턴

싱글턴(Singleton) 패턴이란? • 단, 하나의 객체를 만들어서 사용 (단일 객체) → 한번 생성되고 프로그램 종료될 때까지 메모리에 상주 • 디자인 패턴(Design Pattern)에서 "생성 패턴"에 해당 • 다른

zoosso.tistory.com

 

 

• 팩토리 (Factory)

팩토리 패턴은 객체 생성을 캡슐화하여 생성 로직을 숨기는 패턴이다

이를 통해 다양한 클래스 객체를 생성한다.

유사한 객체를 복제하여 초기화하는 프로토타입 패턴과는 활용 차이가 있다.

 

팩토리 메서드 패턴 (Factory Method)

🎈 팩토리 메서드 패턴(Factory Method Pattern) • 객체 생성 시 확장을 쉽게 하기 위한 설계 방법 강력한 결합 관계는 코드의 수정•변경을 어렵게 한다. • 객체 생성 동작을 별도 클래스로 분리하

zoosso.tistory.com

 

 

• 빌더 (Builder)

복합 객체를 단계별로 설정할 때 사용되는 패턴으로

복잡하게 만들어진 객체를 복제할 때 프로토타입 활용 하면 좋다.

심지어 기존 객체 생성을 몰라도 된다.

 

[디자인 패턴] 빌더 패턴 (Builder Pattern)

🎈 빌더 패턴(Builder Pattern) •빌더 패턴은 생성이 복잡한 객체를 단계적으로 만들 수 있다. → 생성 코드 따로 관리 • 객체 생성을 별도의 다른 클래스에 위임 •생성자 호출 코드를 한 줄로 생

zoosso.tistory.com

디자인 패턴 프로토타입 패턴

반응형

댓글