Mục lục
ToggleBridge Pattern (không chỉ trong PHP) thiết kế với ý tưởng tách rời các hàm xử lý của 1 lớp ra lớp khác. Từ đó sẽ dễ dàng chỉnh sửa, cập nhật mà không làm ảnh hưởng đến những nơi sử dụng class ban đầu. Ở 1 khía cạnh nào đó thì Bridge Pattern khá giống với Adapter Pattern mà CodeTuTam đã giới thiệu trước đó.
Để tìm hiểu kĩ hơn về Bridge Pattern, chúng ta xem thêm về mẫu thiết kế này với ví dụ trên PHP.
Bridge Pattern là một trong những mẫu thiết kế thuộc nhóm cấu trúc (Structural Pattern).
Mẫu Bridge thực hiện với ý tưởng tách tính trừu tượng (abstraction) ra khỏi phần hiện thực (implementation) của nó. Từ đó có thể dễ dàng chỉnh sửa hoặc nâng cấp mà không làm ảnh hưởng đến những nơi có sử dụng lớp ban đầu.
Hiểu 1 cách đơn giản hơn, chúng ta có 1 class ban đầu với rất nhiều hàm chức năng để xử lý nghiệp vụ. Nhưng sau 1 thời gian,chúng ta muốn tối ưu lại class này. Chúng ta không muốn để những hàm xử lý đó trong class nữa.
Điều này dẫn đến việc tạo ra 1 class khác và chuyển các hàm xử lý từ class cũ sang class mới. Khi đó trong class cũ sẽ giữ 1 đối tượng (của class mới) – hay còn gọi là composition. Đối tượng được lưu trữ này sẽ chịu trách nhiệm xử lý thay cho class cũ ban đầu.
2 mẫu thiết kế này có điểm giống nhau ở chỗ sẽ nhờ 1 class khác để thực hiện 1 số chức năng xử lý nào đó. Tuy nhiên, ý nghĩa với mục đích cử dụng của 2 mẫu này khác nhau hoàn toàn.
+ Đối với Adapter Pattern được dùng để biến đổi 1 lớp sang 1 dạng khác có thể sử dụng được. Thông qua Adapter, các lớp bình thường không tương thích với nhau thì có thể làm việc được với nhau.
+ Đối với Bridge Pattern thì là tách nhỏ class hiện thời ra các class khác. Việc này giúp cho việc nâng cấp, thay dổi các hàm trong class mới tách ra không làm ảnh hưởng class ban đầu.
+ Adapter Pattern thường được áp dụng vào hệ thống có sẵn để mở rộng, hoặc giúp cho các phần cũ trong hệ thống tương thích với 1 phần mới nào đó
+ Bridge Pattern được thiết kế trước khi phát triển hệ thống, để tách biệt phần implementation và phần abstraction.
Một Bridge Pattern có các thành phần sau
Client: Đại diện cho khách hàng sử dụng các chức năng (hàm xử lý) thông qua Abstraction.
(Client này có thể là các hàm xử lý nghiệp vụ của hệ thống lớn hoặc là thành phần xử lý nghiệp vụ liên quan tới Abstraction)
Abstraction: định ra một abstract interface (Abstract class) quản lý việc tham chiếu đến đối tượng hiện thực cụ thể (Implementation).
Refined Abstraction (Thể hiện của Abstraction): hiện thực (implement) các phương thức đã được định ra trong Abstraction bằng cách sử dụng một tham chiếu đến một đối tượng của Implementation.
Implementation: định ra các interface cho các lớp hiện thực. Thông thường nó là interface định ra các tác vụ nào đó của Abstraction.
ConcreteImplementations: hiện thực (thể hiện của Implementation)
Để hiểu thêm về Bridge Adapter chúng ta tìm hiểu ví dụ như sau
Như đã biết 1 file ảnh có thể có nhiều định dạng file khác nhau như JPG, PNG, BMP, TIFF, GIF, SVG…
Hiện tại trên thị trường chúng ta cũng có nhiều phiên bản hệ điều hành khác nhau như Window, MacOs, Linux…
Thì đối với mỗi file ảnh( đuôi khác nhau) sẽ có cách đọc và hiển thị trên mỗi điều hành khác nhau. Cụ thể hơn, các đọc file jpg và png trên window sẽ khác nhau, đồng thời cũng khác cách đọc file jpg và png trên MacOs
Ta có lược đồ cơ bản về điều này ( khi xây dựng các lớp thể hiện cho việc đọc file hình ảnh)
Chúng ta sẽ có
1 Abstract class Operator ( Hệ điều hành) trong này sẽ có các phương thức cần ghi đèn như đọc file PNG, đọc file JPG
2 lớp thể hiện của Operator là Window và MacOs
Với mỗi hệ điều hành này, chúng ta có 2 class tương ứng với việc đọc file JPG, hay file PNG
Đoạn mã code mô tả cho đoạn này như sau
<?php class Jpg{ public function preview(){ echo "Xem file định dạng JPG".PHP_EOL; } } class Png{ public function preview(){ echo "Xem file định dạng PNG".PHP_EOL; } } abstract class Operator{ public abstract function previewJpg(Jpg $jpg):void; public abstract function previewPng(Png $png):void; } class Window extends Operator{ public function previewJpg(Jpg $jpg):void{ echo 'Window - '; $jpg->preview(); } public function previewPng(Png $png):void{ echo 'Window - '; $png->preview(); } } class Macos extends Operator{ public function previewJpg(Jpg $jpg):void{ echo 'Macos - '; $jpg->preview(); } public function previewPng(Png $png):void{ echo 'Macos - '; $png->preview(); } } $macos = new Macos; $macos->previewJpg(new Jpg); $macos->previewPng(new Png);
Khi sử dụng code như này sẽ có hạn chế như sau
Hãy tưởng tượng
Với 2 hệ điều hành này, số lượng định dạng hình ảnh cần xây dựng là 3. Lần lượt là JPG Preview, PNG Preview, BMP Preview.
Thì tổng số class cần tạo ra (cho phần xem ảnh) là: 6 class
với 4 định dạng cho 2 hệ điều hành sẽ là 8 class.
Số lượng class cấp số nhân theo số lượng định dạng và số lượng hệ điều hành hiện có.
Từ ví dụ này ta sẽ áp dụng Bridge Pattern vào, ta có lược đồ
Đoạn mã code tương ứng như sau
<?php interface Extension{ public function preview():void; } class Jpg implements Extension{ public function preview():void{ echo 'Xem file JPG'.PHP_EOL; } } class Png implements Extension{ public function preview():void{ echo 'Xem file PNG'.PHP_EOL; } } abstract class Operator{ protected $extension; public function __construct(Extension $extension){ $this->extension = $extension; } public abstract function preview():void; } class Window extends Operator{ public function preview():void{ // Xử lý nghiệp vụ đọc file trên window echo 'Window - '; $this->extension->preview(); } } class MacOs extends Operator{ public function preview():void{ // Xử lý nghiệp vụ đọc file trên MacOs echo 'MacOs - '; $this->extension->preview(); } } $jpg= new Jpg; $png= new Png; $window = new Window($jpg); $window->preview(); //Window - Xem file JPG $window = new Window($png); $window->preview(); //Window - Xem file PNG
Như đoạn mã trên chúng ta sẽ thấy
Khi thêm 1 hệ điều hành, 1 định dạng file ảnh mới thì 1 class mới lại phải tạo ra. Số lượng class tạo ra sẽ không bị cấp số nhân giống như phiên bản code cũ.
+ Giảm sự phụ thuộc giữa các phần abstraction và implementation. Điều này cho phép chúng ta lựa chọn implementation phù hợp lúc runtime.
+ Giảm số lượng những class con không cần thiết, cụ thể trong ví dụ bên trên.
+ Code sạch sẽ gọn gàng, kích thước ứng dụng nhỏ nhắn hơn
+ Dễ dàng nâng cấp mở rộng và bảo trì về sau .
+ Tách biệt giữa phần abstraction và implementation giúp dễ dàng nâng cấp mà không bị ảnh hưởng phần còn lại.
+ Khi bạn muốn tách biệt sự rằng buộc giữa abstraction và implementation. Điều này như đã nói giúp hệ thống mở rộng một cách độc lập.
+ Muốn mở rộng cả phần abstraction và implementation bằng các subclass. Giúp chương trình gọn nhẹ sạch sẽ hơn.
+ Sử dụng ở những nơi mà những thay đổi được thực hiện trong implement không ảnh hưởng đến phía client.
Bài viết đã khái quát cơ bản về Bridge Pattern trong PHP. Thông qua bài viết hi vọng phần nào giúp các bạn hiểu rõ hơn về Design Pattern cũng như ứng dụng chúng vào trong các dự án của mình. Các bạn có thể tham khảo thêm về Adapter Pattern trong PHP
Nếu thấy bài viết có ích hãy like và chia sẻ để ủng hộ Code Tư Tam bạn nhé.
Bình luận: