AAAAAssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Bài viết tham khảo từ Design pattern for dummies strategy Giới thiệu design pattern Design Pattern kỹ thuật lập trình hướng đối tượng, quan trọng lập trình viên muốn giỏi phải biết Được sử dụng thường xuyên ngơn ngữ OOP Nó cung cấp cho bạn "mẫu thiết kế", giải pháp để giải vấn đề chung, thường gặp lập trình Các vấn đề mà bạn gặp phải bạn tự nghĩ cách giải chưa phải tối ưu Design Pattern giúp bạn giải vấn đề cách tối ưu nhất, cung cấp cho bạn giải pháp lập trình OOP Trong Design Pattern có nhóm bao gồm: • Creational Pattern (nhóm khởi tạo) gồm: Abstract Factory, Factory Method, Singleton, Builder, Prototype Nó giúp bạn việt khởi tạo đối tượng, bạn biết để khởi tạo bạn phải sử dụng từ khóa new, nhóm Creational Pattern sử dụng số thủ thuật để khởi tạo đối tượng mà bạn khơng nhìn thấy từ khóa • Structural Pattern (nhóm cấu trúc) gồm: Adapter, Bridge, Composite, Decorator, Facade, Proxy Flyweight Nó dùng để thiết lập, định nghĩa quan hệ đối tượng • Behavioral Pattern gồm: Interpreter, Template Method, Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy Visitor Nhóm dùng thực hành vi đối tượng Tìm hiểu strategy pattern Strategy pattern gì? Strategy pattern (mẫu chiến lược): hiểu cách đơn giản mẫu thiết kế giúp bạn trừu tượng hóa hành vi (behavior, method, function) đối tượng cách đưa cài đặt vào lớp khác Bài toán thực tế Bạn nhận hợp đồng thiết kế ô tô Có nhiều mẫu ô tô để bạn làm Bạn nghĩ đến việc sử dụng OOP vào thiết kế tơ Đầu tiên, bạn tạo lớp sở có tên Vehicle với phương thức có tên go, phương thức xuất lên dòng chữ Now I’m driving abstract class Vehicle { public function go() { echo "Now I'm driving"; } } Sau đó, bạn tạo tiếp lớp lớp StreetRacer thừa kế từ lớp Vehicle sau: public class StreetRacer extends Vehicle { } Tới đây, chương trình bạn hồn tồn tốt đẹp Bạn khai báo đối tượng StreetRacer gọi tới hàm go: $streetRacer = new StreetRacer; $streetRacer->go(); Và kết trả là: Now I’m driving Kết hồn tồn xác Nhưng sau đó, bạn nhận thêm hợp đồng sản xuất máy bay trực thăng Helicopter Bạn nhận thấy máy bay trực thăng phương tiện vận chuyển Vì bạn định tạo lớp Helicopter thừa kế từ lớp Vehicle: public class Helicopter extends Vehicle { } Nhưng bạn nhận vấn đề sử dụng hàm go cho Helicopter, kết trả khơng xác Now I’m driving? Tại máy bay lại driving? Máy bay phải bay nhỉ? Nhưng khơng phải vấn đề lớn Bạn định override hàm go cho lớp Helicopter sau: public class StreetRacer extends Vehicle { public function go() { echo "Now I'm flying"; } } Có vẻ vấn đề giải Giờ máy bay flying Nhưng vài tuần sau, khách hàng yêu cầu phải chuyển từ Now, I’m flying sang Now, I’m flying 200mph nhiều thay đổi Có vấn đề nảy sinh Đó chưa phải vấn đề lớn, bạn phải xử lý cơng việc cách thường xun, việc phải chỉnh sửa lớp trờ thành vấn đề bảo trì nghiêm trọng Bạn bắt đầu suy nghĩ Có lẽ thừa kế khơng phải cách giải tốt cho tình Nơi mà bạn cần phải thay đổi chức thường xuyên lớp Và có nhiều lớp kế thừa liên quan, chúng cần bảo trì có thay đổi đó, bạn phải cập phương thức go nhiều lần Vấn đề bạn cần phải giải để tránh việc thay đổi lớp con, không, bạn phải thay đổi code nhiều file để cập nhật yêu cầu khách hàng Có lẽ bạn cần cách khác tốt để xử lý vấn đề thay sử dụng thừa kế Nắm vững thay đổi từ “is-a” sang “has-a” Với kế thừa, lớp sở lớp có mối quan hệ “is-a” Ví dụ , lớp Helicopter có quan hệ “is-a” với lớp Vehicle, điều có nghĩa Helicopter thừa kế thứ từ Vehicle, bạn phải chỉnh sửa phương thức này, bạn gặp phải vấn đề bảo trì tương lai Lớp sở xử lý phương thức theo cách, lớp kế thừa lại thay đổi nó, lớp lại thay đổi thêm lần Và cuối bạn có lơ lốc biến thể phương thức qua lớp Mặc khác, bạn trích đoạn code dễ thay đổi đóng gói chúng vào đối tượng, bạn sử dụng đối tượng cần Nhiệm vụ xử lý đối tượng Bạn không để việc xử lý lây lan qua lớp Làm cho phép bạn chỉnh sửa code bạn việc tạo “sự kết hợp” composites đối tượng Với composites “kết hợp” này, bạn dễ dàng chọn sử dụng đối tượng cần thiết Một quan hệ “has-a” tạo Một đối tượng StreetRacer có “has-a” cách để di chuyển (go), đóng gói vào đối tượng Một Helicopter có cách riêng để di chuyển (go), đóng gói vào đối tượng Từng đối tượng thực hành động riêng Một đối tượng, nhiệm vụ thường có ý nghĩa việc kế thừa lớp, tạo hàng tá lớp Nói cách khác, xếp lại dựa nhiệm vụ lớp, kế thừa Cách tạo thuật toán: Đầu tiên, bạn tạo giao diện interface cho phương thức go sau: public interface goAlgorithm { public function go(); } Trong giao diện goAlgorithm có phương thức go Sau bạn tạo lớp cụ thể cho thuật toán Đầu tiên lớp GoByDrivingAlgorithm, thực sau: class GoByDrivingAlgorithm implements goAlgorithm { public function go() { echo "Now I'm driving"; } } Ngoài ra, lớp GoByFlyingAlgorithm định nghĩa cho phương tiện bay class GoByFlyingAlgorithm implements goAlgorithm { public function go() { echo "Now I'm flying"; } } Và chí bạn định nghĩa phương thức go cho máy bay phản lực: class GoByFlyingFastAlgorithm implements goAlgorithm { public function go() { echo "Now I'm flying very fast"; } } Vậy bạn tách phương thức xử lý khỏi lớp cụ thể StreetRacer hay Helicopter Bây bạn đưa thuật toán vào sử dụng Sử dụng thuật toán Sau bạn tạo đối tượng từ thuật toán, bạn cần phải lưu trữ đối tượng Vì thêm vào lớp sở Vehicle, phương thức SetGoAlgorithm Phương thức định thuật toán mà bạn muốn sử dụng abstract class Vehicle { private $goAlgorithm public function setGoAlgorithm(GoAlgorithm $ goAlgorithm ) { $goAlgorithm = $ goAlgorithm; } } Bây bạn muốn sử dụng thuật tốn cụ thể lớp kế thừa, tất việc cần làm gọi phương thức setGoAlgorithm với đối tượng thuật toán Phương thức go lớp Vehicle có chút thay đổi public function go() { $goAlgorithm->go(); } Bây tất bạn cần làm chọn thuật toán cho phương tiện Ví dụ StreetRacer GoByDrivingAlgorithm, cho Helicopter GoByFlyingAlgorithm, Tổng kết Vậy vừa tìm hiểu Strategy pattern (mẫu chiến lược).Ý nghĩa thực mẫu chiến lược bạn tách rời phần xử lý chức cụ thể khỏi đối tượng bạn Sau tạo tập hợp thuật toán để xử lý chức lựa chọn thuật tốn mà bạn thấy đắn thực thi chương trình Mẫu thiết kế thường sử dụng để thay cho kế thừa, bạn muốn chấm dứt việc theo dõi chỉnh sửa chức qua nhiều lớp Strategy pattern cho thấy đơi áp dụng tốt cho mục đích hướng chức Và đặc biệt quan trọng bạn muốn thực cơng việc nâng cấp, bảo trì cho đoạn mã dễ thay đổi bạn cách riêng biệt với tồn mã chương trình, bạn muốn thay đổi thuật toán sử dụng chương trình thực thi Bạn nên sử dụng strategy pattern cho trường hợp sau: • Bạn có đoạn code dễ thay đổi, bạn tách chúng khỏi chương trình để dễ dàng bảo trì • Bạn muốn tránh rắc rối, phải thực chức qua q nhiều lớp • Bạn muốn thay đổi thuật toán sử dụng chạy chương trình Template Method Định nghĩa Xác định xương thuật tốn hoạt động, trì hỗn số bước cho lớp Phương thức mẫu cho phép lớp xác định lại bước định thuật tốn mà khơng thay đổi cấu trúc thuật tốn Tần suất sử dụng: Trung bình Sơ đồ lớp UML Các lớp đối tượng tham gia: AbstractClass (DataObject): o định nghĩa hoạt động nguyên thủy trừu tượng mà lớp cụ thể xác định để thực bước thuật toán o thực phương thức mẫu xác định xương thuật toán Phương thức mẫu gọi hoạt động nguyên thủy hoạt động xác định AbstractClass hoạt động đối tượng khác ConcreteClass (CustomerDataObject): thực hoạt động nguyên thủy thực bước cụ thể lớp thuật toán Mã cấu trúc C # Mã cấu trúc thể phương thức Mẫu cung cấp chuỗi phương thức gọi xương Một nhiều bước chuyển đến lớp thực bước mà khơng thay đổi trình tự gọi tổng thể using System; namespace DoFactory.GangOfFour.Template.Structural { /// /// MainApp startup class for Real-World /// Template Design Pattern /// class MainApp 10 { 11 /// 12 /// Entry point into console application 13 /// 14 static void Main() 15 { 16 AbstractClass aA = new ConcreteClassA(); 17 aA.TemplateMethod(); 18 19 AbstractClass aB = new ConcreteClassB(); aB.TemplateMethod(); 20 21 22 // Wait for user 23 Console.ReadKey(); } 24 25 } 26 27 /// 28 /// The 'AbstractClass' abstract class 29 /// 30 abstract class AbstractClass 31 { 32 public abstract void PrimitiveOperation1(); 33 public abstract void PrimitiveOperation2(); 34 35 // The "Template method" 36 public void TemplateMethod() 37 { 38 PrimitiveOperation1(); 39 PrimitiveOperation2(); 40 Console.WriteLine(""); } 41 42 } 43 44 /// 45 /// A 'ConcreteClass' class 46 /// 47 class ConcreteClassA : AbstractClass 48 { 49 public override void PrimitiveOperation1() 50 { Console.WriteLine("ConcreteClassA.PrimitiveOperation1()"); 51 52 } 53 public override void PrimitiveOperation2() 54 { Console.WriteLine("ConcreteClassA.PrimitiveOperation2()"); 55 } 56 57 } 58 59 /// 60 /// A 'ConcreteClass' class 61 /// 62 class ConcreteClassB : AbstractClass 63 { 64 public override void PrimitiveOperation1() 65 { Console.WriteLine("ConcreteClassB.PrimitiveOperation1()"); 66 67 } 68 public override void PrimitiveOperation2() 69 { Console.WriteLine("ConcreteClassB.PrimitiveOperation2()"); 70 } 71 } 72 } 73 74 75 76 Output ConcreteClassA.PrimitiveOperation1() ConcreteClassA.PrimitiveOperation2() Mã giới thực C # Mã giới thực biểu thị phương thức Mẫu có tên Run () cung cấp chuỗi phương thức gọi xương Việc thực bước chuyển đến lớp CustomerDataObject, thực phương thức Kết nối, Chọn, Xử lý Ngắt kết nối using System; using System.Data; using System.Data.OleDb; namespace DoFactory.GangOfFour.Template.RealWorld { /// /// MainApp startup class for Real-World /// Template Design Pattern 10 /// 11 class MainApp 12 { 13 /// 14 /// Entry point into console application 15 /// 16 static void Main() { 17 18 DataAccessObject daoCategories = new Categories(); 19 daoCategories.Run(); 20 21 DataAccessObject daoProducts = new Products(); 22 daoProducts.Run(); 23 24 // Wait for user 25 Console.ReadKey(); } 26 27 } 28 29 /// 30 /// The 'AbstractClass' abstract class 31 /// 32 abstract class DataAccessObject 33 { 34 protected string connectionString; 35 protected DataSet dataSet; 36 37 public virtual void Connect() 38 { 39 // Make sure mdb is available to app 40 connectionString = 41 "provider=Microsoft.JET.OLEDB.4.0; " + 42 "data source= \\ \\ \\db1.mdb"; 43 } 44 45 public abstract void Select(); 46 public abstract void Process(); 47 48 public virtual void Disconnect() 49 { connectionString = ""; 50 51 } 52 53 // The 'Template Method' 54 public void Run() 55 { 56 Connect(); 57 Select(); 58 Process(); Disconnect(); 59 } 60 61 } 62 63 /// 64 /// A 'ConcreteClass' class 65 /// 66 class Categories : DataAccessObject 67 { 68 public override void Select() 69 { 70 string sql = "select CategoryName from Categories"; 71 OleDbDataAdapter dataAdapter = new OleDbDataAdapter( sql, connectionString); 72 73 74 dataSet = new DataSet(); 75 dataAdapter.Fill(dataSet, "Categories"); 76 } 77 78 public override void Process() 79 { Console.WriteLine("Categories "); 80 81 82 DataTable dataTable = dataSet.Tables["Categories"]; 83 foreach (DataRow row in dataTable.Rows) 84 { Console.WriteLine(row["CategoryName"]); 85 86 } 87 Console.WriteLine(); } 88 89 } 90 91 /// 92 /// A 'ConcreteClass' class 93 /// 94 class Products : DataAccessObject 95 { 96 public override void Select() 97 { 98 string sql = "select ProductName from Products"; 99 OleDbDataAdapter dataAdapter = new OleDbDataAdapter( 100 sql, connectionString); 101 102 dataSet = new DataSet(); 103 dataAdapter.Fill(dataSet, "Products"); } 104 105 106 public override void Process() 107 { 108 Console.WriteLine("Products "); 109 DataTable dataTable = dataSet.Tables["Products"]; 110 foreach (DataRow row in dataTable.Rows) 111 { Console.WriteLine(row["ProductName"]); 112 113 } 114 Console.WriteLine(); } 115 } 116 117 118 119 120 } Output Categories -Beverages Condiments Confections Dairy Products Grains/Cereals Meat/Poultry Produce Seafood Products -Chai Chang Aniseed Syrup Chef Anton's Cajun Seasoning Chef Anton's Gumbo Mix Grandma's Boysenberry Spread Uncle Bob's Organic Dried Pears Northwoods Cranberry Sauce Mishi Kobe Niku Visitor Patern Trong thiết kế hướng đối tượng, Visitor mẩu thiết kế(Design Patterns) cho phép định nghĩa thao tác(operations) tập hợp đối tượng (objects) không đồng (về kiểu) mà không làm thay đổi định nghĩa lớp(classes) đối tượng Để đạt điều này, mẩu thiết kế visitor ta định nghĩa thao tác lớp tách biệt gọi lớp visitors, lớp cho phép tách rời thao tác với đối tượng mà tác động đến Với thao tác thêm vào, lớp visitor tương ứng tạo Trong mẫu Khách truy cập, sử dụng lớp khách truy cập thay đổi thuật toán thực thi lớp phần tử Bằng cách này, thuật toán thực phần tử thay đổi khách truy cập thay đổi Mẫu thuộc danh mục mẫu hành vi Theo mẫu, đối tượng phần tử phải chấp nhận đối tượng khách truy cập để đối tượng khách truy cập xử lý thao tác đối tượng phần tử Thực Chúng tạo giao diện ComputerPart xác định chấp nhận Bàn phím , Chuột , Màn hình Máy tính lớp cụ thể thực giao diện ComputerPart Chúng định nghĩa giao diện khác ComputerPartVisitor xác định hoạt động lớp khách truy cập Máy tính sử dụng khách truy cập cụ thể để làm hành động tương ứng VisitorPatternDemo , lớp demo chúng tơi, sử dụng máy tính ComputerPartVisitor lớp học để chứng minh sử dụng mơ hình khách truy cập Bước Xác định giao diện để đại diện cho yếu tố Máy tínhPart.java public interface ComputerPart { public void accept(ComputerPartVisitor computerPartVisitor); } Bước Tạo lớp bê tơng mở rộng lớp Bàn phím.java public class Keyboard implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } Monitor.java public class Monitor implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } Chuột.java public class Mouse implements ComputerPart { @Override public void accept(ComputerPartVisitor computerPartVisitor) { computerPartVisitor.visit(this); } } Máy tính.java public class Computer implements ComputerPart { ComputerPart[] parts; public Computer(){ parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()}; } @Override public void accept(ComputerPartVisitor computerPartVisitor) { for (int i = 0; i < parts.length; i++) { parts[i].accept(computerPartVisitor); } computerPartVisitor.visit(this); } } Bước Xác định giao diện để đại diện cho khách truy cập Máy tínhPartVisitor.java public interface ComputerPartVisitor { public void visit(Computer computer); public void visit(Mouse mouse); public void visit(Keyboard keyboard); public void visit(Monitor monitor); } Bước Tạo khách truy cập cụ thể thực lớp Máy tínhPartDisplayVisitor.java public class ComputerPartDisplayVisitor implements ComputerPartVisitor { @Override public void visit(Computer computer) { System.out.println("Displaying Computer."); } @Override public void visit(Mouse mouse) { System.out.println("Displaying Mouse."); } @Override public void visit(Keyboard keyboard) { System.out.println("Displaying Keyboard."); } @Override public void visit(Monitor monitor) { System.out.println("Displaying Monitor."); } } Bước Sử dụng ComputerPartDisplayVisitor để hiển thị phần Máy tính VisitorPotypeDemo.java public class VisitorPatternDemo { public static void main(String[] args) { ComputerPart computer = new Computer(); computer.accept(new ComputerPartDisplayVisitor()); } } Bước Xác nhận đầu Displaying Mouse Displaying Keyboard Displaying Monitor Displaying Computer