原型模式

<image>

原型模式（prototype pattern），是一種創建型模式，其特點在於通過「複製」一個已經存在的實例來返回新的實例,而不是新建實例。被複製的實例就是我們所稱的「原型」，這個原型是可定製的。

原型模式多用於創建複雜的或者耗時的實例，因為這種情況下，複製一個已經存在的實例使程序運行更高效；或者創建值相等，只是命名不一樣的同類數據。

## 結構

<image>

在上面的UML類圖中，`Client`類提及`Prototype`接口來克隆一個`Product`。`Product1`類通過創建自身複本來實現`Prototype`接口。

UML序列圖展示了運行時交互：`Client`對象調用`clone()`於`prototype:Product1`對象之上，它創建並返回自身的一個複本（`product:Product1`對象）。

## 示例

### Java

下面是Java例子：

```Java
/** Prototype Class **/
public class Cookie implements Cloneable {
    public Object clone() throws CloneNotSupportedException {
        //In an actual implementation of this pattern you would now attach references to
        //the expensive to produce parts from the copies that are held inside the prototype.
        return (Cookie) super.clone();
    }
}
 
/** Concrete Prototypes to clone **/
public class CoconutCookie extends Cookie { }
 
/** Client Class**/
public class CookieMachine {
    private Cookie cookie;//cookie必须是可复制的
   
    public CookieMachine(Cookie cookie) { 
         this.cookie = cookie; 
    } 

    public Cookie makeCookie() {
        try {
            return (Cookie) cookie.clone();
        }
        catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    } 
 
    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();//通过复制原型返回多个cookie 
    } 
}
```

### Python

下面是Python例子：

```python
import copy

class Prototype():
    def clone(self): 
        return copy.copy(self)

class Product(Prototype):
    def __init__(self, number=0, **kwargs):
        self.number = number
        for key, value in kwargs.items():
            self.__dict__[key] = value

class ProductCreator():
    def __init__(self, proto):
        self.proto = proto
        self.number = proto.number
    def __call__(self):
        self.number += 1
        r = self.proto.clone()
        r.number = self.number
        return r
     
product0 = Product()
product_creator = ProductCreator(product0)
products = [product0]
for i in range(1, 20):
    products += [product_creator()]
```

### C++

下面是基於《設計模式》書中前C++98實現的C++23實現迷路園遊戲例子：

```c++
import std;

using std::array;
using std::shared_ptr;
using std::unique_ptr;
using std::vector;

enum class Direction: char { 
    NORTH, 
    SOUTH, 
    EAST, 
    WEST 
};

class MapSite {
public:
    virtual void enter() = 0;
    virtual unique_ptr<MapSite> clone() const = 0;
    virtual ~MapSite() = default;
};

class Room: public MapSite {
private:
    int roomNumber;
    shared_ptr<array<shared_ptr<MapSite>, 4>> sides;
public:
    explicit Room(int n = 0): 
        roomNumber{n}, sides{std::make_shared<array<shared_ptr<MapSite>, 4>>()} {}

    ~Room() = default;

    Room& setSide(Direction d, shared_ptr<MapSite> ms) {
        (*sides)[static_cast<size_t>(d)] = std::move(ms);
        std::println("Room::setSide {} ms", d);
        return *this;
    }

    virtual void enter() override {}

    virtual unique_ptr<MapSite> clone() const override {
        return std::make_unique<Room>(*this);
    }

    Room(const Room&) = delete;
    Room& operator=(const Room&) = delete;
};

class Wall: public MapSite {
public:
    Wall():
        MapSite() {}

    ~Wall() = default;

    virtual void enter() override {}

    [[nodiscard]]
    virtual unique_ptr<MapSite> clone() const override {
        return std::make_unique<Wall>(*this);
    }
};

class Door: public MapSite {
private:
    shared_ptr<Room> room1;
    shared_ptr<Room> room2;
public:
    explicit Door(shared_ptr<Room> r1 = nullptr, shared_ptr<Room> r2 = nullptr):
        MapSite(), room1{std::move(r1)}, room2{std::move(r2)} {}

    ~Door() = default;

    virtual void enter() override {}

    [[nodiscard]]
    virtual unique_ptr<MapSite> clone() const override {
        return std::make_unique<Door>(*this);
    }

    void initialize(shared_ptr<Room> r1, shared_ptr<Room> r2) {
        room1 = std::move(r1);
        room2 = std::move(r2);
    }

    Door(const Door&) = delete;
    Door& operator=(const Door&) = delete;
};

class Maze {
private:
    vector<shared_ptr<Room>> rooms;
public:
    Maze() = default;
    ~Maze() = default;

    Maze& addRoom(shared_ptr<Room> r) {
        std::println("Maze::addRoom {}", reinterpret_cast<void*>(r.get()));
        rooms.push_back(std::move(r));
        return *this;
    }

    [[nodiscard]]
    shared_ptr<Room> roomNo(int n) const { 
        for (const Room& r: rooms) { 
            // actual lookup logic here... 
        } 
        return nullptr; 
    }

    [[nodiscard]]
    virtual unique_ptr<Maze> clone() const {
        return std::make_unique<Maze>(*this);
    }
};

class MazeFactory {
public:
    MazeFactory() = default;

    virtual ~MazeFactory() = default;

    [[nodiscard]]
    virtual unique_ptr<Maze> makeMaze() const {
        return std::make_unique<Maze>();
    }

    [[nodiscard]]
    virtual shared_ptr<Wall> makeWall() const {
        return std::make_shared<Wall>();
    }

    [[nodiscard]]
    virtual shared_ptr<Room> makeRoom(int n) const {
        return std::make_shared<Room>(n);
    }

    [[nodiscard]]
    virtual shared_ptr<Door> makeDoor(shared_ptr<Room> r1, shared_ptr<Room> r2) const {
        return std::make_shared<Door>(std::move(r1), std::move(r2));
    }
};

class MazePrototypeFactory: public MazeFactory {
private:
    unique_ptr<Maze> prototypeMaze;
    shared_ptr<Room> prototypeRoom;
    shared_ptr<Wall> prototypeWall;
    shared_ptr<Door> prototypeDoor;
public:
    MazePrototypeFactory(unique_ptr<Maze> m, shared_ptr<Wall> w, shared_ptr<Room> r, shared_ptr<Door> d):
        MazeFactory(), prototypeMaze{std::move(m)}, prototypeRoom{std::move(r)}, 
        prototypeWall{std::move(w)}, prototypeDoor{std::move(d)} {}

    ~MazePrototypeFactory() = default;

    virtual unique_ptr<Maze> makeMaze() const override {
        return prototypeMaze->clone();
    }

    [[nodiscard]]
    virtual shared_ptr<Room> makeRoom(int n) const override {
        return prototypeRoom->clone();
    }

    [[nodiscard]]
    virtual shared_ptr<Wall> makeWall() const override {
        return prototypeWall->clone();
    }

    [[nodiscard]]
    virtual shared_ptr<Door> makeDoor(shared_ptr<Room> r1, shared_ptr<Room> r2) const override {
        shared_ptr<Door> door = prototypeDoor->clone();
        door->initialize(std::move(r1), std::move(r2));
        return door;
    }

    MazePrototypeFactory(const MazePrototypeFactory&) = delete;
    MazePrototypeFactory& operator=(const MazePrototypeFactory&) = delete;
};

class MazeGame {
public:
    MazeGame() = default;
    ~MazeGame() = default;

    [[nodiscard]]
    unique_ptr<Maze> createMaze(MazePrototypeFactory& factory) {
        unique_ptr<Maze> maze = factory.makeMaze();
        shared_ptr<Room> r1 = factory.makeRoom(1);
        shared_ptr<Room> r2 = factory.makeRoom(2);
        shared_ptr<Door> door = factory.makeDoor(r1, r2);

        maze->addRoom(std::move(r1))
            .addRoom(std::move(r2));

        r1->setSide(Direction::NORTH, factory.makeWall())
            .setSide(Direction::EAST, door)
            .setSide(Direction::SOUTH, factory.makeWall())
            .setSide(Direction::WEST, factory.makeWall());

        r2->setSide(Direction::NORTH, factory.makeWall())
            .setSide(Direction::EAST, factory.makeWall())
            .setSide(Direction::SOUTH, factory.makeWall())
            .setSide(Direction::WEST, door);

        return maze;
    }
};

int main(int argc, char* argv[]) {
    MazeGame game;
    MazePrototypeFactory simpleMazeFactory(
        std::make_unique<Maze>(),
        std::make_shared<Wall>(),
        std::make_shared<Room>(0),
        std::make_shared<Door>()
    );

    unique_ptr<Maze> maze = game.createMaze(simpleMazeFactory);
}
```

程序的輸出為：

```output
Maze::addRoom 0x1160f50
Maze::addRoom 0x1160f70
Room::setSide 0 0x11613c0
Room::setSide 2 0x1160f90
Room::setSide 1 0x11613e0
Room::setSide 3 0x1161400
Room::setSide 0 0x1161420
Room::setSide 2 0x1161440
Room::setSide 1 0x1161460
Room::setSide 3 0x1160f90
```

## 外部連結

- The Design Patterns Memory