Post

[Design Pattern] Prototype Pattern

Mục lục

  1. Phần 1: Factory Method
  2. Phần 2: Abstract Factory
  3. Phần 3: Builder
  4. Phần 4: Prototype
  5. Phần 5: Singleton
  6. Phần 6: Adapter
  7. Phần 7: Bridge

Phần 4: Prototype Pattern

Prototypecreational pattern cho phép ta có thể copy object mà không cần phải phụ thuộc vào class của nó.

Vấn đề

Bạn muốn copy một object, điều đầu tiên cần làm đó là tạo một instance mới của class, sau đò duyệt qua toàn bộ các giá trị của các fields thuộc về object hiện có và gán giá trị đó cho object mới tạo. Cách làm này không sai nhưng không thể áp dụng khi object có những private fields.

Ngoài ra còn có một vấn đề khác đó là bạn cần phải biết class tương ứng với object vừa tạo, điều này khiến code của bạn sẽ phụ thuộc vào một class. Hơn nữa có thể bạn chỉ biết mỗi interface của object chứ không biết được class cụ thể của nó.

Copying an object “from the outside” isn’t always possible.

Giải pháp

Prototype Pattern sẽ uỷ thác việc clone object cho chính object được clone. Cụ thể là bạn có thể định nghĩa một interface chung cho các object mà bạn muốn clone. Đơn giản chỉ là định nghĩa interface với method clone.

Việc triển khai clone method là hoàn toàn giống nhau ở các class. Cụ thể là tạo một object mới, sao lưu toàn bộ giá trị của các fields vào object mới. Bản thân mọi ngôn ngữ lập trình cũng cho phép các object có thể truy cập đến các private field của object khác nên ta hoàn toàn có thể sao lưu các giá trị private một cách dễ dàng.

Một object hỗ trợ cloning sẽ được gọi là prototype.

Khi object của bạn có nhiều fields cũng như các config khác thì việc clone chúng có thể được triển khai ở subclass.

Pre-built prototypes can be an alternative to subclassing.

Implementation

(1) Định nghĩa Prototype interface với clone method

(2) ConcretePrototype class sẽ implement Prototype interface, ngoài việc copy object gốc, clone method có thể có các xử lí khác nếu object cần clone là nested object.

(3) Client có thể tạo ra một bản sao của bất kỳ object nào tuân theo prototype interface.

Pseudocode

Trong ví dụ này, mẫu Prototype cho phép bạn tạo ra các bản sao chính xác của các đối tượng hình học, mà không cần liên kết mã với các lớp của chúng.

Cloning a set of objects that belong to a class hierarchy.

Source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Prototype {
  public primitive: any;
  public component: object;
  public circularReference: ComponentWithBackReference;

  public clone(): this {
    const clone = Object.create(this);

    clone.component = Object.create(this.component);

    // If object has nested object, change reference of nested object to
    // original object to cloned object

    clone.circularReference = {
      ...this.circularReference,
      prototype: { ...this },
    };

    return clone;
  }
}

class ComponentWithBackReference {
  public prototype;

  constructor(prototype: Prototype) {
    this.prototype = prototype;
  }
}

const clientCode = () => {
  const p1 = new Prototype();
  p1.primitive = 245;
  p1.component = new Date();
  p1.circularReference = new ComponentWithBackReference(p1);

  const p2 = p1.clone();

  if (p1.primitive === p2.primitive) {
    console.log("Primitive is THE SAME");
  } else {
    console.log("Primitive is DIFF");
  }

  if (p1.component === p2.component) {
    console.log("Component is THE SAME");
  } else {
    console.log("Component is DIFF");
  }

  if (p1.circularReference === p2.circularReference) {
    console.log("CircularReference is THE SAME");
  } else {
    console.log("CircularReference is DIFF");
  }

  if (p1.circularReference.prototype === p2.circularReference.prototype) {
    console.log("CircularReferencePrototype is THE SAME");
  } else {
    console.log("CircularReferencePrototype is DIFF");
  }
};

clientCode();

[References]

https://refactoring.guru/design-patterns/prototype

This post is licensed under CC BY 4.0 by the author.