{{위키데이터 속성 추적}}
'''프로토타입 패턴'''({{lang|en|prototype pattern}})은 [[소프트웨어 디자인 패턴]] 용어로, 생성할 객체들의 타입이 프로토타입인 인스턴스로부터 결정되도록 하며, 인스턴스는 새 객체를 만들기 위해 자신을 복제(clone)하게 된다.

* 프로토타입 패턴은, [[추상 팩토리 패턴]]과는 반대로, 클라이언트 응용 프로그램 코드 내에서 객체 창조자(creator)를 서브클래스(subclass)하는 것을 피할 수 있게 해준다.
* 프로토타입 패턴은 새로운 객체는 일반적인 방법(예를 들어, <code>new</code>를 사용해서라든지)으로 객체를 생성(create)하는 고유의 비용이 주어진 응용 프로그램 상황에 있어서 불가피하게 매우 클 때, 이 비용을 감내하지 않을 수 있게 해준다.

패턴을 구현하려면, 우선 <code>clone()</code> 메소드를 선언하는 추상 베이스 클래스를 하나 만든다. [[다형성 (컴퓨터 과학)|다형적]] [[생성자]](polymorphic constructor) 기능이 필요한 클래스가 있다면, 그것을 앞에서 만든 클래스를 상속받게 한 후, <code>clone()</code> 메소드 내의 코드를 구현한다.

[[파일:Prototype Pattern ZP.svg|섬네일|412px|구조]]

== 원칙 ==
객체 생성과 관련된 패턴들은 서로 영역이 겹치는 면이 있다. 프로토타입 패턴과 [[추상 팩토리 패턴]] 중 어느 하나가 적용될 수 있는 경우가 있다. 추상 팩토리 패턴이 프로토타입들의 집합을 갖고있다가, 클론(clone)한 뒤 프로덕트(product) 객체를 반환할 수도 있다.<ref name="GoF">{{서적 인용|
       저자= [[Erich Gamma]], [[Richard Helm]], [[Ralph Johnson]], [[John Vlissides]] |
       제목 = [[디자인 패턴 (책)|Design Patterns]] |
       출판사=[[Addison-Wesley]] |
       년도 =[[1994년]] |ISBN=0-201-63361-2
}}</ref>{{Rp|126쪽}}

[[추상 팩토리 패턴]], [[빌더 패턴]], 프로토타입 패턴은 각 구현에 있어서 [[싱글턴 패턴]]을 활용할 수 있다.<ref name="GoF" />{{Rp|81쪽}}{{Rp|134쪽}}

다시 말해 추상 팩토리 클래스는 종종 팩토리 메소드와 함께 구현하거나<ref>[[상속 (객체 지향 프로그래밍)|상속]]을 통해 객체를 생성</ref>, 프로토타입을 이용해서 구현되기도 한다.<ref>[[딜리게이션]]을 통해 객체를 생성</ref>

보통 설계는 처음에는 팩토리 메소드로 출발한다. 다음에 설계자의 재량에 따라 추상 팩토리 패턴, [[빌더 패턴]], 프로토타입 패턴으로 바뀔 수 있다.<ref name="GoF" />{{Rp|136쪽}}

프로토타입은 [[서브클래스|서브클래싱]]을 필요로 하지 않는다. 하지만 "초기화" 동작을 필요로 한다. [[팩토리 메서드 패턴]]은 서브클래싱을 필요로 하나, "초기화" 동작은 필요로 하지 않는다.

설계자는 [[컴포지트 패턴]]이나 [[데코레이터 패턴]]을 매우 많이 사용한 무거운 설계를 프로토타입을 사용하여 더 좋게 만들 수 있다.

원칙은 "[[실행시간|런타임]]"에 또 다른 "객체"를 생성한다는 것이다. 다시 말해 이 시점에 가서 클로닝(cloning)을 하는 객체의 "실제 복사본"이 만들어지는 것이다. "실제 복사본"이라는 말은 새로 생성되는 객체가 클로닝(cloning) 당하는 객체의 [[애트리뷰트]]와 똑같은 애트리뷰트를 가질 것이라는 말이다. 반면에, "<code>new</code>"를 이용해 객체를 생성했다면, 새로이 생성된 객체의 애트리뷰트들은 초기값을 가질 것이다.

예를 들면, 은행 계좌 입출금 트랜잭션을 수행하는 시스템을 가정해 보자. 프로그래머는 은행 이용자의 계좌 정보를 갖고 있는 객체를 하나 복사한다. 그 객체를 가지고 그 객체 상에다 은행 입출금 트랜잭션을 수행한다. 그 다음 원래의 객체를 이 객체로 바꿔치기한다. 프로그래머는 이런 경우 <code>new</code> 대신 <code>clone</code>을 쓴다.

== 예제 ==
=== C++ ===
<syntaxhighlight lang="cpp">
#include <iostream>
#include <map>
#include <string>
#include <cstdint>

using namespace std;

enum RECORD_TYPE_en
{
  CAR,
  BIKE,
  PERSON
};

/**
 * Record is the Prototype
 */

class Record
{
  public :

    Record() {}

    virtual ~Record() {}

    virtual Record* Clone() const=0;

    virtual void Print() const=0;
};

/**
 * CarRecord is Concrete Prototype
 */

class CarRecord: public Record
{
  private :
    string m_oStrCarName;

    uint32_t m_ui32ID;

  public :

    CarRecord(const string& _oStrCarName, uint32_t _ui32ID)
     : Record(), m_oStrCarName(_oStrCarName),
        m_ui32ID(_ui32ID)
    {
    }

    CarRecord(const CarRecord& _oCarRecord)
     : Record()
    {
      m_oStrCarName = _oCarRecord.m_oStrCarName;
      m_ui32ID = _oCarRecord.m_ui32ID;
    }

    ~CarRecord() {}

    CarRecord* Clone() const
    {
      return new CarRecord(*this);
    }

    void Print() const
    {
      cout << "Car Record" << endl
        << "Name : " << m_oStrCarName << endl
        << "Number: " << m_ui32ID << endl << endl;
    }
};

/**
 * BikeRecord is the Concrete Prototype
 */

class BikeRecord: public Record
{
  private :
    string m_oStrBikeName;

    uint32_t m_ui32ID;

  public :
    BikeRecord(const string& _oStrBikeName, uint32_t _ui32ID)
     : Record(), m_oStrBikeName(_oStrBikeName),
        m_ui32ID(_ui32ID)
    {
    }

    BikeRecord(const BikeRecord& _oBikeRecord)
     : Record()
    {
      m_oStrBikeName = _oBikeRecord.m_oStrBikeName;
      m_ui32ID = _oBikeRecord.m_ui32ID;
    }

    ~BikeRecord() {}

    BikeRecord* Clone() const
    {
      return new BikeRecord(*this);
    }

    void Print() const
    {
      cout << "Bike Record" << endl
        << "Name : " << m_oStrBikeName << endl
        << "Number: " << m_ui32ID << endl << endl;
    }
};

/**
 * PersonRecord is the Concrete Prototype
 */

class PersonRecord: public Record
{
  private :
    string m_oStrPersonName;

    uint32_t m_ui32Age;

  public :
    PersonRecord(const string& _oStrPersonName, uint32_t _ui32Age)
     : Record(), m_oStrPersonName(_oStrPersonName),
        m_ui32Age(_ui32Age)
    {
    }

    PersonRecord(const PersonRecord& _oPersonRecord)
     : Record()
    {
      m_oStrPersonName = _oPersonRecord.m_oStrPersonName;
      m_ui32Age = _oPersonRecord.m_ui32Age;
    }

    ~PersonRecord() {}

    Record* Clone() const
    {
      return new PersonRecord(*this);
    }

    void Print() const
    {
      cout << "Person Record" << endl
        << "Name: " << m_oStrPersonName << endl
        << "Age : " << m_ui32Age << endl << endl ;
    }
};

/**
 * RecordFactory is the client
 */

class RecordFactory
{
  private :
    map<RECORD_TYPE_en, Record* > m_oMapRecordReference;

  public :
    RecordFactory()
    {
      m_oMapRecordReference[CAR]    = new CarRecord("Ferrari", 5050);
      m_oMapRecordReference[BIKE]   = new BikeRecord("Yamaha", 2525);
      m_oMapRecordReference[PERSON] = new PersonRecord("Tom", 25);
    }

    ~RecordFactory()
    {
      delete m_oMapRecordReference[CAR];
      delete m_oMapRecordReference[BIKE];
      delete m_oMapRecordReference[PERSON];
    }

    Record* CreateRecord(RECORD_TYPE_en enType)
    {
      return m_oMapRecordReference[enType]->Clone();
    }
};

int main()
{
  RecordFactory* poRecordFactory = new RecordFactory();

  Record* poRecord;
  poRecord = poRecordFactory->CreateRecord(CAR);
  poRecord->Print();
  delete poRecord;

  poRecord = poRecordFactory->CreateRecord(BIKE);
  poRecord->Print();
  delete poRecord;

  poRecord = poRecordFactory->CreateRecord(PERSON);
  poRecord->Print();
  delete poRecord;

  delete poRecordFactory;
  return 0;
}
</syntaxhighlight>

=== C# ===
<syntaxhighlight lang="csharp">
public enum RecordType
{
   Car,
   Person
}

/// <summary>
/// Record is the Prototype
/// </summary>
public abstract class Record
{
   public abstract Record Clone();
}

/// <summary>
/// PersonRecord is the Concrete Prototype
/// </summary>
public class PersonRecord: Record
{
   string name;
   int age;

   public override Record Clone()
   {
      return (Record)this.MemberwiseClone(); // default shallow copy
   }
}

/// <summary>
/// CarRecord is another Concrete Prototype
/// </summary>
public class CarRecord: Record
{
   string carname;
   Guid id;

   public override Record Clone()
   {
      CarRecord clone = (Record)this.MemberwiseClone(); // default shallow copy
      clone.id = Guid.NewGuid(); // always generate new id
      return clone;
   }
}

/// <summary>
/// RecordFactory is the client
/// </summary>
public class RecordFactory
{
   private static Dictionary<RecordType, Record> _prototypes =
      new Dictionary<RecordType, Record>();

   /// <summary>
   /// Constructor
   /// </summary>
   public RecordFactory()
   {
      _prototypes.Add(RecordType.Car, new CarRecord());
      _prototypes.Add(RecordType.Person, new PersonRecord());
   }

   /// <summary>
   /// The Factory method
   /// </summary>
   public Record CreateRecord(RecordType type)
   {
      return _prototypes[type].Clone();
   }
}
</syntaxhighlight>

=== Java ===
<syntaxhighlight lang="java">
/** Prototype Class **/
public class Cookie implements Cloneable {

   public Object clone() {
      try {
         Cookie copy = (Cookie)super.clone();

         //In an actual implementation of this pattern you might now change references to
         //the expensive to produce parts from the copies that are held inside the prototype.

         return copy;

      }
      catch(CloneNotSupportedException e) {
         e.printStackTrace();
         return null;
      }
   }
}

/** Concrete Prototypes to clone **/
public class CoconutCookie extends Cookie { }

/** Client Class**/
public class CookieMachine {

   private Cookie cookie;//could have been a private Cloneable cookie;

   public CookieMachine(Cookie cookie) {
      this.cookie = cookie;
   }
   public Cookie makeCookie() {
      return (Cookie)cookie.clone();
   }
   public Object clone() { }

   public static void main(String args[]) {
      Cookie tempCookie =  null;
      Cookie prot = new CoconutCookie();
      CookieMachine cm = new CookieMachine(prot);
      for (int i=0; i<100; i++)
         tempCookie = cm.makeCookie();
   }
}
</syntaxhighlight>

=== Python ===

<syntaxhighlight lang="python">
    from copy import copy, deepcopy

    class Prototype:
        def __init__(self):
           self._objs = {}

        def registerObject(self, name, obj):
           """
           register an object.
           """
           self._objs[name] = obj

        def unregisterObject(self, name):
           """unregister an object"""
           del self._objs[name]

        def clone(self, name, **attr):
           """clone a registered object and add/replace attr"""
           obj = deepcopy(self._objs[name])
           obj.__dict__.update(attr)
           return obj

    # create another instance, w/state, of an existing instance of unknown class/state

    g = Graphic() # non-cooperative form
    shallow_copy_of_g = copy(g)
    deep_copy_of_g = deepcopy(g)

    class Graphic:
        def clone(self):
           return copy(self)
    g = Graphic() # cooperative form
    copy_of_g = g.clone()
</syntaxhighlight>

=== PHP ===
[[PHP]] 5에서는, 객체는 레퍼런스 형태로 전달된다(pass by reference).

값 형태로 전달하기 위해서는, "매직 펑션" __clone()을 써야한다.
이러한 까닭으로 PHP 5에서는 프로토타입 패턴을 구현하기 매우 쉽다.<ref>[http://us2.php.net/manual/en/language.oop5.cloning.php object cloning]</ref>
<syntaxhighlight lang="php">
<?php
abstract class BookPrototype {
    protected $title;
    protected $topic;
    abstract function __clone();
    function getTitle() {
        return $this->title;
    }
    function setTitle($titleIn) {
        $this->title = $titleIn;
    }
    function getTopic() {
        return $this->topic;
    }
}

class PHPBookPrototype extends BookPrototype {
    function __construct() {
        $this->topic = 'PHP';
    }
    function __clone() {
    }
}

class SQLBookPrototype extends BookPrototype {
    function __construct() {
        $this->topic = 'SQL';
    }
    function __clone() {
    }
}

  writeln('BEGIN TESTING PROTOTYPE PATTERN');
  writeln('');

  $phpProto = new PHPBookPrototype();
  $sqlProto = new SQLBookPrototype();

  $book1 = clone $sqlProto;
  $book1->setTitle('SQL For Cats');
  writeln('Book 1 topic: '.$book1->getTopic());
  writeln('Book 1 title: '.$book1->getTitle());
  writeln('');

  $book2 = clone $phpProto;
  $book2->setTitle('OReilly Learning PHP 5');
  writeln('Book 2 topic: '.$book2->getTopic());
  writeln('Book 2 title: '.$book2->getTitle());
  writeln('');

  $book3 = clone $sqlProto;
  $book3->setTitle('OReilly Learning SQL');
  writeln('Book 3 topic: '.$book3->getTopic());
  writeln('Book 3 title: '.$book3->getTitle());
  writeln('');

  writeln('END TESTING PROTOTYPE PATTERN');

  function writeln($line_in) {
    echo $line_in."<br/>";
  }
?>
</syntaxhighlight><ref>[http://sourcemaking.com/design_patterns/prototype/php Prototype Design Pattern in PHP<!-- 봇이 따온 제목 -->]</ref>
출력은 다음과 같다.
<pre>
BEGIN TESTING PROTOTYPE PATTERN

Book 1 topic: SQL
Book 1 title: SQL For Cats

Book 2 topic: PHP
Book 2 title: OReilly Learning PHP 5

Book 3 topic: SQL
Book 3 title: OReilly Learning SQL

END TESTING PROTOTYPE PATTERN
</pre>

== 같이 보기 ==
* [[프로토타입 기반 프로그래밍]]
* [[Abstract factory 패턴|추상 팩토리(Abstract factory) 패턴]]
* [[Builder 패턴|빌더(Builder) 패턴]]

== 각주 ==
<references />

{{디자인 패턴(소프트웨어)}}

[[분류:소프트웨어 디자인 패턴]]