1. Trang chủ
  2. » Luận Văn - Báo Cáo

(LUẬN VĂN THẠC SĨ) Phát triển mẫu thiết kế phần mềm và ứng dụng

113 2 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 113
Dung lượng 1,64 MB

Cấu trúc

  • 2.1.1. Nguyên lý đóng mở (56)
  • 2.1.2. Nguyên lý Nghịch đảo phụ thuộc (59)
  • 2.1.3. Nguyên lý Thay thế Liskov (0)
  • 2.1.4. Nguyên lý Phân tách giao diện (62)
  • 2.2. Các nguyên lý xây dựng mẫu thiết kế phần mềm (0)
    • 2.2.1. Tình huống phát sinh mẫu thiết kế là từ nguyên lý thiết kế và thực tiễn 58 2.2.2. Mẫu thiết kế là giải pháp cụ thể (65)
    • 2.2.3. Mục tiêu thiết kế mẫu là hướng tới người dùng (66)
    • 2.2.4. Các thuật ngữ trong mẫu thiết kế điển hình và gợi vấn đề (0)
    • 2.2.5. Lựa chọn các tình huống áp dụng điển hình (67)
  • 2.3. Tổng kết chương (68)
  • CHƯƠNG 3. PHƯƠNG PHÁP THIẾT KẾ MẪU PHẦN MỀM (0)
    • 3.1. Các thành phần cơ bản của mẫu thiết kế (69)
      • 3.1.1. Tên khuôn mẫu (69)
      • 3.1.2. Vấn đề (69)
      • 3.1.3. Giải pháp (69)
      • 3.1.4. Hệ quả (70)
    • 3.2. Các định dạng mẫu thiết kế (70)
      • 3.2.1. Định dạng Alexandrian (70)
      • 3.2.2. Định dạng GOF (71)
      • 3.2.3. Định dạng Porland (71)
      • 3.2.4. Định dạng Coplien (72)
      • 3.2.5. Định dạng POSA (72)
      • 3.2.6. Định dạng P of EAA (72)
    • 3.3. Định dạng GoF của mẫu thiết kế (72)
    • 3.4. Việc lựa chọn định dạng mẫu thiết kế (74)
    • 3.5. Tổng kết chương (74)
  • CHƯƠNG 4. PHÁT TRIỂN MẪU THIẾT KẾ VÀ ỨNG DỤNG (76)
    • 4.1. Các mẫu thiết kế đối tƣợng (76)
      • 4.1.1. Mẫu đối tƣợng trống (0)
      • 4.1.2. Mẫu đối tƣợng vai trò (0)
      • 4.1.3. Mẫu đối tƣợng mở rộng (0)
      • 4.1.4. Mẫu đối tƣợng kiểu (0)
    • 4.2. Mẫu thiết kế Ajax (103)
    • 4.3. Ứng dụng mẫu thiết kế trong thiết kế khung cho tầng truy cập dữ liệu (108)
      • 4.3.1. Đặt vấn đề (108)
      • 4.3.2. Mô hình 3 tầng (108)
      • 4.3.3. Cài đặt mô hình khung cho tầng truy cập dữ liệu (109)
    • 4.4. Tổng kết chương (0)
  • KẾT LUẬN (0)
  • TÀI LIỆU THAM KHẢO (0)

Nội dung

Nguyên lý đóng mở

Các thực thể phần mềm nhƣ các lớp, module, hàm nên đƣợc xây dựng theo hướng mở cho việc mở rộng nhưng đóng đối với việc sửa đổi

Trong một dự án phần mềm, các thực thể không tách biệt mà có sự gắn kết và tương tác lẫn nhau Thiết kế phần mềm cần chú trọng đến khả năng bảo trì và mở rộng để đáp ứng các nhu cầu thay đổi Tuy nhiên, sự gắn kết chặt chẽ giữa các thực thể có thể gây khó khăn khi nâng cấp, vì khi một thực thể được nâng cấp, các thực thể liên quan cũng cần được điều chỉnh Với sự phát triển nhanh chóng của công nghệ thông tin, việc nâng cấp và mở rộng phần mềm là điều không thể tránh khỏi.

Để dễ dàng bảo trì và nâng cấp phần mềm, trong quá trình phân tích thiết kế, các kỹ sư phần mềm cần tuân theo nguyên lý đóng mở Điều này có nghĩa là kiến trúc phần mềm cần được xây dựng để có khả năng mở rộng và thay đổi mà không ảnh hưởng đến mã nguồn của các thực thể đã tồn tại Ví dụ, nếu chương trình ban đầu chỉ vẽ các đối tượng hình học như đường thẳng và hình chữ nhật, việc sử dụng phương pháp lập trình cấu trúc có thể được minh họa qua đoạn mã: public enum ShapeType{ LINE, RECTANGLE } public abstract class Shape { public abstract ShapeType getType();

} public class Line: Shape{ public override ShapeType getType(){ return ShapeType.LINE;

// Các lệnh vẽ đoạn thẳng }

} public class Rectangle: Shape{ public override ShapeType getType(){ return ShapeType.RECTANGLE;

// Các lệnh vẽ hình chữ nhật }

} public void draw(ArrayList shapeList){

Rectangle objRectangle; foreach (Shape s in shapeList) switch (s.getType()){ case ShapeType.LINE: objLine = (Line)s; objLine.drawLine(); break; case ShapeType.RECTANGLE: objRectangle = (Rectangle)s; objRectangle.drawRectangle();

Đoạn chương trình hiện tại đã thực hiện thành công việc vẽ đường thẳng và đường tròn trên màn hình Để nâng cấp và mở rộng chương trình, cần thêm mã vẽ đường tròn vào hàm draw Tuy nhiên, việc này có thể yêu cầu chỉnh sửa các hàm và thực thể khác, dẫn đến vi phạm nguyên lý đóng mở Để đảm bảo chương trình tuân thủ nguyên lý này, chúng ta sẽ áp dụng tính đa hình trong lập trình hướng đối tượng, và điều chỉnh chương trình với lớp Shape trừu tượng cùng phương thức draw.

} public class Line: Shape{ public override void draw(){

// Các lệnh vẽ đoạn thẳng }

} public class Rectangle: Shape{ public override void draw(){

// Các lệnh vẽ hình chữ nhật }

} class Circle: Shape{ public override void draw(){

// Các lệnh vẽ hình tròn }

} public void draw(ArrayList shapeList){ foreach (Shape s in shapeList) s.draw();

Trong chương trình, để thêm một hình mới, chỉ cần tạo một lớp đối tượng kế thừa từ lớp trừu tượng Shape mà không cần chỉnh sửa hàm draw.

Việc thiết kế phần mềm cần tuân thủ nguyên lý đóng mở, tuy nhiên không phải tất cả các thực thể phần mềm đều thực hiện điều này Mục tiêu thiết kế là tối ưu hóa việc tuân thủ nguyên lý này, đặc biệt là tính đóng gói của các thực thể phần mềm Các đối tượng quản lý và chịu trách nhiệm về thông tin của mình, từ đó hạn chế sự kết dính giữa các đối tượng Trong trường hợp tối ưu, các thuộc tính của đối tượng được đặt là private, cho phép việc thay đổi thuộc tính chỉ thông qua các thao tác của chính đối tượng đó, ngăn chặn việc truy cập từ các đối tượng khác.

Nguyên lý Nghịch đảo phụ thuộc

Nguyên lý đóng mở là một trong những nguyên lý quan trọng nhất trong thiết kế hướng đối tượng, trong khi nguyên lý thay thế Liskov giúp kiểm tra xem thiết kế hoặc chương trình có đáp ứng được nguyên lý này hay không.

Các thành phần phần mềm nên phụ thuộc vào các khái niệm chung và tổng quát, thay vì những chi tiết cụ thể Ngược lại, các khái niệm tổng quát không nên phụ thuộc vào các yếu tố riêng lẻ Sự phụ thuộc giữa chúng cần được đảo ngược để đảm bảo tính linh hoạt và khả năng mở rộng trong phát triển phần mềm.

Cái chung, tổng quát bao gồm những đặc tính cơ bản nhất của những cái riêng, cụ thể, trong khi những cái riêng tuân theo các quy tắc do cái chung định nghĩa Những cái chung thường ít thay đổi, trong khi sự biến đổi chủ yếu xảy ra ở những cái riêng Việc phụ thuộc vào cái chung giúp các thành phần phần mềm linh hoạt hơn và thích ứng tốt với sự thay đổi Khi dựa vào cái chung, các thành phần phần mềm không bị ảnh hưởng và không cần sửa đổi khi thay thế cái riêng này bằng một cái riêng khác.

Xét đoạn chương trình đọc dữ liệu từ bàn phím và xuất ra máy in: public void copy(){

Printer printer = new Printer(); char c; while ((c = keyboard.read()) != „q‟) printer.write(c);

Khi nâng cấp đoạn chương trình để xuất dữ liệu ra máy in hoặc tập tin, cần điều chỉnh phương thức public void copy(OutputType type).

File objFile = new File(); char c; while ((c = objKeyboard.read()) != „q‟) if (type == OutputType.PRINTER) objPrinter.write(c); else if (type == OutputType.FILE) objFile.write(c);

Hàm copy vi phạm nguyên lý đóng mở vì cần chỉnh sửa khi thêm thiết bị mới, do làm việc với các thiết bị đọc ghi cụ thể Điều này dẫn đến việc vi phạm nguyên lý nghịch đảo phụ thuộc Để khắc phục, cần sửa đổi hàm để làm việc với thiết bị tổng quát Ví dụ, có thể chỉnh sửa đoạn mã thành: public void copy(Reader objReader, Writer objWriter) { char c; while ((c = objReader.read()) != 'q') objWriter.write(c); }

} Một số chú ý khi tuân theo nguyên lý nghịch đảo phụ thuộc:

Nguyên lý nghịch đảo phụ thuộc có mối liên hệ chặt chẽ với nguyên lý đóng mở trong phát triển phần mềm Việc vi phạm nguyên lý này dẫn đến những vấn đề trong việc bảo trì và mở rộng hệ thống Khi các thành phần phần mềm phụ thuộc vào các chi tiết cụ thể, việc nâng cấp sẽ yêu cầu sửa đổi các thành phần liên quan, từ đó vi phạm nguyên lý đóng mở.

Một đặc trưng quan trọng của thiết kế hướng đối tượng là việc truy xuất đối tượng thông qua giao diện, giúp tăng khả năng tuân thủ nguyên lý nghịch đảo phụ thuộc Cách tiếp cận này mang lại tính linh động cao cho các thành phần phần mềm, vì không cần sửa đổi khi thay thế đối tượng Các đối tượng trong lớp thừa kế sẽ có hành vi tương tự như đối tượng trong lớp cơ sở, cho phép chúng thay thế đối tượng lớp cơ sở để thực hiện các thao tác cần thiết.

Việc sử dụng tính chất thừa kế trong lập trình cần được thực hiện một cách cẩn thận Khi có lớp A với phương thức F, việc thêm lớp B kế thừa từ lớp A trong quá trình nâng cấp phần mềm có thể gây ra vấn đề nếu không thay thế đúng cách.

B sẽ khiến phương thức F thực hiện các hành vi khác biệt so với trước khi thay thế Vì vậy, cần điều chỉnh hàm F để đảm bảo rằng cách ứng xử không bị thay đổi, điều này vi phạm nguyên lý đóng mở.

Consider the following example to illustrate the consequences of haphazard inheritance: the public class Stack contains a private ArrayList data The method push, defined as virtual, allows for the addition of an integer n to the stack.

// Đưa số nguyên n vào đỉnh ngăn xếp

// Lấy giá trị ở đỉnh ngăn xếp

} } public class Queue: Stack{ public override void push(int n){

// Thêm số nguyên n vào hàng đợi

// Lấy số nguyên từ hàng đợi

} } public int function1(Stack p){ p.push(2); p.push(3); p.push(4); int a = p.pop(); int b = p.pop(); if (a == 4 && b == 3) return a * b; throw new ArgumentException();

Để tái sử dụng các thuộc tính và phương thức từ lớp Stack, lớp Queue được thiết kế để kế thừa từ lớp Stack Khi truyền đối tượng Queue vào hàm function1, hành vi của hàm này sẽ khác nhau tùy thuộc vào loại đối tượng Đối với đối tượng lớp Stack, hàm function1 trả về tích của hai số 4 và 3, trong khi với đối tượng lớp Queue, hàm sẽ phát sinh ngoại lệ Do đó, cần chỉnh sửa hàm function1 để đảm bảo tính nhất quán trong hành vi Việc này dẫn đến vi phạm nguyên lý đóng mở, và hàm function1 không tuân thủ nguyên lý thay thế Liskov.

Một số chú ý khi xác định nguyên lý thay thế Liskov:

Nguyên lý thay thế Liskov liên quan chặt chẽ đến nguyên lý đóng mở trong lập trình Khi một thực thể phần mềm không tuân thủ nguyên lý này, nó sẽ hoạt động khác nhau ở các lớp cơ sở và lớp dẫn xuất Để đảm bảo các thao tác đúng trên cả hai lớp, cần phải thực hiện chỉnh sửa, điều này có thể dẫn đến việc vi phạm nguyên lý đóng mở.

Nguyên lý thay thế Liskov có tính chất tương đối, nghĩa là một thực thể có thể thỏa mãn nguyên lý trong một tình huống nhưng không còn tuân thủ trong tình huống khác Tuy nhiên, trong phân tích thiết kế hướng đối tượng, cần tối ưu hóa số lượng thực thể tuân thủ nguyên lý này trong các tình huống thường gặp, đặc biệt là đối với các thực thể có khả năng nâng cấp và mở rộng.

2.1.4 Nguyên lý Phân tách giao diện

Không nên để các thực thể phần mềm phụ thuộc vào những giao diện mà chúng không sử dụng đến

Khi xây dựng lớp, đặc biệt là lớp trừu tượng, người ta thường có xu hướng đưa nhiều thuộc tính và phương thức vào, dẫn đến lớp có giao diện bị ô nhiễm Lớp này trở nên phức tạp, khiến thực thể phải sử dụng toàn bộ giao diện để thực hiện các tác vụ đơn giản Khi một lớp muốn kế thừa lớp trừu tượng với những nội dung cụ thể, nó buộc phải cài đặt các phần giao diện không cần thiết, gây ra sự dư thừa và tăng sự kết dính giữa các thực thể Ví dụ, khi thực hiện các phương thức như public: virtual void Draw() const; và virtual void Transfer(double dx, double dy);.

}; class Circle : public Shape{ protected: double radius;

Point center; public: virtual void Draw() const; virtual void Transfer(double dx, double dy);

}; class Line : public Shape{ protected:

Point startPoint, endPoint; public: virtual void Draw() const; virtual void Transfer(double dx, double dy);

To illustrate the violation of the Interface Segregation Principle, we can enhance the existing program by incorporating a coloring function, specifically by adding the Fill method to the Shape interface This involves utilizing enumerations for ColorType, which includes options like red, green, and blue, as well as PatternType, which offers solid, vertical, and horizontal patterns The Shape class then declares three pure virtual functions: Draw, Transfer, and Fill, ensuring that any derived class must implement these methods.

}; class Square : public Shape{ protected: double side;

Point topLeft; public: virtual void Draw() const; virtual void Transfer(double dx, double dy); virtual void Fill(ColorType color, PatternType pattern); };

Nguyên lý Phân tách giao diện

Không nên để các thực thể phần mềm phụ thuộc vào những giao diện mà chúng không sử dụng đến

Khi xây dựng lớp trừu tượng, việc đưa quá nhiều thuộc tính và phương thức vào lớp có thể dẫn đến hiện tượng giao diện bị ô nhiễm Điều này làm cho lớp trở nên phức tạp, khiến cho các thực thể khi thực hiện các tác vụ đơn giản phải tương tác với toàn bộ giao diện của lớp Khi một lớp muốn thừa kế từ lớp có giao diện bị ô nhiễm, nó sẽ phải cài đặt những phần không cần thiết, dẫn đến sự dư thừa và tăng cường sự kết dính giữa các thực thể Ví dụ, khi thực hiện các phương thức như `public: virtual void Draw() const;` và `virtual void Transfer(double dx, double dy);`, sự phức tạp này càng trở nên rõ ràng.

}; class Circle : public Shape{ protected: double radius;

Point center; public: virtual void Draw() const; virtual void Transfer(double dx, double dy);

}; class Line : public Shape{ protected:

Point startPoint, endPoint; public: virtual void Draw() const; virtual void Transfer(double dx, double dy);

To illustrate the violation of the Interface Segregation Principle, we can enhance the existing program by incorporating a coloring function, specifically by adding the Fill method to the Shape interface This introduces the use of enumerations for ColorType, which includes options like red, green, and blue, as well as PatternType, which consists of solid, vertical, and horizontal patterns The Shape class is defined with three pure virtual functions: Draw, Transfer, and Fill, emphasizing the need for a clear separation of responsibilities within the interface.

}; class Square : public Shape{ protected: double side;

Point topLeft; public: virtual void Draw() const; virtual void Transfer(double dx, double dy); virtual void Fill(ColorType color, PatternType pattern); };

Các lớp con Circle, Line, và Square cần phải triển khai phương thức ảo Fill Đối với lớp Circle và Square, việc này có ý nghĩa thực tế, nhưng với lớp Line, chức năng Fill không phù hợp Việc yêu cầu lớp Line định nghĩa hàm Fill gây ra sự vi phạm nguyên lý phân tách giao diện Để khắc phục vấn đề này, cần thực hiện thiết kế lại cho phù hợp.

Hình 2.2 Mô hình thừa kế lớp cải tiến

Một số chú ý khi xem xét nguyên lý phân tách giao diện:

Việc vi phạm nguyên lý phân tách giao diện có thể gây ra nhiều vấn đề Nếu chấp nhận giao diện Shape bị ô nhiễm, trong lớp Line, chúng ta có thể cài đặt hàm Fill và một giải pháp là sử dụng phương thức rỗng Hoặc, trong giao diện Shape, hàm Fill không cần phải là thuần ảo mà có thể là hàm rỗng Tuy nhiên, cả hai giải pháp này đều tiềm ẩn nguy cơ vi phạm nguyên lý thay thế Liskov.

Nguyên lý phân tách giao diện liên quan chặt chẽ đến nguyên lý đóng mở Việc vi phạm nguyên lý phân tách giao diện có thể dẫn đến sự vi phạm nguyên lý đóng mở, ảnh hưởng đến tính linh hoạt và khả năng mở rộng của hệ thống.

Các nguyên lý xây dựng mẫu thiết kế phần mềm

Tình huống phát sinh mẫu thiết kế là từ nguyên lý thiết kế và thực tiễn 58 2.2.2 Mẫu thiết kế là giải pháp cụ thể

Trong phân tích thiết kế hướng đối tượng, việc tuân thủ nguyên lý thiết kế và xây dựng kiến trúc tối ưu cho hệ thống thường dẫn đến sự lặp lại của các kiến trúc Từ đó, chúng ta có thể trừu tượng hóa và tổng quát hóa vấn đề thành các mẫu thiết kế áp dụng cho những tình huống tương tự Vì vậy, có thể khẳng định rằng các mẫu thiết kế được hình thành từ những tình huống và vấn đề cụ thể.

Khi viết mẫu thiết kế, cần tuân thủ các nguyên lý thiết kế và thực tiễn một cách nghiêm ngặt Các thiết kế cần dựa trên sự hiểu biết sâu rộng về thực tiễn, bao gồm cả thiết kế ngầm định và tường minh Việc trích dẫn nguyên lý kèm theo sẽ giúp độc giả hiểu rõ nguồn gốc của các quyết định thiết kế Để bắt đầu, nên nhận dạng và hệ thống hóa các mẫu thiết kế đã được ứng dụng rộng rãi, ghi lại các luồng thông dụng và nguyên lý thiết kế tốt Điều này không chỉ giúp nhận diện các mẫu thiết kế dự định mà còn tạo ra cái nhìn tổng thể, giúp trừu tượng hóa và tổng quát hóa mẫu cần diễn đạt.

Các mẫu có thể được tạo ra từ các mẫu khác và thường là sự kết hợp của nhiều thiết kế khác nhau Tính chất phức tạp của một mẫu cần được xem xét kỹ lưỡng, vì nó có thể bị phân tách thành các mẫu thiết kế nhỏ hơn Do đó, việc xác định xem mẫu đang xây dựng là nguyên tử hay phức hợp là rất quan trọng.

2.2.2 Mẫu thiết kế là giải pháp cụ thể

Mẫu thiết kế được xem như giải pháp cho các vấn đề cụ thể, với trọng tâm là tìm kiếm và mô tả những giải pháp hiệu quả Điều này rất quan trọng vì mỗi mẫu đều mang một đặc điểm riêng, dẫn dắt đến các giải pháp hữu ích Nét tổng thể của mẫu thiết kế phản ánh sự lặp lại và tính chất hữu ích của giải pháp cho vấn đề đó Các nội dung khác trong mẫu thường đóng vai trò hỗ trợ cho giải pháp, phù hợp với từng tình huống cụ thể Việc viết mẫu thiết kế có thể theo nhiều định dạng khác nhau, nhưng tất cả đều hướng tới mục tiêu chung là giải quyết các vấn đề đang được tranh cãi.

Một giải pháp hiệu quả luôn hướng đến việc giải quyết một vấn đề cụ thể Để tìm ra giải pháp, việc nhận diện và hiểu rõ vấn đề là rất quan trọng Suy nghĩ về các vấn đề giúp chúng ta tập trung vào cốt lõi của giải pháp và tránh bị lạc hướng trong các cuộc thảo luận không liên quan Do đó, việc nắm bắt vấn đề sẽ giữ cho giải pháp luôn là trọng tâm trong quá trình phát triển và thực hiện.

Khi bàn về giải pháp, việc tập trung vào bản thân giải pháp và cách áp dụng nó sẽ dễ hiểu hơn Ngược lại, việc xác định giải pháp phù hợp và các điều kiện liên quan có thể gây khó khăn cho việc viết mẫu thiết kế Do đó, người viết mẫu thiết kế thường nhấn mạnh vào vấn đề, giúp tập trung tư duy vào nguồn gốc của các mẫu thiết kế.

Khi đã có một mẫu thiết kế, hãy tránh suy nghĩ quá nhiều về nó và chuyển hướng tư duy sang những ý tưởng khác Điều này thường dẫn đến sự hình thành các mẫu luân phiên, tạo ra một nhóm thiết kế đa dạng Việc này không chỉ giúp khám phá những mẫu phù hợp hơn mà còn giúp chúng ta hiểu sâu sắc hơn về mẫu thiết kế ban đầu mà mình đang hướng tới.

Ngữ cảnh của ứng dụng đóng vai trò quan trọng trong việc đảm bảo thành công cho bất kỳ giải pháp nào được phát triển từ một mẫu Mặc dù các giải pháp được mô tả trong các mẫu có thể hoạt động hiệu quả trong một ngữ cảnh nhất định, nhưng chúng cũng có thể gặp thất bại trong những ngữ cảnh khác Việc hiểu rõ lý do và mức độ phụ thuộc vào ngữ cảnh sử dụng cụ thể là một thách thức lớn.

Mục tiêu thiết kế mẫu là hướng tới người dùng

Trong quá trình viết mẫu thiết kế, cần chú trọng đến người dùng, đặt họ vào trung tâm của mô tả Mọi yếu tố trong mẫu thiết kế phải liên quan đến những vấn đề mà người dùng đã biết và hiểu, đồng thời phản ánh ý định và các hoạt động của họ đối với hành vi của hệ thống Tác giả cũng nên xem người dùng cuối là một phần quan trọng trong khán giả Mục tiêu của việc viết mẫu thiết kế tốt là hỗ trợ người thiết kế trong việc giao tiếp hiệu quả với người dùng cuối.

Một mẫu không đồng nghĩa với thiết kế; cần phân biệt rõ giữa hai khái niệm này Mẫu thiết kế thể hiện sự thực thi tốt nhất đã được áp dụng, đóng vai trò quan trọng trong quá trình sáng tạo.

Một thuật ngữ đƣợc sử dụng trong một mẫu phải có cùng nghĩa khi sử dụng nó trong mẫu khác

Một mẫu thiết kế tốt không chỉ mô tả chức năng mà còn truyền đạt ý nghĩa thông qua tên gọi của nó, giúp tăng cường vốn từ vựng và tạo điều kiện cho sự thảo luận giữa các nhà phát triển và nhà thiết kế Việc lựa chọn tên mẫu là rất quan trọng, vì tên này sẽ phản ánh rõ ràng mục đích và chức năng của mẫu.

Các mẫu đóng góp vào một ngôn ngữ chia sẻ, với các tên được hệ thống hóa, tạo ra một hình thức giao tiếp giữa các nhà phát triển, nhà thiết kế, phân tích nghiệp vụ, và người dùng cuối Nhờ đó, các mẫu trở thành phần quan trọng trong ngôn ngữ thảo luận thiết kế giao diện người dùng, kết nối tất cả những người thực hành và những người bị ảnh hưởng bởi nó.

Lựa chọn phép ẩn dụ một cách cẩn thận là rất quan trọng Nên ưu tiên các phép ẩn dụ rõ ràng và quen thuộc từ kinh nghiệm thực tế, tránh sử dụng thuật ngữ phức tạp Việc chọn phép ẩn dụ phù hợp giúp người dùng nhanh chóng nắm bắt các vấn đề, như hình ảnh của một file di chuyển giữa các thư mục trên màn hình có thể minh họa cho quá trình thay đổi con trỏ file trong cây thư mục, ngay cả khi không có dữ liệu nào thực sự được di chuyển Điều này tạo điều kiện cho người dùng hiểu rõ hơn về quy trình theo cách dễ dàng và tiện lợi.

2.2.5 Lựa chọn các tình huống áp dụng điển hình

Để tạo ra một mô tả mẫu hiệu quả, việc tìm kiếm các ví dụ điển hình là rất quan trọng Những ví dụ này nên đến từ những ứng dụng thành công và đáng tin cậy, giúp minh họa rõ ràng các nguyên lý thiết kế tốt Các ví dụ có thể được trình bày dưới dạng ngôn ngữ của các mẫu hoặc thông qua các sự xác định và thực thi thực tế, nhằm thể hiện tính tổng quát của thiết kế.

Nhiều người lo ngại về các ví dụ và mã mẫu cụ thể Việc tham khảo mẫu thiết kế từ những ví dụ nhất định có thể gây khó khăn khi áp dụng cho các vấn đề tương tự trong phạm vi rộng hơn của mẫu thiết kế Đây là một lý do chính đáng cho sự lo lắng này.

Lựa chọn các tình huống áp dụng điển hình

Tìm kiếm ví dụ điển hình là rất quan trọng để hỗ trợ mô tả mẫu, với các ứng dụng thành công đáng tin cậy Các ví dụ này cần minh họa rõ ràng các nguyên lý thiết kế tốt, thể hiện tính tổng quát Chúng có thể được trình bày dưới dạng ngôn ngữ mẫu hoặc qua các sự xác định và thực thi thực tế.

Nhiều người lo ngại về các ví dụ trong mẫu thiết kế và mã ví dụ cụ thể Việc xem xét mẫu thiết kế trong các ví dụ cụ thể có thể gây khó khăn khi áp dụng cho những vấn đề tương tự trong phạm vi rộng hơn của mẫu thiết kế Điều này dẫn đến mối lo ngại rằng một số độc giả có thể coi các ví dụ như là những mẫu cố định và hiểu sai về bản chất của mẫu thiết kế như là các macro nổi bật.

Việc đưa ra ví dụ giúp trừu tượng hóa vấn đề và áp dụng các nguyên lý tổng quát một cách hiệu quả Thay vì né tránh ví dụ và khiến người đọc lạc lối trong sự trừu tượng, tốt hơn là minh họa bằng ví dụ cụ thể để đánh giá độ rủi ro một cách rõ ràng hơn.

Để làm rõ một mẫu cụ thể, việc sử dụng đa ví dụ là một phương pháp hiệu quả Các ví dụ khác nhau của cùng một mẫu không chỉ giúp minh họa các tình huống phổ biến mà còn cho thấy nhiều cách tiếp cận khác nhau, có thể sử dụng cùng một nền tảng hoặc những nền tảng khác nhau.

Khi viết mã ví dụ, cần cân nhắc giữa độ phức tạp và sự đơn giản Nếu quá đơn giản, độc giả có thể bỏ qua; nhưng nếu quá phức tạp, họ sẽ phải đối mặt với nhiều vấn đề không cần thiết Sự đơn giản hóa mang lại lợi ích, vì từ những điều cơ bản, ta có thể phát triển thêm các khía cạnh phức tạp hơn Hiểu một chút vấn đề còn tốt hơn là cố gắng hiểu quá nhiều thông tin, vì độc giả thường chỉ cần đọc một mẫu để nắm bắt nội dung.

Tổng kết chương

Một trong những mục tiêu chính của thiết kế phần mềm hướng đối tượng là tăng khả năng sử dụng lại Khi phân tích và thiết kế phần mềm theo nguyên lý hướng đối tượng, người ta nhận thấy sự xuất hiện của các cấu trúc lặp lại, từ đó dẫn đến việc phát triển các mẫu thiết kế Nghiên cứu các nguyên lý thiết kế hướng đối tượng là rất quan trọng, vì chúng có mối liên hệ trực tiếp với việc phát triển các mẫu thiết kế phần mềm Kết hợp với việc trừu tượng hóa từ các mẫu thiết kế GoF đã được giới thiệu trong chương 1, tác giả đã tổng quát hóa những điểm quan trọng này.

Năm nguyên lý phát triển mẫu thiết kế phần mềm cung cấp các gợi ý và định hướng tư duy quan trọng cho việc xây dựng thiết kế trong các tình huống phát sinh Những nguyên tắc này đã được trình bày chi tiết trong chương 2 của luận văn.

PHƯƠNG PHÁP THIẾT KẾ MẪU PHẦN MỀM

Các thành phần cơ bản của mẫu thiết kế

Nhìn chung mỗi mẫu thiết kế phần mềm đều bao gồm 4 thành phần cơ bản [6]:

Mỗi mẫu thiết kế được đặt tên nhằm nêu bật vấn đề mà nó giải quyết, giúp mô tả bài toán thiết kế, giải pháp và kết quả áp dụng Việc này không chỉ tăng cường vốn từ vựng trong thiết kế mà còn cho phép thiết kế ở mức trừu tượng cao hơn Khi có từ vựng về các mẫu thiết kế, chúng ta có thể dễ dàng trao đổi với đồng nghiệp và sử dụng trong tài liệu liên quan, tạo điều kiện cho các developer giao tiếp hiệu quả Khi nhắc đến tên mẫu thiết kế, người nghe có thể nhanh chóng nhận ra giải pháp mà người nói đề cập, từ đó hình dung rõ hơn về thiết kế và dễ dàng nhận diện ưu, nhược điểm của nó.

Mẫu thiết kế cần được áp dụng trong ngữ cảnh cụ thể, giúp giải thích những vướng mắc gặp phải Bài viết mô tả các vấn đề thiết kế đặc trưng, chẳng hạn như cách thể hiện thuật toán dưới dạng đối tượng và cấu trúc lớp có dấu hiệu thiết kế không linh hoạt Qua đó, chúng ta có thể nhận biết và tránh những cấu trúc không hiệu quả, đồng thời tìm ra phương pháp khắc phục Việc mô tả vấn đề không chỉ dừng lại ở việc lựa chọn mẫu thiết kế mà còn cần xem xét ngữ cảnh và điều kiện để đảm bảo lựa chọn được chính xác hơn.

Giải pháp cho vấn đề thiết kế bao gồm các thành phần, mối quan hệ và vai trò của từng thành phần cũng như sự tương tác giữa chúng Không mô tả một thiết kế hay cách cài đặt cụ thể, giải pháp này cho phép áp dụng mẫu thiết kế vào nhiều tình huống khác nhau Mẫu thiết kế tập trung vào việc trình bày vấn đề ở mức độ trừu tượng, đồng thời giải thích cách lựa chọn và sắp xếp các đối tượng một cách phù hợp và hiệu quả.

Mặc dù thường bị bỏ qua trong việc mô tả giải pháp, nhưng việc đánh giá mẫu thiết kế là rất quan trọng Nó giúp hiểu rõ ưu nhược điểm của thiết kế và ảnh hưởng đến tốc độ thực hiện cũng như khối lượng tài nguyên cần thiết Hơn nữa, nó cũng quyết định lựa chọn ngôn ngữ và phương pháp cài đặt chương trình Khả năng tái sử dụng là yếu tố then chốt cho sự thành công của hệ thống hướng đối tượng, do đó, kết quả áp dụng mẫu thiết kế ảnh hưởng đến tính linh động, khả năng mở rộng và tính khả truyển của hệ thống Việc liệt kê rõ ràng những ưu nhược điểm của mẫu thiết kế sẽ giúp đánh giá và hiểu chúng một cách dễ dàng hơn.

Các định dạng mẫu thiết kế

Để xây dựng và trình bày các mẫu thiết kế, chúng ta cần mô tả chúng theo một cấu trúc rõ ràng Việc lựa chọn định dạng phù hợp sẽ giúp chúng ta thực hiện mô tả chi tiết về mẫu thiết kế một cách hiệu quả.

Việc sử dụng ký hiệu đồ họa giúp tăng tính trực quan nhưng chưa đủ để mô tả nội dung một cách đầy đủ Để đơn giản hóa, chúng ta có thể rút gọn mô tả các mẫu thiết kế thông qua sơ đồ lớp đối tượng và mối quan hệ giữa chúng Tuy nhiên, sơ đồ thiết kế lớp không thể hiện rõ ý nghĩa và khả năng áp dụng của mẫu thiết kế Để tái sử dụng thiết kế, cần ghi lại các quyết định, sự luân phiên và thỏa hiệp đã dẫn đến nó Các ví dụ cụ thể sẽ giúp minh họa mẫu thiết kế trong thực tế và khả năng áp dụng của nó.

Để mô tả mẫu thiết kế một cách rõ ràng và đầy đủ, việc sử dụng định dạng nhất quán là rất quan trọng Có nhiều định dạng khác nhau để mô tả mẫu thiết kế, nhưng một số định dạng trở nên phổ biến hơn Đặc biệt, định dạng GoF do Erich Gamma phát triển đã được sử dụng để mô tả 23 mẫu thiết kế nổi tiếng Mỗi mẫu trong định dạng này được chia thành các hạng mục, cung cấp cấu trúc đồng dạng, giúp việc học hỏi, so sánh và áp dụng mẫu thiết kế trở nên dễ dàng hơn.

Nhiều người xem một ngôn ngữ mẫu của Christopher Alexander (APL) có tầm mẫu nhất định;

Vấn đề được nêu ra là bản chất của một mẫu thiết kế, được mô tả trong một hoặc hai câu Tiêu đề này trở thành phần thân của vấn đề, nơi trình bày chi tiết nền tảng kinh nghiệm của mẫu thiết kế Bài viết cung cấp bằng chứng cho tính hiệu lực của mẫu thiết kế, đồng thời khám phá phạm vi của các phương pháp khác nhau và cách mà mẫu thiết kế có thể được thể hiện trong một cấu trúc cụ thể.

Giải pháp là yếu tố cốt lõi trong mẫu thiết kế, phản ánh lĩnh vực và các mối quan hệ cần thiết để giải quyết vấn đề trong ngữ cảnh cụ thể Thông thường, giải pháp được trình bày dưới dạng chỉ thị rõ ràng, giúp xác định các bước cần thực hiện để xây dựng mẫu một cách hiệu quả.

 Sau giải pháp, có một biểu đồ với các nhãn để chỉ ra các thành phần chính của nó;

Cuối cùng, có một đoạn liên kết với mẫu tổng thể, kết nối với tất cả các mẫu nhỏ hơn trong ngôn ngữ, điều này là cần thiết để hoàn thiện và trang trí mẫu này.

Trong thiết kế phần mềm theo định dạng Alexandrian, một biến đổi phổ biến là chia phần thân của giải pháp thành hai phần Phần đầu tiên, sau tiêu đề vấn đề, tập trung vào việc mở rộng vấn đề và các khía cạnh liên quan Phần thứ hai, được chuyển xuống sau tổng kết giải pháp, sẽ mô tả chi tiết về giải pháp được đề xuất.

3.2.2 Định dạng GOF Định dạng GOF là một định dạng đƣợc sử dụng trong sách Gang of Four, mà đã đƣa ra các mẫu nổi tiếng trong thế giới phần mềm Nó là một định dạng rất có cấu trúc, chia mẫu thiết kế thành các mục: Intent, Motivation, Applicability, Structure, Participants, Collaborations, Consequences, Implementation, Sample Code, Known Uses, and Related Patterns

3.2.3 Định dạng Porland Định dạng Portland có tên từ thực tế là nhiều người, từ vùng Portland Oregon, tại những thảo luận các mẫu đầu tiên, tất cả đã sử dụng cùng một định dạng Định dạng Portland hoàn toàn nguyên bản và rất ngắn, thường ít hơn một trang trên 1 mẫu Một cặp đoạn mô tả vấn đề, sau đó có một từ “therefore” dùng để nhấn mạnh vấn đề, theo sau lài một cặp đoạn mà mô tả giải pháp

3.2.4 Định dạng Coplien Đƣợc gọi nhƣ vậy, bởi vì nó luôn đồng hành với Jim Coplien Nó cũng đƣợc tham chiếu nhƣ là định dạng Canonical

Các phần chính của định dạng bao gồm: vấn đề, ngữ cảnh, hiệu lực và giải pháp Nhiều tác giả thường bổ sung thêm một số mục mở rộng Mỗi phần được trình bày thành một đoạn riêng biệt, trong đó mục hiệu lực thường được trình bày dưới dạng danh sách Các mẫu theo định dạng này thường ngắn gọn, chỉ khoảng 2 trang.

3.2.5 Định dạng POSA Định dạng này có tên từ sách POSA Tương tự như định dạng GOF, nó là một định dạng rất có cấu trúc và hơi lớn, mặc dù các đề mục của nó gồm: summary, example, context, problem, solution, structure, dynamics, implementation, example resolved, variants, known uses, consequences, and see also Các mẫu thường gồm rất nhiều trang Một phần quan trọng của định dạng này là các mẫu thường được nhắc đến trước bởi một chương trần thuật tổng kết các mẫu và mô tả qua các topic

Another less commonly used format is the P of EAA, introduced by Christopher Alexander in his design patterns This narrative format includes several key components: how it works, when to use it, and one or more examples Typically, a template in this format spans eight pages, although it can range from one to twelve pages in length.

Định dạng GoF của mẫu thiết kế

Tên mẫu thiết kế không chỉ phản ánh bản chất của nó mà còn đóng vai trò quan trọng trong việc xây dựng từ vựng thiết kế Một tên gọi phù hợp, độc đáo giúp định danh và tham chiếu mẫu thiết kế một cách dễ dàng Bên cạnh đó, việc phân loại mẫu thiết kế là cần thiết để xác định cách sử dụng trong các tình huống tương đồng, từ đó nâng cao tính hiệu quả và ứng dụng của mẫu trong thực tế.

Mẫu thiết kế này nhằm mục đích giải quyết các vấn đề cụ thể trong lĩnh vực thiết kế, với những lý do cơ bản và mục tiêu rõ ràng Nó thể hiện các vấn đề mà nó đại diện, giúp người dùng hiểu được giá trị và chức năng của mẫu thiết kế Nói cách khác, mục đích của bài viết là mô tả các mục tiêu trừu tượng của mẫu thiết kế một cách ngắn gọn và dễ hiểu.

Khả năng áp dụng của mẫu thiết kế rất quan trọng, vì nó xác định những tình huống và trường hợp mà mẫu có thể được sử dụng hiệu quả Đặc biệt, cần làm rõ những ngữ cảnh không phù hợp với mẫu thiết kế đó Các ví dụ về thiết kế nghèo nàn giúp chúng ta nhận diện rõ hơn những tình huống không nên áp dụng mẫu, từ đó cải thiện quá trình thiết kế Việc hiểu và nhận ra các tình huống này sẽ giúp tối ưu hóa kết quả thiết kế và nâng cao trải nghiệm người dùng.

Cấu trúc hệ thống được thể hiện qua đồ họa các lớp trong mẫu thiết kế, sử dụng ký hiệu từ kỹ thuật mô hình hóa đối tượng (Object Modeling Technique - OMT) Mô hình thiết kế lớp giúp mô tả cấu trúc các lớp đối tượng, trong khi biểu đồ tương tác minh họa chuỗi yêu cầu và sự cộng tác giữa các đối tượng.

 Thành phần tham gia: danh sách các lớp hay các đối tƣợng tham gia trong mẫu thiết kế và nhiệm vụ, vai trò của từng đối tƣợng trong đó

Sự cộng tác trong mẫu thiết kế là quá trình mà các thành phần tham gia phối hợp và làm việc cùng nhau để truyền tải nhiệm vụ và thực hiện các chức năng một cách hiệu quả Các yếu tố này tương tác nhịp nhàng, tạo nên một hệ thống đồng bộ giúp tối ưu hóa hiệu suất và đạt được mục tiêu chung.

Kết quả của mẫu thiết kế cần hỗ trợ cho các mục tiêu đã đề ra, đồng thời việc thỏa hiệp và áp dụng mẫu thiết kế trong dự án cụ thể sẽ mang lại những kết quả và hậu quả nhất định Ngoài ra, cần xem xét những khía cạnh nào của cấu trúc hệ thống có thể thay đổi một cách độc lập.

Sự thực thi mẫu thiết kế bao gồm việc thực hiện và biểu diễn các giải pháp trong bốn thành phần cơ bản Bài viết này cung cấp các kỹ thuật được áp dụng trong quá trình thực hiện mẫu, đồng thời đề xuất các phương pháp khác nhau cho các thực thi này Ngoài ra, nó cũng đề cập đến những khó khăn có thể gặp phải, các gợi ý và kỹ thuật hữu ích trong quá trình thực thi Một vấn đề quan trọng cần xem xét là việc xác định ngôn ngữ sử dụng trong quá trình thực hiện mẫu.

 Mã nguồn: đoạn mã ví dụ giải thích cho bạn biết có thể thực thi mẫu nhƣ thế nào trong các ngôn ngữ lập trình nhƣ C++ hay Smalltalk

Trong bài viết này, chúng ta sẽ khám phá cách sử dụng thực tế của các mẫu thiết kế thông qua hai ví dụ cụ thể từ các lĩnh vực khác nhau Đầu tiên, trong lĩnh vực phát triển phần mềm, mẫu thiết kế Singleton được áp dụng để đảm bảo rằng chỉ có một thể hiện duy nhất của một lớp được tạo ra, giúp quản lý tài nguyên hiệu quả Thứ hai, trong ngành kiến trúc, mẫu thiết kế Observer được sử dụng để theo dõi và cập nhật thông tin giữa các thành phần của một hệ thống, tạo ra sự tương tác linh hoạt và đồng bộ Những ví dụ này minh chứng cho tính ứng dụng đa dạng của các mẫu thiết kế trong thực tiễn.

 Các mẫu liên quan: các mấu thiết kế nào khác có liên quan gần gũi với mẫu này

Sự khác nhau chính giữa chúng là gì Nó có thể đƣợc sử dụng chung với những mẫu thiết kế nào.

Việc lựa chọn định dạng mẫu thiết kế

Có nhiều định dạng khác nhau để mô tả một mẫu thiết kế, và sự lựa chọn định dạng phụ thuộc vào sở thích cá nhân của từng tác giả Mỗi tài liệu về mẫu thiết kế thường sử dụng các định dạng riêng biệt, có thể biến đổi từ những định dạng cơ bản Điều quan trọng là tìm một định dạng phù hợp với phong cách viết và ý tưởng cần truyền tải Để xác định định dạng mẫu phù hợp, bước đầu tiên là đọc nhiều sách mẫu thiết kế khác nhau và chú ý đến nội dung, đồng thời tự hỏi định dạng nào là tiện lợi nhất Việc đọc lướt qua từ đầu đến cuối cũng có thể giúp xác định sự phù hợp trong công việc.

Khi đã xác định được định dạng mong muốn, hãy bắt tay vào việc viết Thử nghiệm với nhiều định dạng khác nhau có thể giúp bạn tìm ra lựa chọn phù hợp nhất Một phương pháp hiệu quả là viết một mẫu theo nhiều định dạng để so sánh Hãy nhờ người khác xem xét và đánh giá để xác định định dạng nào là tốt nhất.

Các mẫu biến đổi kích cỡ theo các định dạng khác nhau, với định dạng Portland thường sử dụng mẫu ngắn hơn, trong khi POSA có thể kéo dài hàng chục trang Sự lựa chọn này phụ thuộc vào mức độ chi tiết mong muốn; nếu bạn muốn khám phá vấn đề thực thi và cung cấp mã ví dụ, các mẫu dài hơn sẽ phù hợp hơn Trong trường hợp này, định dạng có cấu trúc thường là lựa chọn tối ưu.

Tổng kết chương

Khi đối mặt với các tình huống phát sinh, dựa trên các nguyên lý thiết kế từ chương 2, chúng ta có thể hình dung ra những vấn đề cho mẫu thiết kế dự kiến Tuy nhiên, những ý tưởng này vẫn chỉ là khởi đầu Để cụ thể hóa những ý tưởng này, cần phải có một định hướng hoặc cấu trúc rõ ràng.

PHÁT TRIỂN MẪU THIẾT KẾ VÀ ỨNG DỤNG

Các mẫu thiết kế đối tƣợng

Mẫu đối tượng trống (Null Object pattern) là một giải pháp thay thế cho việc thiếu một đối tượng trong một kiểu đã định Nó cung cấp cách xử lý thông minh mà không thực hiện bất kỳ hành động nào, ẩn sau các chi tiết của các thành phần cộng tác của nó.

Tên khác: Stub, Active Nothing

Khi một Client gọi phương thức hoặc biến có thể là null, điều này có thể gây ra ngoại lệ Để bảo vệ hệ thống khỏi sự cố không mong muốn, cần viết mã để ngăn chặn việc gọi phương thức hoặc giá trị null Cách tiếp cận hiệu quả là kiểm tra giá trị trước khi thực hiện hành động: nếu someObject khác null, thì thực hiện someObject.doSomething(); ngược lại, thực hiện hành vi thay thế.

Việc lặp lại các mã kiểm tra có thể gây phồng lên hệ thống với các đoạn mã không cần thiết So với đoạn mã không bị ràng buộc bởi logic null, đoạn mã bị ràng buộc thường yêu cầu nhiều hơn về tư duy để mở rộng Logic null không cung cấp sự bảo vệ cho mã mới, dẫn đến nguy cơ lỗi nếu lập trình viên quên tính đến nó Mẫu Null Object là giải pháp hiệu quả, loại bỏ việc kiểm tra giá trị null bằng cách cho phép gọi trường hoặc biến một cách an toàn Điều này được thực hiện bằng cách gán cho trường hoặc biến một đối tượng phù hợp, và khi giá trị là null, nó sẽ tham chiếu tới một thực thể Null Object với hành vi mặc định là không làm gì cả hoặc không gây lỗi.

Null Object pattern là một lớp trừu tượng xác định giao diện cho tất cả các đối tượng của kiểu này, với Null Object được thực thi như là lớp con của lớp trừu tượng Vì tuân theo giao diện của lớp trừu tượng, Null Object có thể được sử dụng ở bất kỳ nơi nào cần đến kiểu đối tượng này Mặc dù nhiều người nghĩ rằng Null Object là đơn giản và "ngu ngốc", thực tế chúng luôn biết chính xác những gì cần thực hiện mà không cần tương tác với bất kỳ đối tượng nào khác, cho thấy rằng chúng thực sự rất thông minh.

Hình 4.1 Sơ đồ lớp mẫu Null Object

 Client: cần đến một cộng tác viên

AbstractObject là giao diện được thiết kế cho cộng tác viên của client, thực hiện các xử lý mặc định nhằm đảm bảo tính đồng nhất cho giao diện chung của tất cả các lớp.

 RealObject: định nghĩa một lớp con cụ thể của AbstractObject mà các bản thể của chúng cung cấp cách cƣ xử có ích mà client mong đợi

 Cung cấp một giao diện giống với giao diện của AbstractObject để một null object có thể đƣợc thay thế cho một đối tƣợng thực

Để thực thi giao diện của nó một cách hiệu quả, điều quan trọng là không làm gì cả, nghĩa là không thực hiện hành động nào phụ thuộc vào cách mà client kỳ vọng về hành vi.

 Khi có nhiều hơn một cách để không làm gì, nhiều hơn một lớp NullObject có thể đƣợc yêu cầu

Các client tương tác với các cộng tác viên thông qua giao diện lớp AbstractObject Khi bên nhận là một RealObject, yêu cầu sẽ được xử lý để cung cấp hành vi thực tế Ngược lại, nếu bên nhận là một NullObject, yêu cầu sẽ được xử lý bằng cách không thực hiện hành động nào hoặc ít nhất là trả về một kết quả null.

Mẫu Null Object xác định cấp bậc lớp với sự phân chia giữa các đối tượng thực và đối tượng null Đối tượng null có thể thay thế cho đối tượng thực trong trường hợp không có hành động nào được mong đợi Khi mã client cần một đối tượng thực, nó cũng có thể nhận một đối tượng null mà không gây ra sự cố.

Đơn giản hóa mã client là điều cần thiết, vì các client không phân biệt giữa cộng tác viên thực và cộng tác viên null Điều này giúp giảm bớt sự phức tạp trong mã, vì các client không cần kiểm tra xem chúng đang làm việc với cộng tác viên nào, từ đó tránh việc phải viết mã kiểm thử riêng cho các trường hợp cộng tác viên null.

Có vài vấn đề cần xem xét khi thực thi mẫu null object:

Lớp null object thường được triển khai dưới dạng singleton, vì nó không có trạng thái và không thể thay đổi, dẫn đến việc nhiều bản thể giống nhau Thay vì tạo ra nhiều bản thể giống nhau, hệ thống có thể sử dụng một bản thể duy nhất một cách lặp đi lặp lại.

 Nếu một vài client mong đợi null object không làm gì một cách và một số cách ứng xử khác, các lớp NullObject sẽ đƣợc yêu cầu

Đối tượng null được xem như một chiến lược đặc biệt trong mẫu thiết kế Strategy Một đối tượng null có thể là một trường hợp của mẫu Strategy, nơi mà các lớp ConcreteStrategy xác định các cách tiếp cận khác nhau để hoàn thành một nhiệm vụ Nếu một trong những cách tiếp cận này là không thực hiện hành động nào, thì ConcreteStrategy đó được gọi là NullObject.

Đối tượng null là một trạng thái đặc biệt, có thể coi là một trường hợp đặc biệt của mẫu State Mỗi ConcreteState thường có một số phương thức không thực hiện chức năng nào vì không phù hợp với trạng thái đó Thực tế, một phương thức thường được thiết kế để thực hiện một số tác vụ hữu ích trong hầu hết các trạng thái, nhưng lại không làm gì trong ít nhất một trạng thái Nếu một ConcreteState chủ yếu thực thi các phương thức của nó để không làm gì hoặc chỉ trả về kết quả null, nó sẽ trở thành một trạng thái không làm gì, hay còn gọi là trạng thái null.

Mã ví dụ: public abstract class NullObjectList{ public abstract int size(); public abstract NullObjectList remove(int rem); public abstract NullObjectList append(int toAppend); public abstract void printList();

} class Node: NullObjectList{ int data;

NullObjectList next; public Node(int data, NullObjectList next){ public override NullObjectList append(int toAppend){ return new Node(this.data, this.next.append(toAppend));

Console.WriteLine(this.data); this.next.printList();

} } class Empty : NullObjectList{ public Empty() { } public override int size(){ return 0;

} public override NullObjectList remove(int rem){ return new Empty();

} public override NullObjectList append(int toAppend){ return new Node(toAppend, new Empty());

} public override void printList(){ return;

} } class TestClass{ public static void Main(){ string input; int numInput;

NullObjectList myList = new Empty(); bool isInput = true; while (isInput){

Console.WriteLine("Select An Option: ");

Console.WriteLine("3 Output List Size");

Console.WriteLine("Press Any Other Number to Exit Program");

Console.Write("Selection: "); input = Console.ReadLine(); numInput = Convert.ToInt32(input);

Console.WriteLine(""); switch (numInput){ case 1:

Console.Write("Input a Number: ") input = Console.ReadLine(); numInput = Convert.ToInt32(input); myList = myList.append(numInput);

Console.WriteLine("Input a Number:"); input = Console.ReadLine(); numInput = Convert.ToInt32(input); myList = myList.remove(numInput);

Console.WriteLine("List Size is " + myList.size() + " elements");

Console.WriteLine(""); break; default: isInput = false; break }

Singleton thường được áp dụng để tạo ra một đối tượng duy nhất, vì việc có nhiều bản thể sẽ ảnh hưởng đến cách hoạt động và không có trạng thái bên trong có thể thay đổi.

4.1.2 Mẫu đối tượng vai trò

Mẫu đối tượng vai trò (Role Object Pattern) cho phép thích ứng một đối tượng với nhu cầu đa dạng của các client thông qua việc sử dụng các đối tượng vai trò được kết nối một cách linh hoạt Mỗi đối tượng vai trò đại diện cho một chức năng cụ thể trong ngữ cảnh của client, giúp quản lý các vai trò một cách động Việc tách biệt các vai trò thành các đối tượng riêng biệt giữ cho các ngữ cảnh khác nhau không bị lẫn lộn, từ đó đơn giản hóa cấu hình hệ thống.

Một hệ thống hướng đối tượng thường dựa trên các trừu tượng chính được mô hình hóa qua lớp với các trạng thái và hành vi Cách tiếp cận này hiệu quả cho thiết kế ứng dụng nhỏ Tuy nhiên, khi mở rộng hệ thống thành tập hợp các ứng dụng tích hợp, cần xử lý nhiều client khác nhau, yêu cầu xác định ngữ cảnh trong các trừu tượng chính.

Mẫu thiết kế Ajax

Thế hệ Web 2.0 đang trong giai đoạn khởi đầu và cần thời gian để thay đổi những thói quen hiện tại Công nghệ AJAX đóng vai trò quan trọng trong giai đoạn này, nhờ vào sự thành công của các ứng dụng như Gmail, Google Suggest và Google Maps Mặc dù hiện tại AJAX chưa được áp dụng rộng rãi, nhưng nhiều người tin rằng các ứng dụng sử dụng công nghệ này sẽ phát triển nhanh chóng trong tương lai.

Thuật ngữ AJAX đƣợc xuất hiện vào ngày 18/2/2005 trong bài báo AJAX:

Bài viết "A New Approach to Web Applications" của Jesse James Garrett từ công ty Adaptive Path đã giới thiệu thuật ngữ AJAX, nhanh chóng trở nên phổ biến trong cộng đồng phát triển Web và trở thành yếu tố trung tâm trong các câu chuyện liên quan đến thế hệ Web 2.0.

AJAX, viết tắt của Asynchronous JavaScript and XML, là kỹ thuật giúp tăng tốc độ ứng dụng web bằng cách chia nhỏ dữ liệu và chỉ tải những phần cần thiết, thay vì làm mới toàn bộ trang Kỹ thuật này kết hợp hai tính năng mạnh mẽ của JavaScript, được các nhà phát triển đánh giá cao.

 Gửi yêu cầu đến máy chủ mà không cần nạp lại trang

 Phân tách và làm việc với XML

AJAX không phải một công nghệ đơn lẻ mà là sự kết hợp một nhóm công nghệ: CSS , DOM, XMLHttpRequest, JavaScript Trong đó

 Trình bày trang web theo tiêu chuẩn XHTML và CSS;

 Biểu diễn động và tương tác bằng DOM (Document Object Model);

 Trao đổi và xử lý dữ liệu bằng XML và XSLT;

 Truy cập dữ liệu không đồng bộ bằng MLHttpRequest;

 Liên kết các công nghệ trên với nhau bằng JavaScript

4.2.3 AJAX hoạt động như thế nào

Trong ứng dụng web truyền thống, người dùng phải chờ đợi khi gửi yêu cầu HTTP tới server để nhận lại trang web hoàn chỉnh, gây ra sự chậm trễ trong quá trình tương tác Để khắc phục vấn đề này, kỹ thuật Ajax được phát triển, cho phép gửi và nhận yêu cầu một cách không đồng bộ Thay vì gửi truy vấn HTTP trực tiếp, người dùng sử dụng JavaScript để gửi lệnh tới bộ xử lý Ajax Khi cần thông tin từ server, như tải dữ liệu mới hoặc cập nhật giao diện, Ajax sẽ thực hiện yêu cầu mà không làm gián đoạn trải nghiệm người dùng Server sẽ gửi dữ liệu phản hồi dưới dạng XML tới Ajax Engine, giúp cải thiện hiệu suất và tính mượt mà của ứng dụng web.

Hình 4.14 Ứng dụng web truyền thống (trái) và ứng dụng AJAX

Việc áp dụng cơ chế kỹ thuật Ajax đã tối ưu hóa quá trình truyền tải thông tin và rút ngắn thời gian phản hồi Thay vì phải tải lại toàn bộ trang web, Ajax chỉ nạp những dữ liệu đã thay đổi, giữ nguyên các phần còn lại Nhờ đó, người dùng không còn phải đối mặt với hiện tượng cửa sổ trắng hay biểu tượng đồng hồ cát, biểu thị cho việc server đang xử lý yêu cầu.

Hình 4.15 Tương tác đồng bộ trong ứng dụng web truyền thống (trên) và dị bộ trong ứng dụng AJAX

4.2.4 Các ứng dụng AJAX phổ biến

Google đã đầu tư mạnh mẽ vào phát triển ứng dụng AJAX, ứng dụng công nghệ này được áp dụng trong nhiều sản phẩm của họ như Orkut, Gmail và phiên bản thử nghiệm Google Groups Tính năng Google Suggest cung cấp các gợi ý từ khóa gần như ngay lập tức khi người dùng chưa hoàn tất việc gõ Ngoài ra, Google Maps cho phép người dùng tương tác với bản đồ bằng cách kéo thả, mang lại trải nghiệm tương tự như trên máy tính để bàn.

Yahoo sẽ ra mắt phiên bản Yahoo Mail Beta 1 sử dụng công nghệ AJAX trên toàn cầu Đồng thời, công ty cũng đang phát triển một công cụ AJAX để cập nhật nhanh chóng thông tin về sân bay, chuyến bay và thời gian, nhằm phục vụ tốt hơn cho khách hàng.

Microsoft cũng đang triển khai chương trình Windows Live Mail và Windows Live Messenger hỗ trợ AJAX

AJAX là một công nghệ hiện đại không còn xa lạ, xuất hiện trong nhiều ứng dụng thực tế như Google Suggest với tính năng gợi ý từ khóa đơn giản và Google Maps với khả năng tương tác phức tạp.

4.2.5 Tổng quan về mẫu thiết kế Ajax

Ajax hứa hẹn mang lại nhiều lợi ích trong thiết kế ứng dụng web và đã được áp dụng rộng rãi trong nhiều ứng dụng tiêu biểu Sự xuất hiện của các mẫu thiết kế đã cải thiện hiệu quả phát triển phần mềm, giúp rút ngắn thời gian lập trình và loại bỏ mã dư thừa Mẫu thiết kế Ajax đang ngày càng trở nên phổ biến trong phát triển ứng dụng web, cung cấp các thực hành tốt nhất để cải tiến nhanh chóng các dự án Nó hướng dẫn người dùng cách áp dụng hiệu quả các nguyên lý thiết kế trong các ứng dụng web sử dụng công nghệ Ajax.

Mặc dù thuật ngữ Ajax chỉ mới xuất hiện gần đây, đã có nhiều mẫu thiết kế Ajax được phát triển từ trước đó Các ý tưởng liên quan đến Ajax đã tồn tại trên web trước khi có tên gọi chính thức Hiện nay, hàng trăm trang web mới đang áp dụng Ajax cùng với các công cụ mạnh mẽ như RSS, Technorati, Google và Wikis để nhận diện và cập nhật nội dung khi chúng sẵn sàng.

Currently, over 60 Ajax templates have been developed, categorized into four groups: Foundational Technology, Programming, Functionality and Usability, and Development These templates facilitate web service design, manage the flow of information between the browser and server, locate the DOM upon receiving a response, and optimize execution.

Các mẫu về Chức năng và Tính khả dụng bao gồm 28 mẫu, cung cấp hướng dẫn về giao diện người dùng và các khái niệm liên quan đến khả năng sử dụng Những yếu tố này rất quan trọng đối với người dùng, bao gồm các widget và kỹ thuật tương tác, cấu trúc và duy trì nội dung trên trang web, hiệu ứng trực quan, cùng với các chức năng mà Ajax hỗ trợ.

Các mẫu phát triển: 8 mẫu Đây là những mẫu quy trình được áp dụng trong thực hành phát triển, khác với các mẫu trước đó, chúng tồn tại song song với ứng dụng Ajax Những thực hành này bao gồm việc phát hiện vấn đề và thực hiện kiểm thử.

Hình 4.16 Nhóm các mẫu AJAX

Bài viết trình bày vị trí của 4 nhóm thiết kế mẫu Ajax trong một ứng dụng Ajax Ba nhóm đầu tiên tập trung vào sản phẩm, trong khi nhóm cuối cùng, Development patterns, liên quan đến tiến trình phát triển Các mẫu thuộc nhóm sản phẩm, đặc biệt là Foundational Technologies, giải thích cách sử dụng các công nghệ web cơ bản.

XMLHttpRequest and DOM are essential components in web development, particularly within the context of Programming Patterns This article provides guidance on effectively utilizing these technologies At a higher level, it explores Functionality and Usability patterns, while Foundational Technology Patterns serve as the core of Ajax language frameworks The remaining groups are built upon these foundational patterns and operate independently from one another.

Ứng dụng mẫu thiết kế trong thiết kế khung cho tầng truy cập dữ liệu

Khi phát triển phần mềm, lập trình viên cần chú ý đến hệ cơ sở dữ liệu mà ứng dụng tương tác Một thách thức lớn là khi người dùng thay đổi hệ dữ liệu, ứng dụng có thể không hoạt động hoặc cần phải cài đặt lại.

Một số phần mềm cần tương tác với nhiều hệ cơ sở dữ liệu như DB2, MySQL, SQLServer và Oracle yêu cầu cài đặt các điều khiển truy cập dữ liệu Tuy nhiên, điều này dẫn đến một số vấn đề như khởi tạo tài nguyên cơ sở dữ liệu chậm, mã cài đặt truy cập phức tạp và cồng kềnh, gây khó khăn trong việc phát triển và bảo trì.

Dựa trên lý thuyết thiết kế chương trình theo mô hình ba tầng, bài viết áp dụng các mẫu thiết kế như Factory Method, Singleton và Null Object để xây dựng và triển khai Framework cho tầng truy cập dữ liệu Việc sử dụng đối tượng ADO.Net giúp khắc phục những nhược điểm hiện có trong thiết kế hệ thống.

Ngày nay, hầu hết các ứng dụng sử dụng mô hình 2-tầng Client/Server, trong đó mã lệnh về giao diện người dùng, logic nghiệp vụ và truy cập dữ liệu được viết bên dưới tầng giao diện Mô hình này giúp phát triển nhanh chóng nhưng gặp khó khăn trong việc thay đổi theo yêu cầu thực tế, bảo trì và tích hợp Để quản lý các thành phần hệ thống một cách độc lập và hiệu quả, kiến trúc đa tầng được áp dụng, trong đó mô hình 3-tầng do Microsoft đề xuất là phổ biến nhất, bao gồm tầng trình diễn (Presentation Layer), tầng nghiệp vụ (Business Layer) và tầng dữ liệu (Data Layer).

Các tầng trong ứng dụng giao tiếp với nhau thông qua các dịch vụ mà mỗi tầng cung cấp, tạo nên sự tương tác hiệu quả Mỗi tầng chỉ cần quan tâm đến dịch vụ mà tầng khác cung cấp, mà không cần biết chi tiết về hoạt động bên trong Giao diện người dùng không tương tác trực tiếp với tầng DL, mà tất cả các giao tác được quản lý trong tầng BL để đảm bảo an toàn Mọi trao đổi với cơ sở dữ liệu đều phải thông qua giao diện dịch vụ trong tầng BL.

4.3.3 Cài đặt mô hình khung cho tầng truy cập dữ liệu Để dễ hình dung ta có thể xét một mô hình ứng dụng đƣợc thiết kế theo mô hình 3-tầng như hình dưới Tầng DL lưu trữ và truy xuất dữ liệu, được chia thành hai phần: DataAccess và DataStorage:

 DataStorage: lưu trữ dữ liệu và thực hiện các dịch vụ của hệ quản trị cơ sở dữ liệu nhƣ SQL Server, Oracle, DB2, MySQL, …

DataAccess bao gồm các đối tượng tác nghiệp (Business Object) và đối tượng kết nối để truy cập dữ liệu Business Object cung cấp các dịch vụ từ tầng dữ liệu cho các tầng phía trên, bao gồm các chức năng như chèn, cập nhật và xóa dữ liệu.

Tầng PL và DL hoạt động độc lập, giao tiếp thông qua tầng BL Điều này cho phép việc chuyển đổi giữa các hệ quản trị cơ sở dữ liệu như SQL Server sang Oracle, hoặc Oracle sang DB2 mà không ảnh hưởng đến giao diện chương trình.

Trong cấu trúc khung cho tầng truy cập dữ liệu tổng quát, chúng ta phát triển các Business Object dựa trên ADO.Net nhằm cung cấp dịch vụ cho tầng Business Logic (BL), đồng thời đảm bảo sự độc lập giữa tầng Presentation Layer (PL) và Data Layer (DL).

Một cách để xây dựng lớp dịch vụ là sử dụng phương thức với cấu trúc switch nhằm xác định loại điều khiển truy cập cơ sở dữ liệu Phương thức này có thể được cài đặt như sau: public override void MyProcedure(proType ProviderType) { switch(ProviderType) { case ProviderType.SqlClient: objSqlCommand.SqlProcedure(); break; case ProviderType.OleDb: objOleDbCommand.OleDbProcedure(); break; default: } }

Việc thiết kế và cài đặt như trên có một số nhược điểm, bao gồm việc mã lệnh cài đặt trở nên phức tạp do phải viết lại các đoạn mã tương tự và không cần thiết Ngoài ra, khi cần truy cập cơ sở dữ liệu mới, việc cài đặt và biên dịch lại lớp này cũng gây khó khăn Để khắc phục vấn đề này, có thể áp dụng các mẫu thiết kế như Factory Method, Singleton và Null Object nhằm xây dựng một Framework cho tầng truy cập dữ liệu tổng quát Biểu đồ lớp cho bài toán được trình bày, trong đó lớp DataAccessBaseClass và các lớp kế thừa của nó đóng vai trò là các Business Object, cung cấp dịch vụ cho tầng BL để tương tác với cơ sở dữ liệu.

Hình 4.18 Biểu đồ lớp của tầng truy cập dữ liệu

Lớp DataAccessBaseClass là lớp cơ sở cho sản phẩm, cung cấp các thuộc tính và phương thức tổng quát để tương tác với hệ cơ sở dữ liệu Các phương thức trong lớp này được thiết kế với khả năng nạp chồng, giúp tăng tính linh hoạt và tái sử dụng mã nguồn.

The OdbcDataAccess, OleDbDataAccess, OracleDataAccess, SqlDataAccess, and NullObject classes, which are ConcreteProduct classes, inherit from the DataAccessBaseClass They implement the GetDataProviderConnection() method, which returns a connection string object to the corresponding database, along with a private constructor ConcreteCreator() {}.

//Lấy giá trị được thiết lập trong tệp tin cấu hình private static string GetAppSetting(string setting){ string val; try { val = System.Configuration.ConfigurationSettings AppSettings[setting].ToString();

} if (val == null) val = ""; return val;

/*Lấy chuỗi kết nối database bằng cách đọc trong tệp tin cấu hình (app.config)*/ private static string GetConnectionString(){ string val; val = "server=" + GetAppSetting("Datasource") +

";pwd=" + GetAppSetting("Password") + ((GetAppSetting("Timeout").Length > 0) ?

";Connection Timeout=" + GetAppSetting("Timeout"): ""); return val;

/*Xây dựng một data provider của tầng truy cập dữ liệu dựa trên các thiết lập cấu hình ứng dụng Tập tin cấu hình ứng dụng phải chứa 2 key:

1 "DataProviderType" key : giá trị của nó là một trong các giá trị được định nghĩa (sql,oracle,access,odbc,oledb)

2 "ConnectionString" key : chuỗi kết nối database*/ public static DataAccessBaseClass GetDataAccessLayer(){ if (GetAppSetting("DataProviderType") == null

|| GetAppSetting("Database") == null) throw new ArgumentNullException("Chưa chỉ định 'DataProviderType' hoặc chưa chỉ định 'Server' hoặc chưa chỉ định 'Database' trong tệp tin cấu hình");

DataProviderType dataProvider; try{ dataProvider(DataProviderType)System.Enum.Parse(typeof(DataProviderTy pe),GetAppSetting("DataProviderType"));

} catch(Exception e){ throw new ArgumentException("Kiểu provider cho tầng truy cập dữ liệu không hợp lệ.");

To build a data provider for the data access layer, utilize the provided provider The connection string is obtained from the ConnectionString property of the DataAccessBaseClass The method `GetDataAccessLayer` accepts a `DataProviderType` parameter and returns an instance of `DataAccessBaseClass`, allowing for seamless integration of different data providers.

To build a data provider for the data access layer, utilize the provided connection string and data provider type The method `GetDataAccessLayer` returns an instance of `DataAccessBaseClass` based on the specified `dataProviderType` It supports various types including OleDb, Odbc, Oracle, and Sql, each initializing its respective data access class with the given connection string If an unsupported data provider type is specified, the method returns a `NullObject`, ensuring robust handling of invalid inputs.

Ngày đăng: 17/12/2023, 01:58

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w