Có ai đã từng sử dụng Mô hình cầu trong một ứng dụng thế giới thực chưa? Nếu vậy, bạn đã sử dụng nó như thế nào? Là tôi, hay chỉ là Mô hình bộ điều hợp với một chút phụ thuộc tiêm vào hỗn hợp? Liệu nó thực sự xứng đáng với mô hình riêng của nó?
Có ai đã từng sử dụng Mô hình cầu trong một ứng dụng thế giới thực chưa? Nếu vậy, bạn đã sử dụng nó như thế nào? Là tôi, hay chỉ là Mô hình bộ điều hợp với một chút phụ thuộc tiêm vào hỗn hợp? Liệu nó thực sự xứng đáng với mô hình riêng của nó?
Câu trả lời:
Một ví dụ cổ điển về mẫu Cầu được sử dụng trong định nghĩa hình dạng trong môi trường UI (xem mục Wikipedia mẫu cầu ). Mẫu cầu là tổng hợp của các mẫu Mẫu và Chiến lược .
Đây là một cái nhìn chung về một số khía cạnh của mẫu Adaptor trong mẫu Bridge. Tuy nhiên, để trích dẫn từ bài viết này :
Thoạt nhìn, mẫu Cầu trông rất giống mẫu Bộ điều hợp trong đó một lớp được sử dụng để chuyển đổi một loại giao diện sang loại khác. Tuy nhiên, mục đích của mẫu Adaptor là làm cho một hoặc nhiều giao diện của lớp trông giống như của một lớp cụ thể. Mẫu cầu được thiết kế để tách giao diện của lớp khỏi triển khai của lớp để bạn có thể thay đổi hoặc thay thế triển khai mà không thay đổi mã máy khách.
Có sự kết hợp giữa Federico và John's câu trả lời .
Khi nào:
----Shape---
/ \
Rectangle Circle
/ \ / \
BlueRectangle RedRectangle BlueCircle RedCircle
Tái cấu trúc để:
----Shape--- Color
/ \ / \
Rectangle(Color) Circle(Color) Blue Red
Mẫu cầu là một ứng dụng của lời khuyên cũ, "thích sáng tác hơn kế thừa". Nó trở nên tiện dụng khi bạn phải phân lớp thời gian khác nhau theo cách trực giao với nhau. Nói rằng bạn phải thực hiện một hệ thống phân cấp các hình dạng màu. Bạn sẽ không phân lớp Hình dạng với Hình chữ nhật và Hình tròn và sau đó phân lớp Hình chữ nhật với RedRonymous, BlueRonymous và GreenRonymous và tương tự cho Circle, bạn sẽ không? Bạn muốn nói rằng mỗi Hình dạng có một Màu và để thực hiện một hệ thống phân cấp các màu và đó là Mẫu Cầu. Chà, tôi sẽ không thực hiện một "hệ thống phân cấp màu sắc", nhưng bạn hiểu ý ...
Khi nào:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Tái cấu trúc để:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Adaptor và Bridge chắc chắn có liên quan, và sự khác biệt là tinh tế. Có khả năng một số người nghĩ rằng họ đang sử dụng một trong những mẫu này thực sự đang sử dụng mẫu kia.
Giải thích tôi đã thấy là Bộ điều hợp được sử dụng khi bạn đang cố gắng hợp nhất các giao diện của một số lớp không tương thích đã tồn tại . Bộ điều hợp có chức năng như một loại dịch giả cho các triển khai có thể được coi là di sản .
Trong khi đó mẫu Cầu được sử dụng cho mã có nhiều khả năng là trường xanh. Bạn đang thiết kế Bridge để cung cấp giao diện trừu tượng cho việc triển khai cần thay đổi, nhưng bạn cũng xác định giao diện của các lớp thực hiện đó.
Trình điều khiển thiết bị là một ví dụ thường được nhắc đến của Bridge, nhưng tôi nói đó là Cầu nếu bạn đang xác định thông số giao diện cho nhà cung cấp thiết bị, nhưng đó là Bộ điều hợp nếu bạn đang sử dụng trình điều khiển thiết bị hiện có và tạo lớp bọc cho cung cấp một giao diện hợp nhất.
Vì vậy, mã khôn ngoan, hai mẫu rất giống nhau. Kinh doanh khôn ngoan, họ khác biệt.
Xem thêm http://c2.com/cgi/wiki?BridgePotype
Theo kinh nghiệm của tôi, Bridge là một mô hình khá thường xuyên, bởi vì đó là giải pháp bất cứ khi nào có hai kích thước trực giao trong miền . Ví dụ: hình dạng và phương pháp vẽ, hành vi và nền tảng, định dạng tệp và tuần tự hóa, v.v.
Và một lời khuyên: luôn luôn nghĩ về các mẫu thiết kế từ quan điểm khái niệm , không phải từ quan điểm thực hiện. Từ quan điểm đúng đắn, Bridge không thể bị nhầm lẫn với Adaptor, bởi vì chúng giải quyết một vấn đề khác và thành phần vượt trội hơn so với thừa kế không phải vì lợi ích của chính nó, mà vì nó cho phép xử lý các mối quan tâm trực giao một cách riêng biệt.
Mục đích của Bridge và Adaptor là khác nhau và chúng tôi cần cả hai mẫu riêng biệt.
Mẫu cầu:
Sử dụng mẫu Cầu khi:
@ John Sonmez trả lời rõ ràng cho thấy hiệu quả của mô hình cầu trong việc giảm phân cấp lớp.
Bạn có thể tham khảo liên kết tài liệu bên dưới để hiểu rõ hơn về mẫu cầu với ví dụ mã
Mẫu bộ điều hợp :
Sự khác biệt chính:
Câu hỏi SE liên quan với sơ đồ UML và mã làm việc:
Sự khác biệt giữa mẫu cầu và mẫu bộ điều hợp
Bài viết hữu ích:
bài viết mô hình cầu chua
bài viết mô hình bộ điều hợp nguồn
cầu journaldevbài viết mô hình
BIÊN TẬP:
Ví dụ về thế giới thực của mẫu cầu nối (Theo đề xuất của meta.stackoverflow.com, ví dụ trang web tài liệu được kết hợp trong bài đăng này vì tài liệu sẽ được đặt trước)
Mô hình cầu tách rời sự trừu tượng khỏi việc thực hiện để cả hai có thể thay đổi độc lập. Nó đã đạt được với thành phần hơn là kế thừa.
Mẫu cầu nối UML từ Wikipedia:
Bạn có bốn thành phần trong mô hình này.
Abstraction
: Nó xác định một giao diện
RefinedAbstraction
: Nó thực hiện trừu tượng:
Implementor
: Nó xác định một giao diện để thực hiện
ConcreteImplementor
: Nó thực hiện giao diện Triển khai.
The crux of Bridge pattern :
Hai hệ thống phân cấp lớp trực giao sử dụng thành phần (và không kế thừa). Hệ thống phân cấp trừu tượng và phân cấp thực hiện có thể thay đổi độc lập. Thực hiện không bao giờ đề cập đến Trừu tượng. Trừu tượng chứa giao diện triển khai như một thành viên (thông qua thành phần). Thành phần này làm giảm thêm một cấp bậc phân cấp thừa kế.
Trường hợp sử dụng từ thật:
Cho phép các phương tiện khác nhau có cả hai phiên bản của hệ thống hộp số tay và tự động.
Mã ví dụ:
/* Implementor interface*/
interface Gear{
void handleGear();
}
/* Concrete Implementor - 1 */
class ManualGear implements Gear{
public void handleGear(){
System.out.println("Manual gear");
}
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
public void handleGear(){
System.out.println("Auto gear");
}
}
/* Abstraction (abstract class) */
abstract class Vehicle {
Gear gear;
public Vehicle(Gear gear){
this.gear = gear;
}
abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
public Car(Gear gear){
super(gear);
// initialize various other Car components to make the car
}
public void addGear(){
System.out.print("Car handles ");
gear.handleGear();
}
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
public Truck(Gear gear){
super(gear);
// initialize various other Truck components to make the car
}
public void addGear(){
System.out.print("Truck handles " );
gear.handleGear();
}
}
/* Client program */
public class BridgeDemo {
public static void main(String args[]){
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
gear = new ManualGear();
vehicle = new Truck(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Truck(gear);
vehicle.addGear();
}
}
đầu ra:
Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear
Giải trình:
Vehicle
là một sự trừu tượng. Car
và Truck
là hai triển khai cụ thể của Vehicle
.Vehicle
định nghĩa một phương thức trừu tượng : addGear()
.Gear
là giao diện người thực hiệnManualGear
và AutoGear
là hai triển khai của Gear
Vehicle
chứa implementor
giao diện hơn là thực hiện giao diện. Compositon
Giao diện của người triển khai là mấu chốt của mẫu này: Nó cho phép trừu tượng hóa và triển khai thay đổi độc lập. Car
và Truck
định nghĩa triển khai (trừu tượng được định nghĩa lại) cho trừu tượng addGear()
:: Nó chứa Gear
- Hoặc Manual
hoặcAuto
Ca sử dụng cho mẫu Cầu :
Tôi đã sử dụng mô hình cầu tại nơi làm việc. Tôi lập trình trong C ++, nơi nó thường được gọi là thành ngữ PIMPL (con trỏ để thực hiện). Nó trông như thế này:
class A
{
public:
void foo()
{
pImpl->foo();
}
private:
Aimpl *pImpl;
};
class Aimpl
{
public:
void foo();
void bar();
};
Trong ví dụ class A
này chứa giao diện và class Aimpl
chứa việc thực hiện.
Một cách sử dụng cho mẫu này là chỉ hiển thị một số thành viên công khai của lớp thực hiện, nhưng không sử dụng cho các thành viên khác. Trong ví dụ chỉ Aimpl::foo()
có thể được gọi thông qua giao diện chung của A
, nhưng khôngAimpl::bar()
Một ưu điểm khác là bạn có thể xác định Aimpl
trong một tệp tiêu đề riêng biệt mà người dùng không cần đưa vào A
. Tất cả những gì bạn phải làm là sử dụng khai báo chuyển tiếp Aimpl
trước đó A
được xác định và di chuyển các định nghĩa của tất cả các hàm thành viên tham chiếu pImpl
vào tệp .cpp. Điều này cung cấp cho bạn khả năng giữ Aimpl
tiêu đề riêng tư và giảm thời gian biên dịch.
Để đặt ví dụ hình dạng trong mã:
#include<iostream>
#include<string>
#include<cstdlib>
using namespace std;
class IColor
{
public:
virtual string Color() = 0;
};
class RedColor: public IColor
{
public:
string Color()
{
return "of Red Color";
}
};
class BlueColor: public IColor
{
public:
string Color()
{
return "of Blue Color";
}
};
class IShape
{
public:
virtual string Draw() = 0;
};
class Circle: public IShape
{
IColor* impl;
public:
Circle(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Circle "+ impl->Color();
}
};
class Square: public IShape
{
IColor* impl;
public:
Square(IColor *obj):impl(obj){}
string Draw()
{
return "Drawn a Square "+ impl->Color();;
}
};
int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();
IShape* sq = new Square(red);
IShape* cr = new Circle(blue);
cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();
delete red;
delete blue;
return 1;
}
Đầu ra là:
Drawn a Square of Red Color
Drawn a Circle of Blue Color
Lưu ý sự dễ dàng mà màu sắc và hình dạng mới có thể được thêm vào hệ thống mà không dẫn đến sự bùng nổ của các lớp con do hoán vị.
Bạn đang làm việc cho một công ty bảo hiểm nơi bạn phát triển một ứng dụng quy trình công việc quản lý các loại nhiệm vụ khác nhau: kế toán, hợp đồng, yêu cầu bồi thường. Đây là sự trừu tượng. Về phía triển khai, bạn phải có khả năng tạo các tác vụ từ các nguồn khác nhau: email, fax, tin nhắn điện tử.
Bạn bắt đầu thiết kế của bạn với các lớp này:
public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
Bây giờ, vì mỗi nguồn phải được xử lý theo một cách cụ thể, bạn quyết định chuyên môn hóa từng loại nhiệm vụ:
public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}
public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}
public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}
Bạn kết thúc với 13 lớp. Thêm một loại nhiệm vụ hoặc một loại nguồn trở nên thách thức. Sử dụng mẫu cầu tạo ra một cái gì đó dễ bảo trì hơn bằng cách tách rời tác vụ (sự trừu tượng hóa) khỏi nguồn (đó là một mối quan tâm thực hiện):
// Source
public class Source {
public string GetSender();
public string GetMessage();
public string GetContractReference();
(...)
}
public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}
// Task
public class Task {
public Task(Source source);
(...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}
Thêm một loại nhiệm vụ hoặc một nguồn bây giờ dễ dàng hơn nhiều.
Lưu ý: Hầu hết các nhà phát triển sẽ không tạo ra 13 lớp phân cấp trả trước để xử lý vấn đề này. Tuy nhiên, trong cuộc sống thực, bạn có thể không biết trước số loại nguồn và loại nhiệm vụ; nếu bạn chỉ có một nguồn và hai loại nhiệm vụ, có thể bạn sẽ không tách rời Nhiệm vụ khỏi Nguồn. Sau đó, độ phức tạp tổng thể tăng lên khi các nguồn và loại nhiệm vụ mới được thêm vào. Tại một số điểm, bạn sẽ cấu trúc lại và, thường xuyên nhất, kết thúc bằng một giải pháp giống như cây cầu.
Bridge design pattern we can easily understand helping of service and dao layer.
Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
void save(T t);
}
concrete implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
private Dao<Account> accountDao;
public AccountService(AccountDao dao){
this.accountDao=dao;
}
public void save(Account){
accountDao.save(Account);
}
}
login service-
public class LoginService<Login> implement BasicService<Login>{
private Dao<Login> loginDao;
public AccountService(LoginDao dao){
this.loginDao=dao;
}
public void save(Login){
loginDao.save(login);
}
}
public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}