Dạng thức bắc cầu

**Dạng thức bắc cầu** (*bridge pattern*) là một dạng thức thiết kế được dùng trong công nghệ phần mềm.

Dạng thức bắc cầu dùng để *"tách riêng [[sự trừu tượng (khoa học máy tính)|tính trừu tượng]] ra khỏi [[thực thể]] của nó để cho cả hai có thể thay đổi một cách độc lập"* (Gamma et. al.). Dạng thức bắc cầu dùng tính đóng gói, tính kết hợp, và có thể dùng tính kế thừa để tách riêng việc xử lý (trách nhiệm) giao cho từng lớp khác nhau.

Khi mà các lớp thường xuyên bị thay đổi, thì các đặc tính của lập trình hướng đối tượng trở nên rất hữu ích vì sự thay đổi ở các mã lệnh của chương trình có thể được thực hiện dễ dàng với ít công sức nhất trong việc tìm hiểu toàn bộ chương trình. Dạng thức bắc cầu rất hữu ích khi không chỉ bản thân lớp thay đổi thường xuyên mà ngay cả những công việc mà nó thực hiện cũng thay đổi. Bản thân lớp có thể được xem là một *sự trừu tượng* và những gì mà lớp đó có thể thực hiện là *sự hiện thực*.

Khi mà sự trừu tượng và sự hiện thực được tách riêng thì sự thay đổi của mỗi cái là không phụ thuộc nhau. Một ví dụ hay của dạng thức thiết kế bắc cầu trong tác phẩm *Design Patterns Explained: A New Perspective on Object-Oriented Design* của Shalloway và Trott. Giả sử bạn có một sự trừu tượng, đó là một *shapes*. Bạn muốn có nhiều loại *shapes* (i.e.: rectangle, triangle, circle...) và mỗi cái có các thuộc tính riêng nhưng có những điểm chung, đó là những điểm mà mọi *shapes* đều có thể có. Một trong chúng là mọi *shapes* có thể tự vẽ (draw) chúng. Tuy nhiên, việc vẽ các hình lên màn hình đôi khi phụ thuộc vào các hiện thực đồ họa (hình dáng của mỗi loại shape) khác nhau và hệ điều hành khác nhau. Bạn muốn các *shapes* của mình có thể thực hiện thao tác vẽ trên nhiều loại hệ thống nhưng nếu để cho *shape* tự xử lý toàn bộ hay phải chỉnh sửa từng *shape class* để phù hợp cho từng kiến trúc khác nhau là không thực tiễn. Dạng thức bắc cầu giúp cho bạn tạo ra các lớp mới có thể cung cấp hiện thực các tác vụ vẽ. Lớp trừu tượng, shape, cung cấp các phương thức để lấy các thông tin kích thước và thuộc tính của một *shape* trong khi lớp hiện thực, vẽ, cung cấp một *giao diện* cho các tác vụ vẽ. Bây giờ nếu shape mới cần được tạo ra (một hình bông tuyết chẳng hạn) hay nếu muốn có thể vẽ các hĩnh vẽ của bạn bằng các hàm đồ họa API mới thì chỉ việc thêm một lớp mới để xây dựng (*implement*) các đặc tính bạn cần.

## Cấu trúc

* Client
** Đối tượng cần dùng dạng thức bắc cầu
* Abstraction
** định ra một *abstract interface*
** quản lý việc tham chiếu đến đối tượng hiện thực cụ thể (Implementor)
* Refined Abstraction
** Mở rộng interface mà đã được định ra trong Abstraction
* Implementor
** định ra các interface cho các lớp hiện thực. *(Thông thường thì Abstraction interface định ra các tác vụ ở mức cao dựa trên các hiện thực của interface này.)*
* ConcreteImplementor
** hiện thực Implementor interface

## Ví dụ

### Java
Chương trình Java sau minh hoạ ví dụ *"shapes"* đã nói ở trên và đưa ra kết quả:
 API1.circle at 1.000000:2.000000 radius 7.500000
 API2.circle at 5.000000:7.000000 radius 27.500000


```java
/** "Implementor" */
interface DrawingAPI {
    public void drawCircle(double x, double y, double radius);
}

/** "ConcreteImplementor" 1/2 */
class DrawingAPI1 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) {
        System.out.printf("API1.circle at %f:%f radius %f\n", x, y, radius);
   }
}

/** "ConcreteImplementor" 2/2 */
class DrawingAPI2 implements DrawingAPI {
   public void drawCircle(double x, double y, double radius) { 
        System.out.printf("API2.circle at %f:%f radius %f\n", x, y, radius);
   }
}

/** "Abstraction" */
interface Shape {
   public void draw();                                            // low-level
   public void resizeByPercentage(double pct);     // high-level
}

/** "Refined Abstraction" */
class CircleShape implements Shape {
   private double x, y, radius;
   private DrawingAPI drawingAPI;
   public CircleShape(double x, double y, double radius, DrawingAPI drawingAPI) {
       this.x = x;  this.y = y;  this.radius = radius; 
       this.drawingAPI = drawingAPI;
   }

   // low-level i.e. Implementation specific
   public void draw() {
        drawingAPI.drawCircle(x, y, radius);
   }   
   // high-level i.e. Abstraction specific
   public void resizeByPercentage(double pct) {
        radius *= pct;
   }
}

/** "Client" */
class BridgePattern {
   public static void main(String[] args) {
       Shape[] shapes = new Shape[2];
       shapes[0] = new CircleShape(1, 2, 3, new DrawingAPI1());
       shapes[1] = new CircleShape(5, 7, 11, new DrawingAPI2());

       for (Shape shape: shapes) {
           shape.resizeByPercentage(2.5);
           shape.draw();
       }
   }
}
```


|
## Tham khảo