在学习了适配器模式如何解决接口不兼容问题后,我们来看看桥接模式。桥接模式是一种更侧重于系统设计的模式,它旨在将抽象部分与其实现部分分离,使它们可以独立地变化。
“将抽象部分与它的实现部分分离,使它们都可以独立地变化。” (Decouple an abstraction from its implementation so that the two can vary independently.)
想象一下,你正在开发一个跨平台的图形绘制程序。你有不同的形状(如圆形、矩形、三角形),这些形状需要在不同的操作系统上绘制(如Windows、Linux、macOS)。
draw()
这样的抽象操作。如果直接让每个形状去适配每个平台的API,比如 WindowsCircle
, LinuxCircle
, MacCircle
, WindowsRectangle
… 这样会导致类的数量急剧膨胀(形状数量 x 平台数量),并且每当增加一种新形状或支持一个新平台时,都需要创建大量新类,维护困难。
桥接模式通过引入一个“桥梁”来连接抽象部分和实现部分,使得它们可以各自独立地演化。
桥接模式的主要目的:
遥控器与电视机:
RemoteControl
)。它有 turnOn()
, turnOff()
, changeChannel()
等操作。SonyRemoteControl
, SamsungRemoteControl
。它们可能有一些特定的按钮或功能,但都基于通用的遥控操作。TV
)。它定义了电视机实际执行操作的接口,如 powerOn()
, powerOff()
, tuneChannel()
。SonyTV
, SamsungTV
。它们各自实现了 TV
接口。TV
接口的引用)来控制电视机(实现)。你可以用一个通用的索尼遥控器去控制不同型号的索尼电视,也可以为新的电视品牌设计新的电视实现,而遥控器的设计基本不变。开关与电器:
Switch
)。它有 on()
和 off()
方法。WallSwitch
)、遥控开关(RemoteSwitch
)。Appliance
)。它定义了电器被操作的接口,如 turnOnAppliance()
, turnOffAppliance()
。Light
)、风扇(Fan
)。Appliance
接口,现有的开关类型仍然可用。不同类型的画笔和颜色:
Pen
)。它有 drawShape()
方法。BrushPen
)、蜡笔(CrayonPen
)。Color
)。它定义了 applyColor()
方法。RedColor
)、蓝色(BlueColor
)。桥接模式通常包含以下角色:
工作流程:
ConcreteImplementor
对象和一个 RefinedAbstraction
对象。RefinedAbstraction
对象在构造时会持有一个 ConcreteImplementor
对象的引用(通过 Implementor
接口)。RefinedAbstraction
的操作时,这些操作会委托给其持有的 Implementor
对象去执行实际的底层操作。优点:
RefinedAbstraction
或一个新的 ConcreteImplementor
都很方便。缺点:
让我们通过一个“消息发送”的例子来说明桥接模式。我们有不同类型的消息(如文本消息、图片消息),这些消息需要通过不同的方式发送(如邮件、短信、App推送)。
// message_sender.go (Implementor)
package messaging
// MessageSender 实现类接口
type MessageSender interface {
Send(messageContent string, recipient string) error
}
// MessageSender.java (Implementor)
package com.example.messaging.sender;
// 实现类接口
public interface MessageSender {
void sendMessage(String messageContent, String recipient);
}
// email_sender.go (ConcreteImplementor)
package messaging
import "fmt"
// EmailSender 具体实现类
type EmailSender struct{}
func (s *EmailSender) Send(messageContent string, recipient string) error {
fmt.Printf("Sending EMAIL to %s: %s\n", recipient, messageContent)
// 实际的邮件发送逻辑
return nil
}
// sms_sender.go (ConcreteImplementor)
package messaging
import "fmt"
// SMSSender 具体实现类
type SMSSender struct{}
func (s *SMSSender) Send(messageContent string, recipient string) error {
fmt.Printf("Sending SMS to %s: %s\n", recipient, messageContent)
// 实际的短信发送逻辑
return nil
}
// EmailSender.java (ConcreteImplementor)
package com.example.messaging.sender;
public class EmailSender implements MessageSender {
@Override
public void sendMessage(String messageContent, String recipient) {
System.out.printf("Sending EMAIL to %s: %s%n", recipient, messageContent);
// 实际的邮件发送逻辑
}
}
// SMSSender.java (ConcreteImplementor)
package com.example.messaging.sender;
public class SMSSender implements MessageSender {
@Override
public void sendMessage(String messageContent, String recipient) {
System.out.printf("Sending SMS to %s: %s%n", recipient, messageContent);
// 实际的短信发送逻辑
}
}
// notification.go (Abstraction)
package messaging
// Notification 抽象类
type Notification struct {
sender MessageSender // 持有实现部分的引用
}
func NewNotification(sender MessageSender) *Notification {
return &Notification{sender: sender}
}
// SetSender 允许动态改变发送方式
func (n *Notification) SetSender(sender MessageSender) {
n.sender = sender
}
// Notify 抽象方法,由子类实现具体的消息格式化和发送调用
// 注意:在Go中,通常通过接口组合或嵌入来实现类似抽象类的效果,
// 或者让具体通知类型直接持有sender并实现一个通用接口。
// 这里为了更贴合经典UML结构,我们让具体通知类型持有sender。
// 或者,我们可以定义一个 Notify 方法,它接收格式化后的消息。
func (n *Notification) SendNotification(formattedMessage string, recipient string) error {
if n.sender == nil {
return fmt.Errorf("sender not set")
}
return n.sender.Send(formattedMessage, recipient)
}
// Notification.java (Abstraction)
package com.example.messaging.notification;
import com.example.messaging.sender.MessageSender;
// 抽象类
public abstract class Notification {
protected MessageSender sender; // 持有实现部分的引用
public Notification(MessageSender sender) {
System.out.println("Notification: Initialized with sender: " + sender.getClass().getSimpleName());
this.sender = sender;
}
// 允许动态改变发送方式
public void setSender(MessageSender sender) {
System.out.println("Notification: Sender changed to: " + sender.getClass().getSimpleName());
this.sender = sender;
}
// 抽象方法,由子类实现具体的消息格式化和发送调用
public abstract void notifyUser(String message, String recipient);
}
// text_message.go (RefinedAbstraction)
package messaging
import "fmt"
// TextNotification 精确抽象
type TextNotification struct {
Notification // 嵌入 Notification,继承其字段和方法
}
func NewTextNotification(sender MessageSender) *TextNotification {
return &TextNotification{
Notification: *NewNotification(sender),
}
}
func (tn *TextNotification) Send(text string, recipient string) error {
formattedMessage := fmt.Sprintf("[Text]: %s", text)
fmt.Printf("TextNotification: Formatting message for %s\n", recipient)
return tn.SendNotification(formattedMessage, recipient) // 调用父类(嵌入结构)的方法
}
// image_message.go (RefinedAbstraction)
package messaging
import "fmt"
// ImageNotification 精确抽象
type ImageNotification struct {
Notification
}
func NewImageNotification(sender MessageSender) *ImageNotification {
return &ImageNotification{
Notification: *NewNotification(sender),
}
}
func (in *ImageNotification) Send(imageURL string, caption string, recipient string) error {
formattedMessage := fmt.Sprintf("[Image]: URL=%s, Caption='%s'", imageURL, caption)
fmt.Printf("ImageNotification: Formatting message for %s\n", recipient)
return in.SendNotification(formattedMessage, recipient)
}
// TextNotification.java (RefinedAbstraction)
package com.example.messaging.notification;
import com.example.messaging.sender.MessageSender;
public class TextNotification extends Notification {
public TextNotification(MessageSender sender) {
super(sender);
System.out.println("TextNotification: Created.");
}
@Override
public void notifyUser(String message, String recipient) {
String formattedMessage = "[Text]: " + message;
System.out.println("TextNotification: Formatting message for " + recipient);
sender.sendMessage(formattedMessage, recipient); // 调用实现部分的方法
}
}
// ImageNotification.java (RefinedAbstraction)
package com.example.messaging.notification;
import com.example.messaging.sender.MessageSender;
public class ImageNotification extends Notification {
private String imageURL;
public ImageNotification(MessageSender sender, String imageURL) {
super(sender);
this.imageURL = imageURL;
System.out.println("ImageNotification: Created with URL: " + imageURL);
}
@Override
public void notifyUser(String caption, String recipient) {
String formattedMessage = String.format("[Image]: URL=%s, Caption='%s'", this.imageURL, caption);
System.out.println("ImageNotification: Formatting message for " + recipient);
sender.sendMessage(formattedMessage, recipient);
}
}
// main.go (示例用法)
/*
package main
import (
"./messaging"
"fmt"
)
func main() {
// 创建实现部分的对象
emailSender := &messaging.EmailSender{}
smsSender := &messaging.SMSSender{}
// 1. 发送文本消息通过邮件
fmt.Println("--- Sending Text via Email ---")
textMail := messaging.NewTextNotification(emailSender)
err := textMail.Send("Hello from Go Bridge Pattern!", "[email protected]")
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println("\n--- Sending Image via SMS ---")
// 2. 发送图片消息通过短信
imageSMS := messaging.NewImageNotification(smsSender)
err = imageSMS.Send("http://example.com/image.jpg", "Check out this cool pic!", "+1234567890")
if err != nil {
fmt.Println("Error:", err)
}
fmt.Println("\n--- Sending Text via SMS (changing sender for existing notification object) ---")
// 3. 同一个文本消息对象,改变发送方式为短信
textMail.SetSender(smsSender) // 动态改变实现部分
err = textMail.Send("This is a test text message via SMS now!", "+0987654321")
if err != nil {
fmt.Println("Error:", err)
}
// 4. 增加新的发送方式 (e.g., AppPushSender)
// 只需要创建 AppPushSender struct 实现 MessageSender 接口
// appPushSender := &messaging.AppPushSender{}
// textPush := messaging.NewTextNotification(appPushSender)
// textPush.Send("Push notification content", "userID123")
// 5. 增加新的消息类型 (e.g., VideoNotification)
// 只需要创建 VideoNotification struct 嵌入 Notification
// 并实现其特定的 Send 方法 (如 Send(videoURL, title, recipient))
// videoMail := messaging.NewVideoNotification(emailSender)
// videoMail.Send("http://example.com/video.mp4", "My new video", "[email protected]")
}
*/
// Main.java (示例用法)
/*
package com.example;
import com.example.messaging.notification.ImageNotification;
import com.example.messaging.notification.Notification;
import com.example.messaging.notification.TextNotification;
import com.example.messaging.sender.EmailSender;
import com.example.messaging.sender.MessageSender;
import com.example.messaging.sender.SMSSender;
// import com.example.messaging.sender.AppPushSender; // 假设新增了App推送
public class Main {
public static void main(String[] args) {
// 创建实现部分的对象
MessageSender emailSender = new EmailSender();
MessageSender smsSender = new SMSSender();
// MessageSender appPushSender = new AppPushSender(); // 新增的发送方式
System.out.println("--- Sending Text via Email ---");
// 1. 发送文本消息通过邮件
Notification textMail = new TextNotification(emailSender);
textMail.notifyUser("Hello from Java Bridge Pattern!", "[email protected]");
System.out.println("\n--- Sending Image via SMS ---");
// 2. 发送图片消息通过短信
Notification imageSMS = new ImageNotification(smsSender, "http://example.com/image.jpg");
imageSMS.notifyUser("Check out this cool pic!", "+1234567890");
System.out.println("\n--- Sending Text via SMS (changing sender for existing notification object) ---");
// 3. 同一个文本消息对象,改变发送方式为短信
textMail.setSender(smsSender); // 动态改变实现部分
textMail.notifyUser("This is a test text message via SMS now!", "+0987654321");
// System.out.println("\n--- Sending Text via App Push ---");
// 4. 使用新的发送方式 (如果 AppPushSender 已添加)
// Notification textPush = new TextNotification(appPushSender);
// textPush.notifyUser("This is an App Push notification!", "userID789");
// 5. 增加新的消息类型 (e.g., VideoNotification)
// 只需要创建 VideoNotification class 继承 Notification
// 并实现其特定的 notifyUser 方法
// Notification videoEmail = new VideoNotification(emailSender, "http://example.com/video.mp4");
// videoEmail.notifyUser("My new video", "[email protected]");
}
}
*/
桥接模式和适配器模式都用于处理不同类之间的协作,但它们的意图和结构不同:
意图 (Intent):
结构 (Structure):
解决的问题:
简单来说,适配器是让“旧的”能“用起来”,桥接是让“新的”能“灵活组合和扩展”。
桥接模式是一种强大的结构型模式,它通过将抽象和实现分离到不同的类层次结构中,使得它们可以独立地进行扩展和修改。这对于处理具有多个变化维度的系统非常有用,可以有效地控制类的数量,提高系统的灵活性和可维护性。
记住它的核心:分离抽象与实现,独立变化。