Mẫu thiết kế Facade

Structural Design Pattern

Mục đích

  • Cung cấp một giao diện nhất quán cho tập hợp các giao diện trong một subsystem. Facade Pattern định nghĩa một giao diện ở một cấp độ cao hơn để giúp cho người dùng có thể dễ dàng sử dụng subsystem này vì chỉ cần giao tiếp với một giao diện chung duy nhất.
  • Bao bọc một subsystem phức tạp với một giao diện đơn giản.

Vấn đề

Một phân đoạn của client community cần một giao diện đơn giản cho toàn thể các chức năng của một subsystem phức tạp.

Thảo luận

Facade che đi sự phức tạp của các thành phần subsystem (phụ) đối với client. Khắc phục cấu trúc khớp nối lỏng lẻo giữa các subsystem với nhau và giữa các subsystem với client. Mặt khác, nếu Facade chỉ là điểm truy cập duy nhất cho subsystem nó sẽ giới hạn tính năng và tính linh hoạt mà “những người sử dụng thành thạo” có thể cần. Đối tượng Facade nên điều phối hoặc hỗ trợ đơn giản. Nó không nên trở thành một nhà tiên tri biết tất cả mọi thứ hay một đối tương “thần thánh” nào đó.

Cấu trúc

Facade sẽ lấy một “thứ khó hiểu bí ẩn được phủ trong sự thần bí” và chen vào một lớp phủ để có thể thuần hóa sự vô định hình và tính khó hiểu của đống phần mềm hỗn độn đó.

SubsystemOne và SubsystemThree không tương tác trực tiếp với các thành phận nội tại của SubsystemTwo thay vào đó chúng sử dụng SussystemTwoWrapper “facade”(ví dụ: Trừu tượng ở cấp độ cao”)

Ví dụ

Facade định nghĩa một giao diện thống nhất, cấp cao hơn cho một subsystem làm cho nó sử dụng dễ dàng hơn. Người mua gặp Facade khi đặt hàng từ một cửa hàng. Người mua gọi vào một số và nói chuyện với đại diện dịch vụ khách hàng. Bộ phận đại diện dịch vụ khách hàng đóng vai trò như Facade, cung cấp giao diện cho bộ phận hoàn thành đơn đặt hàng, bộ phận thanh toán và bộ phận vận chuyển.

Danh sách kiểm tra

  1. Nhận diện một giao diện đơn giản, thống nhất cho cho subsystem hoặc component.
  2. Thiết kế class“bọc ngoài” đóng gói subsystem.
  3. Facade(hay còn gọi là wrapper) nắm bắt sự phức tạp và cộng tác của các component và phân công xử lý cho các method thích hợp.
  4. Client sử dụng (Chỉ giao tiếp) với Facade.
  5. Cân nhắc việc thêm hay không thêm Facade khi muốn tạo dữ liệu.

Quy tắc chung

  • Facade định nghĩa một interface mới, trong khi Adapter sử dụng interface cũ. Hãy nhớ rằng Adapter làm cho hai interface hiện tại có thể làm việc cùng nhau trái ngược với việc tạo một interface hoàn toàn mới.
  • Trong khi Flyweight cho thấy làm thế nào để làm việc trên nhiều đối tượng thì Facade cho thấy làm thế nào để một đối tượng đại diện cho toàn bộ subsystem.
  • Mediator tương tự như Facade trong đó nó trừu tượng hóa chức năng của các lớp hiện tại. Mediator trừu tượng hóa/tập trung tùy ý sự giao tiếp giữa các đối tượng tương đồng.Nó thường “adds value” và nó được biết/tham chiếu bởi các đối tượng tương đồng. Ngược lại, Facade định nghĩa một giao diện đơn giản, nó không thêm mới chức năng. và nó cũng không được biết đến bởi các lớp subsystem.
  • Abstract Factory có thể được sử dụng như một thay thế cho Facade cho việc ẩn các lớp nền tảng được chỉ định.
  • Đối tượng Facade thường là Singletons vì chỉ có một đối tượng Facade là cần thiết.
  • Adapter và Facade cả hai đều là bọc ngoài(wrappers) nhưng chúng là các loại wrapper khác nhau. Mục đích của Facade là tạo ra một interface đơn giản còn mục đích của Adapter là thiết kế trên interface hiện có. Trong khi Facade thường kết hợp nhiều đối tượng thì Adapter chỉ kết hợp một đối tượng duy nhất; Facade có thể front-end một đối tượng phức tạp đơn lẻ và Adapter có thể gói một vài đối tượng kế thừa.

Câu hỏi: Sự khác nhau giữa Adapter pattern và Facade pattern là Adapter bao bọc một lớp còn Facade có thể đại diện cho nhiều lớp đúng hay không? 

Trả lời: Không! Hãy nhớ rằng, Adapter pattern thay đổi giao diện của một hoặc nhiều lớp thành một giao diện mà client mong muốn. Trong khi các ví dụ trong sách phổ biến chỉ ra rằng adapter adapting cho một lớp, bạn cần phải adapt nhiều lớp để cung cấp giao diện cho một client đã được xây dựng. Tương tự như vậy, Facade cung cấp một giao diện đơn giản cho một lớp duy nhất với 1 giao diện phức tạp. Sự khác nhau giữa 2 loại trên không phải là về số lượng các lớp chúng “wrap” mà là về mục đích của chúng.

Ví dụ về làm thế nào để thực thi một mẫu thiết kế Facade trong Java

 Mẫu thiết kế Facade

  1. Xác định một interface mong muốn cho tập hợp subsystems
  2. Thiết kế “wrapper” class có thể đóng gói sử dụng của các subsystems
  3. Client sử dụng(kết hợp với) Facade
  4. Facade/wrapper “ánh xa” vào các API của các subsystems
// 1. Subsystem
class PointCartesian {
private double x, y;
public PointCartesian(double x, double y ) {
this.x = x;
this.y = y;
}

public void move( int x, int y ) {
this.x += x;
this.y += y;
}

public String toString() {
return "(" + x + "," + y + ")";
}

public double getX() {
return x;
}

public double getY() {
return y;
}
}

// 1. Subsystem
class PointPolar {
private double radius, angle;

public PointPolar(double radius, double angle) {
this.radius = radius;
this.angle = angle;
}

public void rotate(int angle) {
this.angle += angle % 360;
}

public String toString() {
return "[" + radius + "@" + angle + "]";
}
}

// 1. Desired interface: move(), rotate()
class Point {
// 2. Design a "wrapper" class
private PointCartesian pointCartesian;

public Point(double x, double y) {
pointCartesian = new PointCartesian(x, y);
}

public String toString() {
return pointCartesian.toString();
}

// 4. Wrapper maps
public void move(int x, int y) {
pointCartesian.move(x, y);
}

public void rotate(int angle, Point o) {
double x = pointCartesian.getX() - o.pointCartesian.getX();
double y = pointCartesian.getY() - o.pointCartesian.getY();
PointPolar pointPolar = new PointPolar(Math.sqrt(x * x + y * y),Math.atan2(y, x) * 180 / Math.PI);
// 4. Wrapper maps
pointPolar.rotate(angle);
System.out.println(" PointPolar is " + pointPolar);
String str = pointPolar.toString();
int i = str.indexOf('@');
double r = Double.parseDouble(str.substring(1, i));
double a = Double.parseDouble(str.substring(i + 1, str.length() - 1));
pointCartesian = new PointCartesian(r*Math.cos(a*Math.PI/180) + o.pointCartesian.getX(),
r*Math.sin(a * Math.PI / 180) + o.pointCartesian.getY());
}
}

class Line {
private Point o, e;
public Line(Point ori, Point end) {
o = ori;
e = end;
}

public void move(int x, int y) {
o.move(x, y);
e.move(x, y);
}

public void rotate(int angle) {
e.rotate(angle, o);
}

public String toString() {
return "origin is " + o + ", end is " + e;
}
}

public class FacadeDemo {
public static void main(String[] args) {
// 3. Client uses the Facade
Line lineA = new Line(new Point(2, 4), new Point(5, 7));
lineA.move(-2, -4);
System.out.println( "after move: " + lineA );
lineA.rotate(45);
System.out.println( "after rotate: " + lineA );
Line lineB = new Line( new Point(2, 1), new Point(2.866, 1.5));
lineB.rotate(30);
System.out.println("30 degrees to 60 degrees: " + lineB);
}
}

Kết quả

after move:  origin is (0.0,0.0), end is (3.0,3.0)
PointPolar is [4.242640687119285@90.0]
after rotate: origin is (0.0,0.0), end is (2.5978681687064796E-16,4.242640687119285)
PointPolar is [0.9999779997579947@60.000727780827376]
30 degrees to 60 degrees: origin is (2.0,1.0), end is (2.499977999677324,1.8660127018922195)

Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Leave a Reply

Your email address will not be published. Required fields are marked *