Prototype pattern – một trong những pattern phổ biến nhất

Bài viết được sự cho phép của tác giả Kiên Nguyễn

Quay lại chuỗi bài viết về Design Pattern ở Kieblog, hôm nay vẫn tiếp tục là Creational Pattern (pattern khởi tạo), cụ thể là Prototype pattern.

Prototype pattern – một trong những pattern phổ biến nhất
Nguồn ảnh / Source: refactoring.guru

Trong cái pattern dễ nhất là Singleton Pattern, nắm vững. Sau đó quay qua tìm hiểu cái Prototype, pattern này cũng khá phổ biến, được sử dụng rất nhiều trong thực tế.

Bắt đầu vào tìm hiểu ngay thôi!.

  Design pattern là gì? Tại sao nên sử dụng Design pattern?
  Giải thích Flux Pattern theo phong cách John Wick

1. Prototype pattern là gì?

Bắt đầu với cái định nghĩa khá là lèo nhèo của Prototype pattern.

Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes.

Prototype là pattern khởi tạo cho phép copy một object sẵn có nhưng không làm đoạn code mới độc lập với class đã có

Vãi linh hồn cái định nghĩa. Loằng ngoằng, lèo ngèo khó hiểu. Thực ra thì diễn giải đơn giản chỉ như này.

Prototype pattern sẽ tạo ra một object mới bằng cách copy, tuy nhiên tương tự cách hiểu về kế thừa. Không cần phải copy ra đoạn source code mới, chỉ cần extends lại cái cũ là đã ok rồi

Prototype pattern – một trong những pattern phổ biến nhất
Thực tế lúc code không phải cái gì cũng nên copy ra. Không khéo thì từ “bay được” thành “đốt được”. Nguồn ảnh / Source: refactoring.guru

2. Giải pháp của prototype pattern

Về bản chất thì prototype sẽ định nghĩa ra các interface chung nhất (common interface) cho tất cả các object có hỗ trợ cloning. Những method này cho phép clone object. Mà thực hiện clone thì không cần copy source của object cũ.

Thông thường khi implement Prototype pattern, mỗi interface sẽ có duy nhất một phương thức clone.

Một object hỗ trợ cloning thì gọi là prototype. Prototype mang ý nghĩa cái chung nhất, cái common có thể cho những cái khác clone

Prototype pattern – một trong những pattern phổ biến nhất
Với prototype, có thể loại bỏ hoặc thay thế việc sử dụng quá nhiều subclassing. Nguồn ảnh / Source: refactoring.guru

3. Cấu trúc

Phần một (1) là phần khai báo iterface, method hỗ trợ cloning. Trong nhiều trường hợp, một interface chỉ có một method clone duy nhất.

Phần hai (2) là Concrete Prototype class, phần này sẽ implement cho method clone được khai báo ở interface. Cũng có thể handle một số case về các object không được clone.

Phần ba (3) là phần client có thể copy bất kì object nào thông qua phương thức clone

4. Hiện thực như nào?

Giả sử ta đang viết 3 class cho phần vẽ hình. Bao gồm hình tròn (Circle)

public class Circle extends Shape {
public int radius;

public Circle() {
}

public Circle(Circle target) {
super(target);
if (target != null) {
this.radius = target.radius;
}
}
// Method clone của Circle, trả về một object mới.
// Từ khóa this mang ý nghĩa state của tất cả các variable sẽ được giữ nguyên
@Override
public Shape clone() {
return new Circle(this);
}

@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Circle) || !super.equals(object2)) return false;
Circle shape2 = (Circle) object2;
return shape2.radius == radius;
}
}

Và hình chữ nhật Rectangle

public class Rectangle extends Shape {
public int width;
public int height;

public Rectangle() {
}

public Rectangle(Rectangle target) {
super(target);
if (target != null) {
this.width = target.width;
this.height = target.height;
}
}

@Override
public Shape clone() {
return new Rectangle(this);
}

@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Rectangle) || !super.equals(object2)) return false;
Rectangle shape2 = (Rectangle) object2;
return shape2.width == width && shape2.height == height;
}
}

Abstract class của 2 thằng này là Shape. Tất nhiên, tròn hay vuông thì đều là một cái hình mà thôi

public abstract class Shape {
public int x;
public int y;
public String color;

public Shape() {
}

public Shape(Shape target) {
if (target != null) {
this.x = target.x;
this.y = target.y;
this.color = target.color;
}
}
// Shape cũng có method abstract riêng của nó
public abstract Shape clone();

@Override
public boolean equals(Object object2) {
if (!(object2 instanceof Shape)) return false;
Shape shape2 = (Shape) object2;
return shape2.x == x && shape2.y == y && Objects.equals(shape2.color, color);
}
}

Khi sử dụng, phương thức clone sẽ sử dụng như sau

public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
List<Shape> shapesCopy = new ArrayList<>();

Circle circle = new Circle();
circle.x = 10;
circle.y = 20;
circle.radius = 15;
circle.color = "red";
shapes.add(circle);

// Dễ dàng có thể tạo ra một object clone mới, không duplicate source, source đẹp rõ ràng
Circle anotherCircle = (Circle) circle.clone();
shapes.add(anotherCircle);

Rectangle rectangle = new Rectangle();
rectangle.width = 10;
rectangle.height = 20;
rectangle.color = "blue";
shapes.add(rectangle);

cloneAndCompare(shapes, shapesCopy);
}

5. Ưu nhược điểm

Đầu tiên, rõ ràng là sử dụng Prototype pattern không làm cho code độc lập với cái class mà nó clone. Chắc chắn.

Thứ hai, dùng prototype pattern giúp clone object một cách nhanh chóng, không bị duplicate code. Trường hợp object phức tạp, chứa rối rắm nhiều thứ, Prototype là lựa chọn tuyệt vời.

Tuy nhiên, nếu object có tham chiếu hoặc kế thừa từ các object khác. Clone thì dễ, nhưng không biết mức độ ảnh hưởng tới đâu. Nếu object clone có dây mơ rễ má nhiều chỗ -> rất dễ trở thành một đám tơ vò.

6. Tham khảo

Ở phía bên Javascript có thể tham khảo bài viết về prototype cũng khá hay.

Thanks for read – Have a great day – Happy coding!

Bài viết gốc được đăng tải tại kieblog.vn

Có thể bạn quan tâm:

Xem thêm Việc làm Developer hấp dẫn trên TopDev