Tải bản đầy đủ (.doc) (15 trang)

Thiết kế phần mềm hướng đối tượng

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (260.11 KB, 15 trang )

Thiết kế phần mềm HDT
GVHD: Thầy Nguyễn Trần Thi Văn
Nhóm 4:
Vũ Minh Hải 07110037
Lâm Chí Hiền 07110041
Nguyễn Công Hiếu 07110045
Chiếng Mành Sâm 07110100
Prototype pattern
I. Prototype pattern là gì?
1. Thuộc nhóm Creational Patterns(Abstract factory, Builder, Factory method, Prototype và
Singleton).
2. Prototype là mẫu thiết kế chỉ định ra một đối tượng đặc biệt để khởi tạo, nó sử dụng một thể
nghiệm sơ khai rồi sau đó sao chép ra các đối tượng khác từ mẫu đối tượng này.
II. Mục đích và ý nghĩa,tính chất:
Prototype Pattern giúp cho việc khởi tạo object bằng một object nguyên mẫu (prototype), là copy của object
“mẫu” đó.
Ý tưởng của mẫu là chúng ta được cung cấp một object và sẽ dùng chính object này để như là một hình mẫu
(template) khi cần tạo lập một đối tượng mới. Việc tạo lập object mới sẽ dựa trên object mẫu mà không sử dụng
toán tử new hoặc constructor … được cụng cấp bởi ngôn ngữ lập trình. Lý do là chúng ta không biết được thông
tin nội tại chi tiết bên trong object và object có thể che dấu và chỉ cung cấp ra bên ngoài một lượng thông tin
giới hạn. Do vậy ta không thể dùng toán tử new và sao chép những dữ liệu được object cung cấp (vốn không
đầy đủ) cho một object mới. Cách tốt nhất là để cho chính object “mẫu” tự xác định thông tin và dữ liệu sao
chép.
Tính chất:
• Tần suất sử dụng thấp hoặc trung bình
• Đặc trưng chủ yếu của prototype là dùng phương thức clone() để sao chép đối tượng.
• Có liên quan chặc chẽ với mẫu Abstract Factory
III. Cấu trúc:
- Prototype: Cung cấp interface để copy chính bản thân nó (phương thức clone())
- ConcreatePrototype: Implement interface được cung cấp bởi Prototype để copy chính bản thân nó. (thể hiện cụ
thể phương thức clone())


- Client: Tạo một object mới bằng cách yêu cầu prototype copy chính bản thân nó.
IV. Ưu và nhược điểm:
Prototype ẩn đi những lớp con rời rạc từ phía client, do vậy làm giảm đi số lớp con mà client cần biết. Hơn thế
nữa pattern này làm cho client hoạt động với những lớp con cụ thể mà không phải thay đổi gì.
Sau đây là một vài lợi ích khác của prototype pattern:
- Thêm và loại bỏ lớp concreate lúc run-time: Prototype hợp nhất quá trình khởi tạo một object mới vào trong
hệ thống đơn giản chỉ bằng cách đăng ký một thực thể nguyên mẫu với client. Điều đó linh động hơn một chút
so với các mẫu kiến tạo khác bởi vì client có thể cài đặt và loại bỏ các nguyên mẫu tại thời điểm run-time.
- Khởi tạo object mới bằng cách thay đổi một vài attribute của object (các object có ít điểm khác biệt nhau):
Một hệ thống linh động sẽ để cho chúng ta tự define một hành động nào đó thông qua sự kết hợp với một object
(nghĩa là một phương thức của một class) hơn là define một class mới. Client có thể thể hiện một tác động khác
bằng cách ủy quyền cho lớp prototype. Đồng thời cách thức khởi tạo này cũng giúp cho chúng ta khai báo một
“lớp mới” mà không phải lập trình gì cả. Thực tế thì việc copy một nguyên mẫu giống như việc khởi tạo một
object từ một class mới. Prototype pattern giúp giảm số lớp mà hệ thống cần dùng.
- Khởi tạo object mới bằng cách thay đổi cấu trúc: Rất nhiều ứng dụng xây dựng hệ thống từ nhiều phần và
các phần con. Các phần con lại khởi tạo từ nhiều phần con khác (chia nhỏ bài toán). Prototype pattern cũng hỗ
trợ điều này. Nghĩa là các phần có đó có thể được khởi tạo từ việc copy một nguyên mẫu từ một “cấu trúc”
khác. Miễn là các phần kết hợp đều thể hiện Clone() và được sử dụng với cấu trúc khác nhau làm nguyên mẫu.
- Giảm việc phân lớp: Đôi khi hệ thống quá phức tạp vì có quá nhiều class, và cây thừa kế của lớp khởi tạo có
quá nhiều lớp song song cùng mức. Prototype pattern rõ ràng làm giảm số lớp và sự phức tạp của cây thừa kế
(class hierarchy)
Một điểm hạn chế của prototype pattern là đôi khi việc phải implement mỗi phương thức clone() của mỗi lớp
con của Prototype sẽ gặp khó khăn. Ví dụ như việc implement clone() sẽ khó khăn khi mà những object nội
hàm của chúng không hỗ trợ việc copy hoặc có một reference (không copy được reference)
V. Implementation (ex)
using System;
using System.Collections.Generic;

namespace DoFactory.GangOfFour.Prototype.RealWorld
{

class MainApp
{
static void Main()
{
ColorManager colormanager = new ColorManager();
colormanager["red"] = new Color(255, 0, 0);
colormanager["green"] = new Color(0, 255, 0);
colormanager["blue"] = new Color(0, 0, 255);


colormanager["angry"] = new Color(255, 54, 0);
colormanager["peace"] = new Color(128, 211, 128);
colormanager["flame"] = new Color(211, 34, 20);


Color color1 = colormanager["red"].Clone() as Color;
Color color2 = colormanager["peace"].Clone() as Color;
Color color3 = colormanager["flame"].Clone() as Color;

Console.ReadKey();
}
}

abstract class ColorPrototype
{
public abstract ColorPrototype Clone();
}


class Color : ColorPrototype

{
private int _red;
private int _green;
private int _blue;

// Constructor
public Color(int red, int green, int blue)
{
this._red = red;
this._green = green;
this._blue = blue;
}

// Create a shallow copy
public override ColorPrototype Clone()
{
Console.WriteLine(
"Cloning color RGB: {0,3},{1,3},{2,3}",
_red, _green, _blue);

return this.MemberwiseClone() as ColorPrototype;
}
}


class ColorManager
{
private Dictionary<string, ColorPrototype> _colors =
new Dictionary<string, ColorPrototype>();
public ColorPrototype this[string key]

{
get { return _colors[key]; }
set { _colors.Add(key, value); }
}
}
}
Out put
Cloning color RGB: 255, 0, 0
Cloning color RGB: 128,211,128
Cloning color RGB: 211, 34, 20
VI. Các mẫu liên quan:
• Đôi khi các thiết kế trong Creational Patterns chồng chéo lên nhau. Có những trường hợp cả hai
(Phototype và Abstract Factory) đều phù hợp. Trường hợp khác lại bổ sung cho nhau.,cụ thể : Abstract
Factory có thể lưu trữ một tập hợp các bản mẫu (Phototype) để từ đó nhân bản và trả lại các đối tượng
sản phẩm .Abstract Factory, Builder , và Prototype có thể sử dụng Singleton trong việc triển khai thực
hiện.Các lớp Abstract Factory thường được thực hiện với Factory Methods ( tạo thông qua kế thừa ),
nhưng chúng cũng có thể được thực hiện bằng cách sử dụng Phototype( tạo thông qua các ủy quyền cho
các đối tượng tự sao chép chính bản thân nó )
• Thông thường ,các thiết kế bắt đầu bằng cách sử dụng FactoryMethod (ít phức tạp, khả năng tùy biến
cao, các lớp con nhiều),và có thể cải tiến thành Abstract Factory, Phototype hoặc Builder (linh hoạt hơn
và cũng phức tạp hơn ) khi nhà thiết kế cần sự linh hoạt nhiều hơn.
• Phototype không yêu cầu lớp con ,nhưng lại cần một hàm khởi tạo (hàm clone() copy đối tượng).
Factory Method lại yêu cầu lớp con nhưng không cần hàm khởi tạo.
• Phototype thường có lợi khi được sử dụng trong những thiết kế composite và decorator pattern .
• Các nguyên tắc có thể là bạn sẽ cần phải sao chép một đối tượng khi bạn muốn tạo ra một đối tượng
trong thời gian chạy mà nó là một bản sao thực sự của đối tượng mà bạn đang sao chép. Bản sao thực sự
có nghĩa là tất cả các thuộc tính của đối tượng vừa được tạo phải giống với đối tượng được sao chép.
Nếu bạn có thể khởi tạo sử dụng new() , bạn sẽ nhận được một đối tượng với tất cả các thuộc tính có giá
trị ban đầu.Ví dụ, nếu bạn đang thiết kế một hệ thống cho các giao dịch tài khoản ngân hàng thực hiện,
sau đó bạn sẽ muốn tạo một bản sao của các đối tượng nắm giữ thông tin tài khoản của bạn, thực hiện

các giao dịch trên nó, và sau đó thay thế các đối tượng ban đầu với một trong những sửa đổi. Trong
trường hợp này, bạn sẽ muốn sử dụng clone () thay vì new().
VII. Một số ví dụ cụ thể:
• Ví dụ, nếu bạn đang thiết kế một hệ thống cho các giao dịch tài khoản ngân hàng thực hiện, sau đó bạn
sẽ muốn tạo một bản sao của các đối tượng nắm giữ thông tin tài khoản của bạn, thực hiện các giao dịch
trên nó, và sau đó thay thế các đối tượng ban đầu với một trong những sửa đổi. Trong trường hợp này,
bạn sẽ muốn sử dụng clone () thay vì new().
• Xây dựng cho một trò chơi có sử dụng một mê cung với các vật cản khác nhau mà nhân vật phải vượt
qua. Ta cần một phương pháp nhanh chóng tạo ra các bản đồ phức tạp bằng cách sử dụng các đối tượng:
bức tường, cửa, lối đi, phòng
Thay vì cứng nhắc dùng hàm khởi tạo (new()) cho từng đối tượng, ta dùng cách hay hơn là sử dụng
prototype, phương thức createMaze(tạo mê cung ) sẽ nhận tham số từ những bản mẫu phòng, tường, Vì
thế bản đồ dễ dàng thay đổi bằng cách thay thế những đối tượng mẫu với những bản đồ khác nhau.
Client là phương thức createMaze(tạo mê cung ) và các lớp ConcretePrototype sẽ tạo ra bản sao cho
các đối tượng khác nhau.
• Giả sử chúng ta đang làm một phân tích bán hàng trên một tập hợp các dữ liệu từ cơ sở dữ liệu. Thông
thường, ta sẽ sao chép thông tin từ cơ sở dữ liệu, đóng gói nó vào một đối tượng và phân tích trên đối
tượng đó. Nhưng nếu có thể có những phân tích khác trên cùng một bộ dữ liệu,ta phải đọc cơ sở dữ liệu
một lần nữa và tạo ra một đối tượng mới. Điều đó thật sự không cần thiết và hao tổn nhiều tài nguyên. .
Nếu chúng ta sử dụng các mô hình prototype : đối tượng được sử dụng trong phân tích đầu tiên sẽ được
nhân bản và được sử dụng cho các phân tích khác.
Khách hàng là ở đây một trong những phương pháp gói gọn thông tin từ CSDL vào một đối tượng. The
ConcretePrototype classes will be classes that, from the object created after extracting data from the
database, will copy it into objects used for analysis. Các lớp ConcretePrototype sẽ sao chép các đối
tượng đó vào để thực hiện các phân tích.
VIII. Một số điểm cần lưu ý khi thực hiện:
• Sử dụng một prototype manager:
Khi ứng dụng có nhiều nguyên mẫu .Những nguyên mẫu đó có thể được tạo ra và phá hủy động, một bản của
nguyên mẫu nên được giữ lại.Vì thế cần phải có một prototype manager để quản lý các nguyên mẫu với từng
khóa nhất định, tìm kiếm một nguyên mẫu với một khóa nào đó, loại bỏ một nguyên mẫu đã tồn tại, v .v Các

client sẽ sử dụng giao diện của prototype manager để thao tác với nguyên mẫu tại thời gian chạy và phải yêu
cầu sự cho phép trước khi sử dụng phương pháp Clone ().
Không có sự khác biệt nhiều giữa việc sử dụng một nguyên mẫucó prototype manager và factory method sử
dụng các nhà máy khởi tạo.Có lẽ sự khác biệt chỉ ở sự thực thi.
• Thực thi Clone():
Một vấn đề nhỏ xuất hiện khi nói về deep or shallow a clone (nhân bản sâu và nông): deep-clone sao chép các
biến trong đối tượng gốc còn shallow-clone chia sẻ các biến đó giữa bản gốc và bản sao. Thông thường
shallow-clone là đủ và đơn giản nhưng khi sao chép những bản mẫu phức tạp ta nen dùng deep-clone để bản
sao và bản mẫu có thể độc lập với nhau và bản sao có các thành phần của nó là bản sao của bản mẫu.
• Khởi tạo bằng sao chép:
Có những trường hợp khi các thành phần bên trong của bản sao nên được khởi tạo sau khi bản sao được tạo ra.
Điều này xảy ra bởi vì các thành phần đó chưa được "khởi tạo" bằng phương pháp Clone(). Trong trường hợp
này việc khởi tạo nên được thực hiện bằng cách sử dụng thiết lập và cài đặt lại các phương thức của lớp mẫu
thử nghiệm hoặc sử dụng một phương thức khởi tạo lại các thông số của thành phần đó.
• Chú ý:
-Prototype Manager - thường là một Hashtable giữ đối tượng để nhân bản. Khi sử dụng nó, nguyên mẫu trở
thành một factory method có sử dụng sao chép thay vì khởi tạo.
- Deep Clones và Shallow Clones- khi chúng ta nhân bản đối tượng phức tạp, trong đó có các đối tượng khác,
chúng ta phải đảm bảo rằng chúng cũng được nhân bản. Ta có thể sao chép các đối tượng con (deep-clone) hoặc
chúng ta có thể cùng tham chiếu cho chúng, và chia sẻ chúng giữa các đối tượng sao chép.
- Khởi tạo nội bộ - có những tình huống nhất định khi đối tượng cần phải được khởi tạo sau khi bản sao được
tạo ra.
Singleton pattern
I. Định nghĩa:
II. Động lực
III. Trường hợp sử dụng
IV. Cấu trúc
V. Ưu nhược điểm
VI. Cách thực hiện
VII. Ví dụ

VIII. Pattern lien quan
I. Định nghĩa:
• Vấn đề đặt ra:
Ta hãy xem xét về một đối tượng quản lý tài nguyên trong các ứng dụng. Mỗi
ứng dụng có một bộ quản lý tài nguyên, nó cung cấp các điểm truy cập cho các đối
tượng khác trong ứng dụng. Các đối tượng (ta gọi là đối tượng khách) có thể thực hiện
lấy ra từ bộ quản lý tài nguyên những gì chúng cần và thay đổi giá trị nằm bên trong bộ
quản lý tài nguyên đó. Để truy cập vào bộ quản lý tài nguyên đối tượng khách cần phải
có một thể nghiệm của bộ quản lý tài nguyên, như vậy trong một ứng dụng sẽ có rất
nhiều thể nghiệm của bộ quản lý tài nguyên được tạo ra.
class ResourceManager
{
private: int x;
public: ResourceManager(){x =0; }
void SetX(int _x){ x = _x;}
void GetX(){ return x;}
}
class ClientObject1
{
public:
void DoSomething()
{
ResourceManager rm;
printf(“x = %d”,rm.GetX());
x = 100;
}
}
class ClientObject2
{
public:

void DoSomething()
{
ResourceManager rm;
printf(“x = %d”,rm.GetX());
x = 500;
}
}
Trong ví dụ trên hàm DoSomething() của ClientObject1 khi truy cập vào đối
tượng thuộc lớp ResourceManager sẽ in ra màn hình X = 0; và đặt vào đó x = 100;
Sau đó hàm Dosomething() của ClientObject2 khi truy cập vào đối tượng thuộc
lớp ResourceManager sẽ in ra màn hình X = 0 và đặt vào đó x = 500; Rõ ràng là tài
nguyên mà các đối tượng khách truy cập vào không thể hiện sự thống nhất lẫn nhau.
Điều mà lẽ ra khi giá trị x trong ResourceManager bị ClientObject1 thay đổi thì
ClientObject2 phải nhận biết được điều đó và in ra màn hình X=100 . Nhưng theo logic
cài đặt ở trên thì khi đối tượng thuộc lớp ClientObject1 truy cập đến ResourceManager
nó tạo ra một Instance và đặt thuộc tính x = 100; Đối tượng ClientObject2 truy cập đến
ResourceManager nó tạo ra một Instance và đặt thuộc tính x = 500. Hai Instance này
hoàn toàn độc lập nhau về vùng nhớ do đó mà tài nguyên x mà nó quản lý cũng là 2 tài
nguyên độc lập với nhau.Vấn đề đặt ra là phải tạo ra một bộ quản lý tài nguyên hoàn
hảo tạo ra mọi thể nghiệm giống nhau tại nhiều nơi nhiều thời điểm khác nhau trong
ứng dụng.Singleton cung cấp cho ta một cách giải quyết vấn đề này.
• Singleton đảm bảo rằng một class có duy nhất một thực thể (instance) hay đối tượng (object) và cung
cấp một cách thức truy nhập toàn cục để có thể từ bất kỳ đâu cũng lấy được instance duy nhất đó. Chúng
ta cũng gọi thực thể duy nhất đó là thực thể chính (solely) của class đó.
• Đối với một vài class thì việc chỉ có duy nhất một instance (object) là rất quan trọng. Mặc dù có rất
nhiều máy in trong hệ thống nhưng chỉ có một printer spooler. Cũng chỉ có một file system và một
windows manager. Cũng như trong thiết kế connection pool thì chỉ có một connection pool quản lý
nhiều connection.
Hộp thoại Find, một ví dụ cụ thể cho Singleton Pattern chỉ một hộp thoại duy nhất xuất hiện dù chọn menu
nhiều lần.

II. Động lực:
• Làm thế nào để chúng ta đảm bảo một class chỉ có một thể hiện duy nhất và dễ dàng truy nhập được?
Một object toàn cục có thể làm cho nó dễ dàng truy nhập được nhưng không ngăn cấm bạn tạo thêm
nhiều object khác.
• Một giải pháp tốt hơn là làm cho class đó tự bản thân nó có thể điều khiển được thực thể chính của nó.
Class đó có thể đảm bảo rằng không có thực thể khác được tạo ra (bằng cách chặn đứng các yêu cầu tạo
object mới) và cung cấp cách thức để truy nhập được thực thể chính của nó. Đó là singleton pattern.
III. Trường hợp sử dụng:
• Trong trường hợp chỉ cần một thể hiện duy nhất của một lớp.
• Khi thể hiện duy nhất khả mở thông qua việc kế thừa, người dùng có thể sử dụng thể hiện kế thừa đó mà
không cần thay đổi các đoạn mã của chương trình.
IV. Cấu trúc:
V. Ưu nhược điểm:
• Quản lý việc truy cập tốt hơn vì chỉ có một thể hiện đơn nhất.
• Cho phép cải tiến lại các tác vụ (operations) và các thể hiện (representation) do pattern có thể được kế
thừa và tùy biến lại thông qua một thể hiện của lớp con
• Quản lý số lượng thể hiện của một lớp, không nhất thiết chỉ có một thể hiện mà có số thể hiện xác định.
• Khả chuyển hơn so với việc dùng một lớp có thuộc tính là static, vì việc dùng lớp static chỉ có thể sử
dụng một thể hiện duy nhất, còn Singleton Pattern cho phép quản lý các thể hiện tốt hơn và tùy biến theo
điều kiện cụ thể.
VI. Cách thực hiện:
Thực hiện Singleton Pattern theo các bước sau:
• Định nghĩa một thuộc tính private và static trong lớp Singleton: instance.
• Định nghĩa tất cả các constructor thành protected hoặc private để người dùng không thể tạo thực
thể trực tiếp từ lớp.
• Định nghĩa một accessor public và static trong lớp: getInstance().
• Thực hiện "lazy initialization" (khởi tạo chậm, khởi tạo khi yêu cầu) trong getInstance(): trả về một
thể hiện mới hay một giá trị rỗng (null) tùy thuộc vào một biến boolean, biến này như một cờ hiệu
dùng báo xem lớp đó đã có thể hiện hay chưa.
• Clients chỉ dùng getInstance() để tạo đối tượng của lớp Singleton.

• Thừa kế cũng được hỗ trợ, nhưng không che (overridden) các phương thức static: lớp cơ sở phải
được khai báo là friend với lớp dẫn xuất (để truy xuất đến protected constructor).
 Thực hiện (Implement) của một mẫu singleton phải đáp ứng các thể hiện duy nhất và nguyên tắc truy
cập toàn cầu. Nó đòi hỏi một cơ chế để truy cập các thành viên lớp singleton mà không tạo ra một lớp
đối tượng và cơ chế để tồn giá trị của các thành viên lớp giữa các đối tượng lớp.Các mẫu singleton được
thực hiện bằng cách tạo ra một lớp với một phương thức để tạo ra một thể hiện mới của lớp nếu không
tồn tại. Nếu một thể hiện đã tồn tại, nó chỉ đơn giản trả về một tham chiếu đến đối tượng đó. Để chắc
chắn rằng đối tượng không thể khởi tạo bất kỳ cách nào khác, các Constructors được thực hiện bảo vệ
(make Protected) (bởi vì tái sử dụng và kiểm tra các đơn vị có thể cần truy cập vào các
Constructor ). Lưu ý những khác biệt giữa một trường hợp tĩnh đơn giản của một lớp và một singleton:
mặc dù singleton có thể được thực hiện như một thể tĩnh, nó cũng có thể được xây dựng uể oải (lazily
constructed), không đòi hỏi bộ nhớ hoặc các nguồn lực cho đến khi cần thiết. Một khác biệt đáng chú ý
là các lớp thành viên tĩnh không thể thực hiện một giao diện, trừ khi có giao diện đơn giản chỉ là một
dấu hiệu. Vì vậy, nếu lớp này phải thực hiện một hợp đồng biểu diễn bằng một giao diện, nó thực sự có
phải là một singleton.
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
};

Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance () {
if (_instance == 0) {
_instance = new Singleton;
}
return _instance;

}
 Singleton với Registry
class Singleton
{
public:
static void Register(const char* name, Singleton*);
static Singleton* Instance();
protected:
static Singleton* Lookup(const char* name);
private:
static Singleton* _instance;
Static List<NameSingletonPair>* _registry;
};
 Singleton với Registry (tt)
Singleton* Singleton::Instance ()
{
if (_instance == 0)
{
const char* singletonName = getenv("SINGLETON"); // user or environment supplies
this at startup
_instance = Lookup(singletonName); // Lookup returns 0 if there's no such singleton
}
return _instance;
}
 Singleton với Registry (tt)
class MySingleton : public Singleton
{
public MySingleton();
};
MySingleton::MySingleton()

{
//…
Singleton::Register(“MySingleton”, this);
}
Static MySingleton theSingleton;
VII. Ví dụ:
• Implement 1 class cho phép lấy duy nhất 1 đối tượng kết nối tới 1 database (chia sẻ kết nối – phù hợp
các ứng dụng web)
namespace SamplePatterns.Signleton
{
public class ClsConnection
{
// Instance of object
private static SqlConnection instance;
// Connection string
private string strConnectString = string.Empty;
// Initializes a new instance of the ClsConnection class.
/// <param name="strConnect">The STR connect.</param>
private ClsConnection(string strConnect)
{
this.strConnectString = strConnect;
}
// Gets the connection.
/// <param name="strConnect">The STR connect.</param>
/// <returns>SqlConnection</returns>
public static SqlConnection GetConnection(string strConnect)
{
if (instance == null)
{
instance = new SqlConnection(strConnect);

}
return instance;
}
}
}
• Ví dụ (tt)
public class Singleton {
private String _strName;
private static Singleton instance;
private Singleton(String name) {
_strName = name;
}
public static Singleton getInstance(String name) {
if (instance == null) {
instance = new Singleton(name);
}
return instance;
}
public void printName() {
System.out.println(this._strName);
VIII. Pattern lien quan:
• Abstract Factory: thường dùng để trả về các đối tượng duy nhất.
• Builder: dùng tạo một đối tượng phức tạp, trong đó Singleton được dùng để tạo một đối tượng truy xuất
tổng quát.
• Prototype: dùng để sao chép một đối tượng, hoặc tạo ra một đối tượng khác từ prototype (nguyên mẫu)
của nó, trong đó Singleton được dùng để chắc chắn chỉ có một prototype.

×