写在前面
Hello,我是易元 ,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!
本文为设计模式间的组合使用,涉及代码较多,个人觉得熟能生巧,希望自己能从中学习到新的思维、新的灵感,希望大家也能有所收获。
接上一篇文章 《模式组合应用-桥接模式(一)》
桥接模式+装饰器模式
装饰器是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。
场景
在一个消息通知系统中,我们需要支持多种通知渠道(例如: 邮件、短信、微信),同时每种通知又可能需要附加额外的功能(例如: 加密、压缩、日志记录等)。
各模式职责
桥接模式
: 分离通知类型和通知发送方式,将通知类型做为抽象部分,具体通知发送方式做为实现部分。装饰器模式
: 动态地给通知添加额外的功能,无需修改原始结构。
具体结构设计
桥接模式
抽象部分
:Notification
定义通知的抽象类,持有对MessageSender
的引用。扩展抽象部分
:UrgentNotification
、GeneralNotification
等具体通知类型。实现部分接口
:MessageSender
定义消息发送的接口。具体实现者
:EmailSender
、SmsSender
、WeChatSender
等具体消息发送器。
装饰器模式
抽象组件
:MessageSender
接口(同时作为桥接模式的实现部分接口)。具体组件
:EmailSender
、SmsSender
、WeChatSender
(同时作为桥接模式的具体实现者)。抽象装饰器
:MessageSenderDecorator
实现了MessageSender
接口,并持有一个MessageSender
引用。具体装饰器
:EncrptedSenderDecorator
、CompressedSenderDecorator
、LoggingSenderDecoratot
等,为消息发送添加特定功能。
代码示例
1. MessageSender
arduino
/**
* 消息发送接口
* <p>
* 桥接模式(实现部分接口)
* 装饰器模式(抽象组件)
*/
public interface MessageSender {
void send(String message);
}
- 在桥接模式中,担任实现部分接口,定义了消息发送的通用行为。
- 在装饰器模式中,充当了 抽象组件 的角色,定义了可以被装饰的对象的基本接口。
send(String message)
方法是所有消息发送器必须实现的方法。
2. EmailSender
SmsSender
WeChatSender
typescript
/**
* 邮件发送器
* <p>
* 桥接模式(具体实现者)
* 装饰器模式(具体组件)
*/
public class EmailSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送邮件消息: " + message);
}
}
/**
* 短信发送器
* <p>
* 桥接模式(具体实现者)
* 装饰器模式(具体组件)
*/
public class SmsSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送短信消息: " + message);
}
}
/**
* 微信发送器
* <p>
* 桥接模式(具体实现者)
* 装饰器模式(具体组件)
*/
public class WeChatSender implements MessageSender {
@Override
public void send(String message) {
System.out.println("发送微信消息: " + message);
}
}
- 在桥接模式中,它们是具体实现者角色,提供了通过不同渠道发送消息的具体实现。
- 在装饰器模式中,它们是具体组件角色,是装饰器可以包装的原始对象。
3. MessageSenderDecorator
typescript
/**
* 消息发送器装饰类
* <p>
* 装饰器模式(抽象装饰器)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public abstract class MessageSenderDecorator implements MessageSender {
protected MessageSender decoratorSender;
public MessageSenderDecorator(MessageSender decoratorSender) {
this.decoratorSender = decoratorSender;
}
@Override
public void send(String message) {
decoratorSender.send(message);
}
}
- 装饰器模式中的 抽象装饰器 ,它实现了
MessageSender
接口,并持有一个MessageSender
类型的引用(decoratedSender
)。 send()
方法默认只调用decoratedSedner.send()
,子类可以在此基础上天啊及额外的行为。
4. EncryptedSenderDecorator
CompressedSenderDecorator
LoggingSenderDecorator
java
/**
* 消息压缩发送器
* <p>
* 装饰器模式(具体装饰器)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public class CompressedSenderDecorator extends MessageSenderDecorator {
public CompressedSenderDecorator(MessageSender decoratorSender) {
super(decoratorSender);
}
@Override
public void send(String message) {
String compressedMessage = "[ZIP]" + message + "[/ZIP]";
System.out.println("消息压缩中 ... ");
super.send(compressedMessage);
}
}
/**
* 消息加密发送器
* <p>
* 装饰器模式(具体装饰器)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public class EncryptedSenderDecorator extends MessageSenderDecorator {
public EncryptedSenderDecorator(MessageSender decoratorSender) {
super(decoratorSender);
}
@Override
public void send(String message) {
String encryptedMessage = "[AES]" + message + "[/AES]";
System.out.println("消息加密中 ... ");
super.send(encryptedMessage);
}
}
/**
* 日志记录器
* <p>
* 装饰器模式(具体装饰器)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public class LoggingSenderDecorator extends MessageSenderDecorator {
public LoggingSenderDecorator(MessageSender decoratorSender) {
super(decoratorSender);
}
@Override
public void send(String message) {
System.out.println("记录消息发送日志: " + message);
super.send(message);
}
}
- 装饰器模式中的具体装饰器,继承自
MessageSenderDecorator
- 每个装饰器都在调用
super.send()
之前或之后添加了特定的功能(加密、压缩、记录日志) - 装饰器之间可以任意组合,进而实现多种功能叠加。
5. Notification
java
/**
* 消息通知类型
* <p>
* 桥接模式(抽象部分)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public abstract class Notification {
protected MessageSender sender;
public Notification(MessageSender sender) {
this.sender = sender;
}
public void setSender(MessageSender sender) {
this.sender = sender;
}
public abstract void notifyUser(String message);
}
- 桥接模式中的 抽象部分,维护一个
MessageSender
类型的引用,作为通知类型与消息发送方式之间的桥梁。 setSender()
方法允许在运行时动态改变通知的发送方式notifyUser()
抽象方法,由具体的通知类型实现其特有的逻辑通知。
6. UrgentNotification
GeneralNotification
java
/**
* 紧急消息
* <p>
* 桥接模式(抽象扩展部分)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public class UrgentNotification extends Notification {
public UrgentNotification(MessageSender sender) {
super(sender);
}
@Override
public void notifyUser(String message) {
System.out.println("紧急消息: ");
sender.send("[紧急]" + message);
}
}
/**
* 普通消息
* <p>
* 桥接模式(抽象扩展部分)
*
* @author YiYuan
* @data 2025/8/18
* @apoNote
*/
public class GeneralNotification extends Notification {
public GeneralNotification(MessageSender sender) {
super(sender);
}
@Override
public void notifyUser(String message) {
System.out.println("普通消息: ");
sender.send(message);
}
}
- 桥接模式中的 抽象部分扩展,代表了不同优先级的通知类型。
- 通过调用
sender.send()
方法,将实际的消息发送工作委托给具体的MessageSender
实现(这个实现可能已经被一个或多个装饰器包装)
7. 测试类
java
public class MessageSenderTest {
@Test
public void test_bridge() {
System.out.println("\n--- 桥接模式 ---");
MessageSender emailSender = new EmailSender();
Notification urgentEmail = new UrgentNotification(emailSender);
urgentEmail.notifyUser("检测到系统出现异常!");
System.out.println();
MessageSender smsSender = new SmsSender();
Notification generalSms = new GeneralNotification(smsSender);
generalSms.notifyUser("购买商品已发货。");
}
@Test
public void test_decorator() {
System.out.println("\n--- 装饰器模式 ---");
MessageSender encryptedEmailSender = new EncryptedSenderDecorator(new EmailSender());
encryptedEmailSender.send("机密内容");
System.out.println();
MessageSender compressedSmsSender = new CompressedSenderDecorator(new SmsSender());
compressedSmsSender.send("超长文件内容");
System.out.println();
MessageSender loggingEncryptedWeChatSender = new LoggingSenderDecorator(new EncryptedSenderDecorator(new WeChatSender()));
loggingEncryptedWeChatSender.send("多装饰器使用");
}
@Test
public void test_bridge_decorator() {
System.out.println("\n--- 桥接模式与装饰器模式组合应用 ---");
MessageSender encryptedEmail = new EncryptedSenderDecorator(new EmailSender());
Notification urgentEncryptedEmail = new UrgentNotification(encryptedEmail);
urgentEncryptedEmail.notifyUser("邮件加密发送");
System.out.println();
MessageSender loggingCompressedSms = new LoggingSenderDecorator(new CompressedSenderDecorator(new SmsSender()));
Notification generalLoggingCompressedSms = new GeneralNotification(loggingCompressedSms);
generalLoggingCompressedSms.notifyUser("短信发送普通超长内容");
System.out.println();
System.out.println("\n--- 动态切换通知的发送方式和附加功能 ---");
Notification dynamicNotification = new GeneralNotification(new EmailSender());
dynamicNotification.notifyUser("一般消息内容");
System.out.println("\n切换发送方式");
dynamicNotification.setSender(new EncryptedSenderDecorator(new SmsSender()));
dynamicNotification.notifyUser("保密消息内容");
System.out.println("\n切换发送方式");
dynamicNotification.setSender(new LoggingSenderDecorator(new WeChatSender()));
dynamicNotification.notifyUser("二级消息内容");
}
}
test_bridge
展示了如何创建不同类型的通知(UrgentNotification
GeneralNotification
)并结合不同的发送器(EmailSender
SmsSender
)test_decorator
展示了如何通过装饰器为MessageSender
添加功能,例如加密邮件发送器、压缩短信发送器,以及多重装饰(带日志写入的加密微信发送器)test_bridge_decorator
展示了如何将桥接模式中的MessageSender
接口与装饰器模式结合,意味着通知类型(抽象部分)可以与任何被装饰 过的消息发送器(实现部分)组合。例如,一个紧急通知可以通过一个加密的邮件发送器发送,而普通通知可以通过一个带日志写入和压缩功能的短信发送器发送。
组合优势
- 功能动态扩展: 装饰器模式允许在运行时动态地为消息发送器添加新的功能(如加密、压缩、日志),而无需修改其核心代码,这与桥接模式的解耦 能力相结合,使得通知系统在功能和渠道两个维度上都具有极高的灵活性。
- 避免类爆炸: 如果使用继承来添加功能,会导致大量的子类组合,装饰器模式避免了这种类爆炸,使得功能可以独立地添加和组合。
- 职责清晰: 桥接模式分离了通知类型和发送机制,装饰器模式分离了核心发送功能和附加功能,功能的划分使得系统更易于理解和维护。
- 高度可配置: 客户端可以根据需要自由组合不同的通知类型、发送渠道和附加功能,创建出高度定制化的通知行为。
桥接模式+模板方法模式
模板方法模式,行为设计模式,在超类中定义一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。
场景
在文档处理系统中,需要支持多种文档类型(例如: 报告、合同、发票),每种文档的生成过程都有固定的步骤(例如: 数据加载、内容格式化、保存), 但具体的格式化方式可能因输出格式(例如: 纯文本、HTML、PDF)而异。
模式职责
- 桥接模式: 将文档类型(抽象部分) 与具体的文档输出格式(实现部分)解耦
- 模板方法: 定义文档生成过程的骨架,将具体步骤的实现延迟到子类。
具体设计
桥接模式
- 抽象部分:
Document
定义文档的抽象类,持有对DocumentFormatter
的引用。 - 扩展抽象部分: 具体文档类型
ReportDocument
ContractDocument
InvoiceDocument
等。 - 实现者接口:
DocumentFormantter
定义了文档格式化的接口。 - 具体实现者: 具体格式化器,包含
PlaninTextFormatter
HtmlFormatter
PdfFormatter
等。
模板方法模式
- 抽象类:
Document
抽象类(同时作为桥接模式的抽象部分), 定义了generate()
模板方法,其中包含固定的文档生成步骤(loadData()
formatContent()
saveDocument()
)。 - 具体方法:
loadData()
和saveDocument()
可以是具体方法也可以是钩子方法。 - 抽象方法/钩子方法:
formatContent()
是一个抽象方法, 有子类(ReportDocument
ContractDocument
InvoiceDocument
) 实现, 并利用桥接模式的DocumentFormatter
来完成具体的格式化。
代码示例
1. DocumentFormatter
arduino
/**
* 文档格式化接口
* <p>
* 桥接模式(实现者接口)
*/
public interface DocumentFormatter {
String format(String content);
}
- 桥接模式中的 实现者接口,定义了文档内容如何格式化的通用方法,
format(String content)
接收原始内容并返回格式化后的字符串。
2. PlainTextFormatter
HtmlFormatter
PdfFormatter
typescript
/**
* 纯文本格式化类
* <p>
* 桥接模式(具体实现者)
*/
public class PlainTextFormatter implements DocumentFormatter {
@Override
public String format(String content) {
return "[纯文本] " + content;
}
}
/**
* HTML格式化类
* <p>
* 桥接模式(具体实现者)
*/
public class HtmlFormatter implements DocumentFormatter{
@Override
public String format(String content) {
return "<html><body><p>" + content + "</p></body></html>";
}
}
/**
* PDF格式化类
* <p>
* 桥接模式(具体实现者)
*/
public class PdfFormatter implements DocumentFormatter {
@Override
public String format(String content) {
return "[PDF] " + content;
}
}
- 桥接模式中的 具体实现者,分别实现了将文档内容格式化为纯文本、HTML 和 模拟PDF格式的逻辑。
3. Documnet
csharp
/**
* 文档类
* <p>
* 桥接模式(抽象部分)/模板方法模式(抽象类)
*/
public abstract class Document {
protected DocumentFormatter formatter;
public Document(DocumentFormatter formatter) {
this.formatter = formatter;
}
public void setFormatter(DocumentFormatter formatter) {
this.formatter = formatter;
}
public final void generate() {
loadData();
String formatContent = this.formatContent();
this.saveDocument(formatContent);
System.out.println("文档生成成功。");
}
protected void loadData() {
System.out.println("加载文档数据...");
}
protected abstract String formatContent();
protected void saveDocument(String content) {
System.out.println("将内容保存至文档: " + content);
}
}
- 桥接模式中的 抽象部分,维护了一个
DocumentFormatter
类型的引用,作为文档类型与格式化器直接的桥梁,setFormatter()
允许动态 切换格式化器。 - 模板方法模式的 抽象类,定义了
generate()
模板方法,该方法封装了文档生成过程的固定骨架:loadData()
->formatContent()
->saveDocument()
。 loadData()
和saveDocument()
是具体方法,提供了通用的实现。formatContent()
是一个抽象方法,它将具体的格式化逻辑延迟到子类实现。
4. ReportDocument
ContractDocument
typescript
/**
* 报告文件
* <p>
* 桥接模式(扩展抽象部分)/模板方法模式(具体子类)
*/
public class ReportDocument extends Document {
private String reportData;
public ReportDocument(DocumentFormatter formatter, String reportData) {
super(formatter);
this.reportData = reportData;
}
@Override
protected String formatContent() {
System.out.println("--- 格式化报表内容 ---");
return formatter.format("报告数据: " + reportData);
}
@Override
protected void loadData() {
super.loadData();
System.out.println("加载特定报表数据...");
}
}
/**
* 合同文件
* <p>
* 桥接模式(扩展抽象部分)/模板方法模式(具体子类)
*/
public class ContractDocument extends Document {
private String contractDetails;
public ContractDocument(DocumentFormatter formatter, String contractDetails) {
super(formatter);
this.contractDetails = contractDetails;
}
@Override
protected String formatContent() {
System.out.println("--- 格式化合同文件内容 ---");
return formatter.format(contractDetails);
}
}
- 桥接模式中的 扩展抽象部分,代表具体的文档类型。
- 模板方法模式中的 具体子类,继承自
Dcoument
抽象类,并实现了formatContent()
抽象方法,在formatContent()
中,通过调用formatter.format()
方法,将实际的格式化工作委托给具体的DocumentFormatter
实现。 ReportDocument
中重写了loadData()
方法,以展示子类可以扩展或修改模板方法中的特定步骤。
5. 测试类
ini
@Test
public void test_bridge_template() {
DocumentFormatter pdfFormatter = new PdfFormatter();
ReportDocument salesReport = new ReportDocument(pdfFormatter, "小米SU7销售报表");
System.out.println("PDF格式的销售报表: ");
salesReport.generate();
System.out.println();
DocumentFormatter htmlFormatter = new HtmlFormatter();
ContractDocument serviceContract = new ContractDocument(htmlFormatter, "服务品质协议");
serviceContract.generate();
System.out.println();
System.out.println("--- 切换格式化器 ---");
salesReport.setFormatter(htmlFormatter);
System.out.println("HTML格式的销售报表: ");
salesReport.generate();
}
组合优势
- 流程与实现分离: 模板方法模式定义了固定的操作流程,桥接模式则将流程中的可变部分(具体实现) 抽象出来,并允许其独立变化,使得文档生成 的核心流程保持稳定,而具体格式化细节可以灵活切换。
- 职责明确清晰: 模板方法模式关注 如何执行一系列操作,桥接模式关注 如何将抽象和实现解耦。
桥接模式+工厂方法模式
工厂方法模式,创建型设计模式,其在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。
场景
在一个多媒体播放器应用中,我们需要支持多种播放设备(例如: 电视机、音响、投影仪),并且每种设备都可以播放不同类型的媒体(例如: 电影、 音乐、图片)。同时,为了创建这些媒体播放器,我们希望能够根据不同的设备类型,动态地创建响应的媒体播放器实例。
模式职责
- 桥接模式: 将媒体播放器(抽象部分) 与 具体的播放设备(实现部分) 解耦。
- 工厂方法模式: 定义了一个用于创建媒体播放器对象的接口,由子类进行实例化。
具体设计
桥接模式
- 抽象部分:
MediaPlayer
定义媒体播放器的抽象类,持有对PlaybackDevice
的引用。 - 扩展抽象部分: 具体媒体播放器,
MoviePlayer
MusicPlayer
ImagePlayer
。 - 实现者接口:
PlaybackDevice
定义了播放设备的接口。 - 具体实现者: 具体播放设备,包含
Television
SpeakerSystem
Projector
。
工厂方法模式
- 抽象创建者:
MediaPlayerFactory
定义创建媒体播放器对象的工厂方法。 - 具体创建者:
TelevisionPlayerFactory
SpeakerPlayerFactory
ProjectorPlayerFactory
,每个工厂负责创建特定设备上的媒体播 放器。 - 产品:
MediaPlayer
抽象类,同时作为桥接模式的抽象部分。
代码示例
1. PlaybackDevice
arduino
/**
* 播放设备
* <p>
* 桥接模式(实现者接口)
*/
public interface PlaybackDevice {
void play(String mediaType, String content);
void stop();
}
- 桥接模式中的 实现者接口,定义了播放设备的通用行为,如
play()
和stop()
。
2. Television
SpeakerSystem
Projector
typescript
/**
* 电视机
* <p>
* 桥接模式(具体实现者)
*/
public class Television implements PlaybackDevice {
private String name;
public Television(String name) {
this.name = name;
}
@Override
public void play(String mediaType, String content) {
System.out.println(name + " 电视机播放 " + mediaType + " : " + content);
}
@Override
public void stop() {
System.out.println(name + " 电视机停止播放.");
}
}
/**
* 音响
* <p>
* 桥接模式(具体实现者)
*/
public class SpeakerSystem implements PlaybackDevice {
private String name;
public SpeakerSystem(String name) {
this.name = name;
}
@Override
public void play(String mediaType, String content) {
System.out.println(name + " 音响播放 " + mediaType + ": " + content);
}
@Override
public void stop() {
System.out.println(name + " 音响停止播放. ");
}
}
/**
* 投影仪
* <p>
* 桥接模式(具体实现者)
*/
public class Projector implements PlaybackDevice {
private String name;
public Projector(String name) {
this.name = name;
}
@Override
public void play(String mediaType, String content) {
System.out.println(name + " 投影仪播放 " + mediaType + ": " + content);
}
@Override
public void stop() {
System.out.println(name + " 投影仪停止播放.");
}
}
- 桥接模式中的 具体实现者,分别实现
PlaybackDevice
接口,代表不同的播放设备。
3. MediaPlayer
csharp
/**
* 媒体播放器
* <p>
* 桥接模式(抽象部分) / 工厂方法模式(产品)
*/
public abstract class MediaPlayer {
protected PlaybackDevice device;
public MediaPlayer(PlaybackDevice device) {
this.device = device;
}
public void setDevice(PlaybackDevice device) {
this.device = device;
}
public abstract void playMedia(String content);
public void stopMedia() {
device.stop();
}
}
- 桥接模式中的 抽象部分,维护了一个
PlaybackDevice
类型的引用,提供setDetvice()
方法,允许动态切换播放设备。 - 工厂方法模式的 产品,它是工厂方法模式中呗创建的抽象产品, 定义了抽象方法
playMedia()
,需要具体的媒体播放器实现特有播放逻辑。
4. MoviePlayer
MusicPlayer
ImagePlayer
java
/**
* 电影播放器
* <p>
* 桥接模式(扩展抽象部分)
*/
public class MoviePlayer extends MediaPlayer {
public MoviePlayer(PlaybackDevice device) {
super(device);
}
@Override
public void playMedia(String content) {
System.out.println("播放电影: ");
device.play("电影", content);
}
}
/**
* 音乐播放器
* <p>
* 桥接模式(扩展抽象部分)
*/
public class MusicPlayer extends MediaPlayer {
public MusicPlayer(PlaybackDevice device) {
super(device);
}
@Override
public void playMedia(String content) {
System.out.println("播放音乐: ");
device.play("音乐", content);
}
}
/**
* 图片播放器
* <p>
* 桥接模式(扩展抽象部分)
*/
public class ImagePlayer extends MediaPlayer {
public ImagePlayer(PlaybackDevice device) {
super(device);
}
@Override
public void playMedia(String content) {
System.out.println("播放图片: ");
device.play("图片", content);
}
}
- 桥接模式中的 扩展抽象部分,代表不同类型的媒体播放器。
- 实现
MediaPlayer
抽象方法,通过调用device.play()
方法,将实际的播放工厂委托给具体的PlaybackDevice
实现。
5. MediaPlayerFactory
csharp
/**
* 多媒体播放器工厂
* <p>
* 工厂方法模式(抽象创建者)
*/
public interface MediaPlayerFactory {
MediaPlayer createMoviePlayer();
MediaPlayer createMusicPlayer();
MediaPlayer createImagePlayer();
}
- 工厂方法模式中的 抽象创建者,定义了创建各种媒体播放器(
MoviePlayer
MusicPlayer
ImagePlayer
) 的工厂方法。
6. TelevisionPlayerFactory
SpeakerPlayerFactory
csharp
/**
* 电视播放器工厂
* <p>
* 工厂方法模式(具体创建者)
*/
public class TelevisionPlayerFactory implements MediaPlayerFactory {
private PlaybackDevice television;
public TelevisionPlayerFactory(String name) {
this.television = new Television(name);
}
@Override
public MediaPlayer createMoviePlayer() {
System.out.println("为电视创建电影播放器.");
return new MoviePlayer(television);
}
@Override
public MediaPlayer createMusicPlayer() {
System.out.println("为电视创建音乐播放器.");
return new MusicPlayer(television);
}
@Override
public MediaPlayer createImagePlayer() {
System.out.println("为电视创建图片播放器.");
return new ImagePlayer(television);
}
}
/**
* 音响播放器工厂
* <p>
* 工厂方法模式(具体创建者)
*/
public class SpeakerPlayerFactory implements MediaPlayerFactory {
private PlaybackDevice speakerSystem;
public SpeakerPlayerFactory(String name) {
this.speakerSystem = new SpeakerSystem(name);
}
@Override
public MediaPlayer createMoviePlayer() {
System.out.println("为扬声器创建电影音频播放器.");
return new MoviePlayer(speakerSystem);
}
@Override
public MediaPlayer createMusicPlayer() {
System.out.println("为扬声器创建音乐播放器.");
return new MusicPlayer(speakerSystem);
}
@Override
public MediaPlayer createImagePlayer() {
System.out.println("扬声器无法播放图片信息.");
return null;
}
}
- 工厂方法模式中的 具体创建者,它们实现了
MediaPlayerFacotry
接口。 TelevisionPlayerFactory
负责创建在Television
设备上播放的各种媒体播放器。SpeakerPlayerFactory
负责创建在SpeakerSystem
设备上播放的各种媒体播放器,但createImagePlayer()
方法返回null,音响 系统不支持图片播放,工厂方法可以根据具体创建者的特性来决定是否支持某种产品。
测试类
ini
public class DeviceTest {
@Test
public void test_design_factory() {
MediaPlayerFactory tvFactory = new TelevisionPlayerFactory("小米电视机");
MediaPlayer tvMoviePlayer = tvFactory.createMoviePlayer();
tvMoviePlayer.playMedia("《泰坦尼克号》");
System.out.println();
MediaPlayer musicPlayerOnTV = tvFactory.createMusicPlayer();
musicPlayerOnTV.playMedia("《阿刁》");
System.out.println();
MediaPlayerFactory speakerFactory = new SpeakerPlayerFactory("漫步者ASZ");
MediaPlayer speakerMusicPlayer = speakerFactory.createMusicPlayer();
speakerMusicPlayer.playMedia("《My Heart Will Go On》");
MediaPlayer imagePlayerOnSpeakers = speakerFactory.createImagePlayer();
if (imagePlayerOnSpeakers == null) {
System.out.println("音响无法播放图片信息.");
}
}
}
组合优势
- 工厂方法模式担任了媒体播放器对象创建的任务,将客户端与具体播放器类的实例化过程解耦
- 当需要根据不同条件来创建不同种类的对象,并且这些对象本身又具有独立变化的抽象和实现维度时,这种组合非常有效。
桥接模式+组合模式
组合模式,结构型设计模式,可以使用它将对象组合成数状结构,并且能像使用单独对象一样使用它们。
场景
开发一个简单图形编辑器,用户可以绘制各种图形(例如: 圆形、矩形、文本框),这些图形可以单独存在,也可以组合成复杂的复合图形,同时,这些图 形需要支持不同的渲染方式(如:矢量渲染、位图渲染)。
模式职责
- 桥接模式: 将图形作为抽象部分,具体渲染方式作为实现部分,进行解耦。
- 组合模式: 将对象组合成树形结构以表示 部分-整体 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
具体设计
桥接模式
- 抽象部分:
Shape
定义了图形的抽象类,持有对Rederer
的引用。 - 扩展抽象部分: 具体的图形,包含
Circle
Rectangle
Text
等。 - 实现者接口:
Rederer
定义图形渲染方式的接口。 - 具体实现者: 具体图形渲染器,包含
VectorRenderer
RasterRenderer
等。
组合模式
- 组件:
Shape
抽象类(桥接模式的抽象部分),定义了所有图形(包括复合图形)的通用接口,如draw()
add()
remove()
getChild()
等管理子组件的方法。 - 叶子:
Circle
Rectangle
Text
等具体图形,没有子组件。 放器。 - 复合:
CompositeShape
包含子组件的集合,并实现了组件接口,将对子组件的操作委托给它们。
代码示例
1. Rederer
arduino
/**
* 渲染器接口
* <p>
* 桥接模式(实现者接口)
*/
public interface Renderer {
void renderCircle(double radius);
void renderRectangle(double width, double height);
void renderText(String text);
}
- 桥接模式中的 实现者接口,定义了图形渲染的通用行为,如
renderCircle()
renderRectangle()
renderText()
。
2. VectorRenderer
RasterRenderer
arduino
/**
* 矢量图形渲染器
* <p>
* 桥接模式(具体实现者)
*/
public class VectorRenderer implements Renderer {
@Override
public void renderCircle(double radius) {
System.out.println("[矢量图] 半径为: " + radius + "的圆。");
}
@Override
public void renderRectangle(double width, double height) {
System.out.println("[矢量图] 宽度: " + width + ", 高度: " + height + "的矩形。");
}
@Override
public void renderText(String text) {
System.out.println("[矢量图] 内容为: " + text + "的文本框。");
}
}
/**
* 位图渲染器
* <p>
* 桥接模式(具体实现者)
*/
public class RasterRenderer implements Renderer {
@Override
public void renderCircle(double radius) {
System.out.println("[位图] 半径为: " + radius + "的圆。");
}
@Override
public void renderRectangle(double width, double height) {
System.out.println("[位图] 宽度: " + width + ", 高度: " + height + "的矩形。");
}
@Override
public void renderText(String text) {
System.out.println("[位图] 内容为: " + text + "的文本框。");
}
}
- 桥接模式中的 具体实现者,分别实现了矢量渲染和位图渲染的逻辑。
3. Shape
csharp
/**
* 图形
* <p>
* 桥接模式(抽象部分)/组合模式(组件)
*/
public abstract class Shape {
protected Renderer renderer;
public Shape(Renderer renderer) {
this.renderer = renderer;
}
public void setRenderer(Renderer renderer) {
this.renderer = renderer;
}
public abstract void draw();
public void add(Shape shape) {
throw new UnsupportedOperationException("方法未实现!");
}
public void remove(Shape shape) {
throw new UnsupportedOperationException("方法未实现!");
}
public void getChild(int i) {
throw new UnsupportedOperationException("方法未实现!");
}
}
- 桥接模式中的 抽象部分,维护了一个
Renderer
类型的引用,提供setRenderer()
方法,允许动态切换渲染器。 - 组合模式中的 组件,定义了所有图形(包括叶子和复合)的通用接口
draw()
,同时定义了add()
remove()
getChild()
等用于管理子 组件的方法,这些方法在叶子节点中会抛出UnsupportedOperationException
。
4. Circle
Rectangle
Text
arduino
/**
* 圆形
* <p>
* 桥接模式(扩展抽象部分)/组合模式(叶子)
*/
public class Circle extends Shape {
private double radius;
public Circle(Renderer renderer, double radius) {
super(renderer);
this.radius = radius;
}
@Override
public void draw() {
renderer.renderCircle(radius);
}
}
/**
* 矩形
* <p>
* 桥接模式(扩展抽象部分)/组合模式(叶子)
*/
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(Renderer renderer, double width, double height) {
super(renderer);
this.width = width;
this.height = height;
}
@Override
public void draw() {
renderer.renderRectangle(width, height);
}
}
/**
* 文本框
* <p>
* 桥接模式(扩展抽象部分)/组合模式(叶子)
*/
public class Text extends Shape {
private String content;
public Text(Renderer renderer, String content) {
super(renderer);
this.content = content;
}
@Override
public void draw() {
renderer.renderText(content);
}
}
- 桥接模式中的 扩展抽象部分,代表具体的图形类型。
- 组合模式中的 叶子,它们没有子组件,实现了
draw()
方法,并委托给renderer
进行实际的绘制,不实现add()
remove()
getChild()
方法,或抛出异常。
5. CompositeShape
typescript
/**
* 复合图形类
* <p>
* 组合模式(组件)
*/
public class CompositeShape extends Shape {
private List<Shape> children = new ArrayList<>();
private String compositeName;
public CompositeShape(Renderer renderer, String compositeName) {
super(renderer);
this.compositeName = compositeName;
}
@Override
public void draw() {
System.out.println("---开始绘制名为 [" + compositeName + "] 的复合图---");
for (Shape shape : children) {
shape.setRenderer(shape.renderer);
shape.draw();
}
System.out.println("---名为 [" + compositeName + "] 的复合图绘制结束---");
}
@Override
public void setRenderer(Renderer renderer) {
super.setRenderer(renderer);
for (Shape child : children) {
child.setRenderer(this.renderer);
}
}
@Override
public void add(Shape shape) {
children.add(shape);
}
@Override
public void remove(Shape shape) {
children.remove(shape);
}
@Override
public void getChild(int i) {
children.get(i);
}
}
- 组合模式中的 复合,它维护了一个
List<Shape>
来存储子组件 - 实现了
Shape
接口的draw()
方法,该方法会遍历集合中的子组件并调用它们的draw()
方法,从而实现对整个复合结构的绘制。 draw()
方法中,通过调用shape.setRenderer(this.renderer)
确保子图形与复合图形使用相同的渲染器,在setRenderer()
方法中 也选择性的将渲染器传播给子组件,使得复合图形的渲染器可以影响到其所有子类。
测试类
ini
public class RendererTest {
@Test
public void test_design_composite() {
Renderer vectorRenderer = new VectorRenderer();
Renderer rasterRenderer = new RasterRenderer();
CompositeShape group1 = new CompositeShape(vectorRenderer,"group1");
group1.add(new Circle(vectorRenderer, 5.0));
group1.add(new Rectangle(vectorRenderer, 8.0, 6.0));
CompositeShape group2 = new CompositeShape(rasterRenderer,"group2");
group2.add(new Text(rasterRenderer, "超大的文本框"));
group2.add(new Circle(rasterRenderer, 3.0));
CompositeShape drawing = new CompositeShape(vectorRenderer,"drawing");
drawing.add(group1);
drawing.add(group2);
drawing.add(new Rectangle(vectorRenderer, 100.0, 50.0));
drawing.draw();
System.out.println("\n改变整个绘图的渲染器\n");
drawing.setRenderer(rasterRenderer);
drawing.draw();
System.out.println("\n改变group1子组件的渲染器\n");
group1.setRenderer(vectorRenderer);
drawing.draw();
}
}
- 构建了包含有两个子组件的复合结构,且两个复合结构使用了不同的渲染器。
- 当改变主复合图形的渲染器时,将渲染器自动传播到所有子图形中,演示对整个图形结构渲染方式的统一设置。
- 改变其中子组件渲染器,演示独立的选择其中渲染方式。
桥接模式作为一种结构型设计模式,其核心价值在于将抽象与实现解耦,使得两者能够独立演化和扩展。通过 组合由于继承 的原则,有效的解决了传统继承模式在多维度变化场景下导致的类爆炸问题。 本文主要练习了 桥接模式与其他设计模式的组合使用(工厂方法模式、抽象工厂模式、策略模式、装饰器模式、模板方法模式和组合模式)。