Những Design pattern thú vị trong Java | Part 3
Bài đăng này đã không được cập nhật trong 3 năm
Tiếp tục chủ đề về Design Pattern trong Java, thiết nghĩ rằng đây là nên tảng tốt cho sự phát triển skill sau này của mỗi developer nên mình tiếp tục bàn luận và đưa ra thêm một số điều mới mẻ hơn. 1. Prototype Pattern 2. Decorator Pattern 3. Memento Pattern Trong bài viết này mình để Memento Pattern ở nội dung cuối các bạn theo đọc đến cuối bài sẽ có những phần thú vị kèm theo đó, đừng bỏ sót nhé !
Prototype Pattern
Có một lúc nào đó trong quá trình phát triển ứng dụng của bạn mà cần phải liên tục tạo ra các object không ? Và bài toán lúc này là tiết kiệm tài nguyên một cách tối đa, performance sẽ thành vấn đề quan tâm lớn. Giải pháp tốt hơn việc khởi tạo ra một object mới đó là cloning từ một object trước đó.
Prototype Pattern : nó cho phép một object tạo ra những customized object mà không cần biết class của nó và bất kỳ chi tiết gì làm thế nào để tạo ra chúng. Mẫu thiết kế này sử dụng java cloning để copy một object.
Giải pháp:
Quy định cụ thể những loại object muốn tạo ra bằng cách sử dụng một prototypical instance tạo ra các object mới bằng cách sao chép chính nó.
Ví dụ: Một bài toán thực tế áp dụng để có cái nhìn rõ nét hơn các bạn nhé, giả sử có một Object được load từ database, chúng ta cần chỉnh sửa object này nhiều lần. Thực sự là không tốt nếu tạo một object mới bằng key-value sau đó load lại data một lần nữa. Giải quyết vấn đề này mình sẽ copy object từ object có trước sau đó thao tác data cần thiết.
Triển khai:
Employees.java
import java.util.ArrayList;
import java.util.List;
public class Employees implements Cloneable{
private List<String> empList;
public Employees(){
empList = new ArrayList<String>();
}
public Employees(List<String> list){
this.empList=list;
}
public void loadData(){
//read all employees from database and put into the list
empList.add("Pankaj");
empList.add("Raj");
empList.add("David");
empList.add("Lisa");
}
public List<String> getEmpList() {
return empList;
}
@Override
public Object clone() throws CloneNotSupportedException{
List<String> temp = new ArrayList<String>();
for(String s : this.getEmpList()){
temp.add(s);
}
return new Employees(temp);
}
}
***Nhận thấy method clone
dùng để copy nguyên list Employees.***Tiếp theo chúng ta sẽ viết class áp dụng mẫu thiết kế này:
PrototypePatternTest.java
import java.util.List;
import com.journaldev.design.prototype.Employees;
public class PrototypePatternTest {
public static void main(String[] args) throws CloneNotSupportedException {
Employees emps = new Employees();
emps.loadData();
//Use the clone method to get the Employee object
Employees empsNew = (Employees) emps.clone();
Employees empsNew1 = (Employees) emps.clone();
List<String> list = empsNew.getEmpList();
list.add("John");
List<String> list1 = empsNew1.getEmpList();
list1.remove("Pankaj");
System.out.println("emps List: "+emps.getEmpList());
System.out.println("empsNew List: "+list);
System.out.println("empsNew1 List: "+list1);
}
}
Output:
emps HashMap: [Pankaj, Raj, David, Lisa]
empsNew HashMap: [Pankaj, Raj, David, Lisa, John]
empsNew1 HashMap: [Raj, David, Lisa]
Decorator Pattern
Được dùng để thay đổi chức năng của Object lúc runtime mà không làm ảnh hưởng tới cấu trúc của nó. Vì nằm trong nhóm structural design pattern nên cách triển khai sẽ có phần giống như một số mẫu thiết kế khác mà chúng ta đã làm là dùng abstract class hoặc interface để triển khai.
Vì khái niệm khá đơn giản nên mình sẽ đi vào triển khai luôn qua 1 ví dụ từ hình ảnh trên nhé. Giả sử chúng ta muốn tạo ra những loại xe khác nhau, đầu tiên sẽ có interface Car
sau đó có Basic Car
để có những đặc tính cơ sở của một chiếc xe hơi, tiếp theo là những loại xe chúng ta muốn có. Mọi chuyện thật dễ dàng nếu như chúng ta chỉ làm một lần và không có một yêu cầu thay đổi nào.
Cho đến khi mong muốn từ phía khách hàng vừa muốn một chiếc xe mang phong cách sport lại muốn nó thật sang trọng, rồi đến khi quy trình lắp dáp muốn rằng phải đưa tính sang trọng vào đầu tiên sau đó mới là sport , bla bla... Khi đấy bạn biết rằng đã phức tạp hơn yêu cầu ban đầu rất nhiều phải không nào
Tất nhiên rồi, sẽ có cách để làm và câu trả lời các bạn cũng biết đó là Decorator Pattern
Triển khai: Sơ đồ tổng thể
Car.java
component chung cho những loại xe chúng ta cần có.
public interface Car {
public void assemble();
}
BasicCar.java
public class BasicCar implements Car {
@Override
public void assemble() {
System.out.print("Basic Car.");
}
}
CarDecorator.java
public class CarDecorator implements Car {
protected Car car;
public CarDecorator(Car c){
this.car=c;
}
@Override
public void assemble() {
this.car.assemble();
}
}
SportsCar.java
public class SportsCar extends CarDecorator {
public SportsCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print(" Adding features of Sports Car.");
}
}
LuxuryCar.java
public class LuxuryCar extends CarDecorator {
public LuxuryCar(Car c) {
super(c);
}
@Override
public void assemble(){
super.assemble();
System.out.print(" Adding features of Luxury Car.");
}
}
DecoratorPatternTest.java
chương trình chạy áp dụng
public class DecoratorPatternTest {
public static void main(String[] args) {
Car sportsCar = new SportsCar(new BasicCar());
sportsCar.assemble();
System.out.println("\n*****");
Car sportsLuxuryCar = new SportsCar(new LuxuryCar(new BasicCar()));
sportsLuxuryCar.assemble();
}
}
Output:
Basic Car. Adding features of Sports Car.
*****
Basic Car. Adding features of Luxury Car. Adding features of Sports Car.
Memento Pattern
Sử dụng khi muốn save trạng thái của một object sau đó có thể dễ dàng khôi phục lại, bảo vệ tính toàn vẹn của object.
Giải pháp:
Memento sẽ thực thi với 2 object là Originator
và Caretaker
Originator khởi tạo đối tượng cần được save và restore trạng thái. Trong đó có một Memento class inner là private nên không thể truy cập từ class bên ngoài.
Caretaker là class helper lưu trữ và khôi phục lại Originator thông qua Memento object.
Triển khai:
Một ví dụ mà rất hay gặp với Text là save lại trạng thái sau khi input và undo lại trạng thái trước lúc save.
FileWriterUtil.java
Originator object - tạo ra method save và undo , có Memento inner class.
public class FileWriterUtil {
private String fileName;
private StringBuilder content;
public FileWriterUtil(String file){
this.fileName=file;
this.content=new StringBuilder();
}
@Override
public String toString(){
return this.content.toString();
}
public void write(String str){
content.append(str);
}
public Memento save(){
return new Memento(this.fileName,this.content);
}
public void undoToLastSave(Object obj){
Memento memento = (Memento) obj;
this.fileName= memento.fileName;
this.content=memento.content;
}
private class Memento{
private String fileName;
private StringBuilder content;
public Memento(String file, StringBuilder content){
this.fileName=file;
//notice the deep copy so that Memento and FileWriterUtil content variables don't refer to same object
this.content=new StringBuilder(content);
}
}
}
FileWriterCaretaker.java
Caretaker object - thực thi save - undo object mà không thay đổi dữ liệu và cũng không biết tới cấu trúc của object đó là gì.
public class FileWriterCaretaker {
private Object obj;
public void save(FileWriterUtil fileWriter){
this.obj=fileWriter.save();
}
public void undo(FileWriterUtil fileWriter){
fileWriter.undoToLastSave(obj);
}
}
FileWriterClient.java
chương trình chạy
public class FileWriterClient {
public static void main(String[] args) {
FileWriterCaretaker caretaker = new FileWriterCaretaker();
FileWriterUtil fileWriter = new FileWriterUtil("data.txt");
fileWriter.write("First Set of Data\n");
System.out.println(fileWriter+"\n\n");
// lets save the file
caretaker.save(fileWriter);
//now write something else
fileWriter.write("Second Set of Data\n");
//checking file contents
System.out.println(fileWriter+"\n\n");
//lets undo to last save
caretaker.undo(fileWriter);
//checking file content again
System.out.println(fileWriter+"\n\n");
}
}
Ouput:
First Set of Data
First Set of Data
Second Set of Data
First Set of Data
Tổng kết:
Trên đây là những Design Pattern thú vị mà mình muốn gửi tới các bạn yêu thích lập trình hoặc đang trong quá trình học bước đầu để nâng cao kĩ năng, mong rằng bạn sẽ tìm thấy điều gì đó hữu ích từ bài viết này. ^_^ Chúc các bạn ngày càng pro hơn nhé !
All rights reserved