1.概要
这里结合多种模式完成需求,为了体现各模式的使用价值,在对比中对某种模式的特点有一个相对形象的认识。还是一坦克大战作为需求原型,因为需求简单,易于理解,不会在需求本身上消耗太多精力,更容易专注模式本身。
2.内容
需求:坦克大战
创建两种坦克
| 坦克类型 | 射程 | 速度 |
| b70 | 70米 | 时/70公里 |
| b50 | 50米 | 时/50公里 |
类图
需求设计思路
坦克,不同的型号采用策略模式,这里用不同的规格承载策略,其实就是70和50两个参数
在调用的过程中,采用的是装饰模式,对象的套用关系是 坦克(射击(跑(客户端)));调用流程就是 坦克->create(射击->create(跑->update(客户端->ceateTankFinish)));这就是一个装饰模式的调用过程。其实这个结构也可以说是职责链,只不过这个函数执行的功能都在基础功能上有叠加功能,并不是将职责下抛。所以叫装饰模式更像一些,但其实完全满足职责链的结构关系,这里也能看出职责链和装饰模式的对象结果关系机会相同,都是有一个桥接链构成。
基础功能的承载就是“射击”和“跑”两个功能,结合不同的参数体现射程和跑的速度。这里“射击”和“跑”两个对象用的享元模式。
功能对象是用命令模式,功能是以命令对象的方式加载给坦克的,这就是整个坦克的函数执行链的基础。用命令模式把函数储存成对象链,这也是命令模式在这里的运用。
坦克的两种型号的抽象用策略模式,抽象的差别用两个规格承载,70,50;功能是固定的一个“射击”,一个“跑”,这两个功能是静态的,只是注入的规格不同,体现不同的功能,所以用享元模式。功能的组装过程是 坦克装“射击 ”,“射击”装“跑”,跑装“客户端”;这里的这个组装过程用的装饰模式;这里的“射击”和“跑”用的命令模式,所以组装的对象就等同于组装函数;坦克->射击->跑->客户端,这一系列的过程是否像一个职责链,这里你也可以体会一下装饰模式和职责链的共性和个性差别。坦克一创建就通知,射击,射击一运行就通知跑,跑一运行就通知客户端,坦克->射击->跑->客户端,这一通知的链条是否像是观察者模式,被观察的对象有动作,我就会收到通知,虽然标准的观察者模式,都是一对多的,但是多少并不是核心的,重要的是对象的联动关系,当然 联动的关键是状态,这里的状态就是我动你就动,隐形的状态传递多久是动与不动,只不过没有提出一个专门的状态。
代码
import java.util.HashMap; //--接口层--------------------------------------------------- //基本功能基类 class Function{ public String mStr; public String mUnit; Function(String str,String unit){ mStr = str; mUnit = unit; } public void exe(int specification) { System.out.println(mStr+specification+mUnit); } }; // Strategy Pattern // 功能执行接口 interface IFun{ void exe(); } //命令模式-用于创建坦克 interface IHandler{ void create(Tank t); } //抽象坦克接口定义 interface IStrategy{ //algorithm void create(); void update(Tank t); } //功能规格控制接口 interface ISpecificationOfTank{ int getSpecification(); } // 功能接口抽象-和功能规格控制接口 绑定 abstract class ConcreteFun implements IFun{ public ConcreteFun(ISpecificationOfTank s) { mSpecificationOfTank = s; } protected ISpecificationOfTank mSpecificationOfTank; } //Concrete //--实现层----------------------------------------------------------- //规格获取类实现 class ConcreteSpecification implements ISpecificationOfTank{ int mSpecification; public ConcreteSpecification(int value) { mSpecification = value; } public int getSpecification() { return mSpecification; } } //基本功能实现-设计 class ShotFlyweight extends Function{ public ShotFlyweight() { super("发射距离","米"); } } //基本功能实现-跑 class RunFlyweight extends Function{ public RunFlyweight() { super("速度","公里"); } } //享元模式-管理功能类,使功能对象享元 class FlyweightFactory{ static FlyweightFactory mFlyweightFactory = new FlyweightFactory(); static FlyweightFactory get() { return mFlyweightFactory; } HashMap<String,Function> mMaps = new HashMap<String,Function>(); public Function GetFlyweitht(String key) { Function f = mMaps.get(key); if(f == null) { return createFlyweight(key); }else { return f; } } public Function createFlyweight(String key) { Function f = null; if(key == "shot") { f = new ShotFlyweight(); }else { f = new RunFlyweight(); } mMaps.put(key, f); return f; } } //功能执行类实现-射击 class Shot extends ConcreteFun{ public Shot(ISpecificationOfTank s) { super(s); } public void exe() { //享元模式 //为了保证无论创建多少个坦克,射击和跑这两个功能共享 //这里采取了享元的设计模式 Function f = FlyweightFactory.get().GetFlyweitht("shot"); f.exe(mSpecificationOfTank.getSpecification()); } } //功能执行类实现-跑 class Run extends ConcreteFun{ public Run(ISpecificationOfTank s) { super(s); } public void exe() { //享元模式 Function f = FlyweightFactory.get().GetFlyweitht("run"); f.exe(mSpecificationOfTank.getSpecification()); } } //坦克定义 class Tank{ Shot mShot; Run mRun; public void exe() { mShot.exe(); mRun.exe(); } } //功能抽象类-命令模式 abstract class Handler implements IHandler{ protected ISpecificationOfTank mSpecificationOfTank; public Handler(ISpecificationOfTank s) { mSpecificationOfTank = s; } } //跑功能-命令模式 class HandlerRun extends Handler{ IStrategy mStrategy; public HandlerRun(IStrategy stragegy,ISpecificationOfTank s) { super(s); mStrategy = stragegy; } public void create(Tank t) { t.mRun = new Run(mSpecificationOfTank); //跑掉坦克的update //这里也是装饰模式的体现: //嵌套结构: 设计对象(跑对象->create(坦克对象->update(客户端->ceateTankFinish))) mStrategy.update(t); } } //射击-命令模式 class HandlerSort extends Handler{ HandlerRun mNextHandler; public HandlerSort(HandlerRun h,ISpecificationOfTank s){ super(s); mNextHandler = h; } public void create(Tank t) { t.mShot = new Shot(mSpecificationOfTank); //这里就是套娃内部对想的调用 设计调运行 //装饰模式的体现 mNextHandler.create(t); } } //抽象坦克-策略模式 class Strategy implements IStrategy{ HandlerSort mStartChain; ISpecificationOfTank mSpecificationOfTank; Client mClient; public Strategy(Client c) { mClient = c; } protected void myinit() { //这里用了模版模式,套娃的方式,HandlerRun套this,HandlerSort套HandlerRun(endChain) //所以当运行HandlerSort时也会运行跑,设计需要跑的配合 HandlerRun endChain = new HandlerRun(this,mSpecificationOfTank); mStartChain = new HandlerSort(endChain,mSpecificationOfTank); } //当创建坦克后会调用这个函数, //这里会调用mStartChain的create //mStartChain的create调用时,会调用他桥接对象的create //这个调用也是桥接和装饰模式结合的结构 public void create() { Tank t = new Tank(); mStartChain.create(t); } //这里是观察着模式,坦克给客户端法通知 public void update(Tank t) { mClient.ceateTankFinish(t); } } //坦克70 class B70Strategy extends Strategy{ public B70Strategy(Client c) { super(c); //这里采用70 和 50 规格的不同策略 使用的是策略模式 mSpecificationOfTank = new ConcreteSpecification(70); myinit(); } } //坦克50 class B50Strategy extends Strategy{ public B50Strategy(Client c) { super(c); //这里采用70 和 50 规格的不同策略 使用的是策略模式 mSpecificationOfTank = new ConcreteSpecification(50); myinit(); } } //--用户调用层------------------------------------------------- public class Client { public static void main(String[] args) { System.out.println("hello world !"); Client c = new Client(); IStrategy strategy = new B70Strategy(c); strategy.create(); } public void ceateTankFinish(Tank t) { System.out.println("ceateTankFinish"); t.exe(); } }运行结果
3.关联链接
4.关联知识
1.桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,旨在将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式通过组合的方式,将两个独立变化的维度解耦,从而提高了系统的灵活性和可扩展性。
一、桥接模式的定义与原理
桥接模式的定义是:将抽象部分与它的实现部分分离,使它们都可以独立地变化。该模式涉及一个接口,它充当一个“桥”,使得具体类可以在不影响客户端代码的情况下改变。
桥接模式的核心思想是将系统的抽象层次和实现层次分离,抽象层次定义了抽象接口,包含了一些抽象方法;实现层次则实现了这些抽象方法,并提供了具体的方法实现。通过组合的方式,将抽象层次和实现层次的对象关联起来,从而在运行时动态地组合不同的实现。
二、桥接模式的结构
桥接模式包含以下角色:
- 抽象化(Abstraction)角色:定义抽象类的接口,并保存一个对实现化对象的引用。
- 修正抽象化(Refined Abstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
- 实现化(Implementor)角色:定义实现化角色的接口,但不给出具体的实现。这个接口不一定和抽象化角色的接口定义相同。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
三、桥接模式的优点
- 提高系统的灵活性:桥接模式允许在抽象层次和实现层次之间动态地组合不同的对象,从而提高了系统的灵活性。
- 降低类之间的耦合度:通过将抽象部分和实现部分分离,桥接模式降低了类之间的耦合度,使得系统更易于维护和扩展。
- 支持多个独立变化的维度:桥接模式能够处理多个独立变化的维度,使得系统可以轻松地扩展新的抽象或实现。
- 减少继承的使用:桥接模式使用组合关系替代了传统的继承关系,从而避免了类层次结构的复杂性。
四、桥接模式的应用场景
桥接模式通常适用于以下场景:
- 需要在抽象和具体实现之间增加灵活性:例如,一个系统需要支持多种操作系统和多种文件格式,可以使用桥接模式将操作系统和文件格式作为两个独立的维度进行处理。
- 一个类存在两个或多个独立变化的维度:例如,在图形处理系统中,图形可以按照形状和颜色进行分类,形状和颜色就是两个独立变化的维度。
- 不希望使用继承或因为多层继承导致系统类的个数剧增:桥接模式通过组合关系替代继承关系,可以有效控制系统中类的个数。
五、桥接模式的示例
以视频播放器的设计为例,可以使用桥接模式来处理视频格式和操作系统两个独立变化的维度。
定义实现化角色接口:
interface VideoPlayerImplementor { void playVideo(); }创建具体实现类:
class FLVVideoPlayer implements VideoPlayerImplementor { public void playVideo() { System.out.println("播放FLV格式的视频。"); } } class MP4VideoPlayer implements VideoPlayerImplementor { public void playVideo() { System.out.println("播放MP4格式的视频。"); } }定义抽象化角色:
abstract class VideoPlayer { protected VideoPlayerImplementor implementor; public VideoPlayer(VideoPlayerImplementor implementor) { this.implementor = implementor; } public abstract void play(); }创建修正抽象化角色:
class WindowsVideoPlayer extends VideoPlayer { public WindowsVideoPlayer(VideoPlayerImplementor implementor) { super(implementor); } public void play() { System.out.println("在Windows系统上播放视频:"); implementor.playVideo(); } } class LinuxVideoPlayer extends VideoPlayer { public LinuxVideoPlayer(VideoPlayerImplementor implementor) { super(implementor); } public void play() { System.out.println("在Linux系统上播放视频:"); implementor.playVideo(); } }客户端代码:
public class Client { public static void main(String[] args) { VideoPlayerImplementor flvPlayer = new FLVVideoPlayer(); VideoPlayerImplementor mp4Player = new MP4VideoPlayer(); VideoPlayer windowsFLVPlayer = new WindowsVideoPlayer(flvPlayer); VideoPlayer linuxMP4Player = new LinuxVideoPlayer(mp4Player); windowsFLVPlayer.play(); // 在Windows系统上播放FLV视频 linuxMP4Player.play(); // 在Linux系统上播放MP4视频 } }
六、总结
桥接模式是一种非常有用的设计模式,它通过将抽象部分与实现部分分离,使得系统可以独立地扩展这两个部分,从而提高了系统的灵活性和可扩展性。在实际应用中,桥接模式可以处理多个独立变化的维度,减少继承的使用,降低类之间的耦合度,使得系统更易于维护和扩展。
2.策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式的核心思想是将算法的定义与使用分离,客户端代码不直接调用具体的算法,而是通过一个统一的接口(策略接口)来访问不同的算法。
一、策略模式的基本结构
策略模式通常包含以下几个角色:
- 策略接口(Strategy):定义了一个公共接口,所有具体的策略类都需要实现这个接口。
- 具体策略类(Concrete Strategy):实现了策略接口,提供了具体的算法实现。
- 上下文类(Context):持有一个策略接口的引用,可以在运行时设置不同的策略,并通过调用策略接口的方法来执行相应的算法。
二、策略模式的优点
- 灵活性和可扩展性:策略模式使得算法可以独立于使用它的客户端而变化,客户端可以根据需要动态地选择不同的算法。
- 避免多重条件判断:使用策略模式可以避免在客户端代码中出现大量的
if-else或switch-case语句,使代码更加清晰和简洁。 - 符合开闭原则:策略模式可以在不修改现有代码的情况下添加新的算法,符合开闭原则(对扩展开放,对修改关闭)。
三、策略模式的应用场景
策略模式适用于以下场景:
- 算法需要动态选择:当系统中存在多种算法,并且这些算法需要根据不同的条件或需求动态选择时,可以使用策略模式。
- 避免使用多重条件判断:如果客户端代码中存在大量的
if-else或switch-case语句,用于根据不同的条件执行不同的算法,那么可以考虑使用策略模式来重构代码。 - 需要封装算法:当算法的实现比较复杂,或者算法的实现可能会发生变化时,可以使用策略模式将算法封装起来,以便在不影响客户端代码的情况下进行修改和扩展。
四、策略模式的示例
以下是一个简单的策略模式示例,用于模拟不同支付方式的实现:
// 策略接口 interface PaymentStrategy { void pay(double amount); } // 具体策略类:信用卡支付 class CreditCardStrategy implements PaymentStrategy { public void pay(double amount) { System.out.println("Paid " + amount + " using Credit Card."); } } // 具体策略类:支付宝支付 class AlipayStrategy implements PaymentStrategy { public void pay(double amount) { System.out.println("Paid " + amount + " using Alipay."); } } // 具体策略类:微信支付 class WeChatPayStrategy implements PaymentStrategy { public void pay(double amount) { System.out.println("Paid " + amount + " using WeChat Pay."); } } // 上下文类 class ShoppingCart { private PaymentStrategy paymentStrategy; public void setPaymentStrategy(PaymentStrategy paymentStrategy) { this.paymentStrategy = paymentStrategy; } public void pay(double amount) { if (paymentStrategy != null) { paymentStrategy.pay(amount); } else { System.out.println("No payment strategy set."); } } } // 客户端代码 public class StrategyPatternExample { public static void main(String[] args) { ShoppingCart cart = new ShoppingCart(); // 设置支付方式为信用卡支付 cart.setPaymentStrategy(new CreditCardStrategy()); cart.pay(100.0); // 设置支付方式为支付宝支付 cart.setPaymentStrategy(new AlipayStrategy()); cart.pay(200.0); // 设置支付方式为微信支付 cart.setPaymentStrategy(new WeChatPayStrategy()); cart.pay(300.0); } }在这个示例中,PaymentStrategy接口定义了支付的公共接口pay(),CreditCardStrategy、AlipayStrategy和WeChatPayStrategy是具体策略类,实现了不同的支付方式。ShoppingCart是上下文类,维护了一个PaymentStrategy类型的引用,可以在运行时设置不同的支付策略。客户端代码通过调用ShoppingCart的pay()方法,并使用不同的支付策略来进行支付操作。
五、总结
策略模式是一种强大的设计模式,它通过将算法的定义与使用分离,提高了系统的灵活性和可扩展性。在实际应用中,策略模式可以应用于多种场景,如算法的动态选择、避免多重条件判断以及算法的封装等。通过合理地使用策略模式,可以使代码更加清晰、简洁和易于维护。
3.职责链模式
职责链模式(Chain of Responsibility Pattern),又称责任链模式,是一种行为型设计模式。它通过将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求为止,从而避免了请求的发送者和接收者之间的耦合关系。以下是关于职责链模式的详细解释:
一、模式定义
职责链模式定义了一个请求处理的框架,请求沿着处理者链进行传递,每个处理者都有机会处理该请求,如果某个处理者不能处理该请求,则将该请求传递给链中的下一个处理者。
二、模式结构
职责链模式通常包含以下角色:
- 抽象处理者(Handler):
- 定义了一个处理请求的接口,通常包含一个指向下一个处理者的引用。
- 具体处理者(Concrete Handler):
- 实现抽象处理者的接口,处理它负责的请求,或者将请求传递给链中的下一个处理者。
- 客户端(Client):
- 创建处理者链,并向链的第一个处理者发送请求。
三、模式优缺点
优点:
- 降低耦合度:职责链模式将请求的发送者和接收者解耦,发送者不需要知道具体的接收者是谁,降低了系统各模块之间的耦合度。
- 提高灵活性:可以动态地增加或删除处理者,修改处理链的结构,增强系统的灵活性。
- 符合开闭原则:可以通过扩展新的处理者类来增加新的请求处理逻辑,而不需要修改现有的代码,符合开闭原则。
缺点:
- 性能问题:请求在链中传递时,可能需要经过多个处理者,导致性能下降。特别是当职责链过长或请求在链中被频繁传递时,性能问题会更加明显。
- 调试困难:由于请求在链中的传递过程可能涉及多个处理者,调试时可能会比较复杂。
- 请求可能未被处理:如果不能保证每个请求都能被处理,可能会导致某些请求被忽略。
四、应用场景
职责链模式适用于以下场景:
- 多个对象可以处理同一个请求:当系统中有多个对象可以处理同一个请求时,可以使用职责链模式来组织这些对象,使得请求能够沿着链进行传递,直到被处理为止。
- 处理顺序不确定:当处理请求的对象顺序不确定时,可以使用职责链模式来动态地组织处理链。
- 请求处理流程需要灵活配置:当请求的处理流程需要根据不同的业务场景进行灵活配置时,职责链模式提供了一种灵活的解决方案。
五、示例
以下是一个简单的职责链模式示例,假设有一个日志系统,需要根据不同的日志级别(如INFO、DEBUG、ERROR)将日志消息传递给不同的处理器:
// 抽象处理者 abstract class Handler { protected Handler nextHandler; public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public abstract void handle(String logMessage); } // 具体处理者:INFO日志处理器 class InfoHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("INFO")) { System.out.println("INFO Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 具体处理者:DEBUG日志处理器 class DebugHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("DEBUG")) { System.out.println("DEBUG Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 具体处理者:ERROR日志处理器 class ErrorHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("ERROR")) { System.out.println("ERROR Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 客户端 public class Client { public static void main(String[] args) { Handler infoHandler = new InfoHandler(); Handler debugHandler = new DebugHandler(); Handler errorHandler = new ErrorHandler(); infoHandler.setNextHandler(debugHandler); debugHandler.setNextHandler(errorHandler); infoHandler.handle("INFO: This is an informational message."); infoHandler.handle("DEBUG: This is a debug message."); infoHandler.handle("ERROR: This is an error message."); } }在这个示例中,不同的日志级别由不同的处理者处理。客户端将请求传递给链的第一个处理者(InfoHandler),如果InfoHandler不能处理该请求(即日志级别不是INFO),则将该请求传递给下一个处理者(DebugHandler),依此类推,直到找到能够处理该请求的处理者为止。
六、总结
职责链模式通过引入处理者链的概念,实现了请求的发送者和接收者之间的解耦,提高了系统的灵活性和可扩展性。然而,它也存在一些缺点,如性能问题和调试困难等。在实际应用中,需要根据具体的业务场景和需求来选择是否使用职责链模式。
4.命令模式
命令模式(Command Pattern)是一种行为设计模式,它将一个请求封装为一个对象,从而使得你可以用不同的请求对客户端进行参数化、对请求排队或记录请求日志,以及支持可撤销的操作。命令模式的核心在于将请求的调用者和执行者解耦,通过引入命令对象作为中间层来实现这一点。
主要角色
- 命令接口(Command):
- 声明执行操作的接口。
- 具体命令(Concrete Command):
- 将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现
Execute方法。
- 将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现
- 接收者(Receiver):
- 执行请求的具体对象。
- 调用者(Invoker):
- 要求该命令执行这个请求。
- 客户端(Client):
- 创建一个具体命令对象并设置它的接收者。
结构
+-------------+ +-------------+ +-------------+ | Command |<------| ConcreteCmd |-------| Receiver | +-------------+ +-------------+ +-------------+ | + Execute() | | - receiver | | + Action() | +-------------+ | + Execute() | +-------------+ +-------------+ ^ | +-------------+ | Invoker | +-------------+ | + Invoke() | +-------------+示例代码
以下是一个简单的命令模式示例,以电视机为例:
// 命令接口 interface Command { void execute(); } // 接收者 class Television { public void on() { System.out.println("The television is on."); } public void off() { System.out.println("The television is off."); } } // 具体命令 class TurnOnTV implements Command { private Television tv; public TurnOnTV(Television tv) { this.tv = tv; } @Override public void execute() { tv.on(); } } class TurnOffTV implements Command { private Television tv; public TurnOffTV(Television tv) { this.tv = tv; } @Override public void execute() { tv.off(); } } // 调用者 class RemoteControl { private Command command; public void setCommand(Command command) { this.command = command; } public void pressButton() { command.execute(); } } // 客户端 public class Client { public static void main(String[] args) { Television tv = new Television(); Command turnOnCommand = new TurnOnTV(tv); Command turnOffCommand = new TurnOffTV(tv); RemoteControl remote = new RemoteControl(); remote.setCommand(turnOnCommand); remote.pressButton(); // 输出: The television is on. remote.setCommand(turnOffCommand); remote.pressButton(); // 输出: The television is off. } }优点
- 降低系统的耦合度:命令模式使得请求者与执行者解耦,通过命令对象进行沟通。
- 扩展性良好:新的命令可以很容易地加入到系统中。
- 容易实现命令队列和宏命令:可以将多个命令对象放入队列中逐个执行,或组合成一个宏命令一起执行。
- 支持撤销操作:可通过维护历史命令记录来实现撤销功能。
缺点
- 增加系统复杂性:引入额外的命令类和调用者类,增加了系统的复杂性和理解难度。
- 可能导致命令类膨胀:如果系统中有大量的命令,会导致命令类数量急剧增加。
命令模式在需要实现复杂请求处理、支持撤销/重做功能、或需要记录请求日志的场景中非常有用。
5.职责链模式
职责链模式(Chain of Responsibility Pattern),又称责任链模式,是一种行为型设计模式。它通过将请求沿着处理者链进行传递,直到有一个处理者能够处理该请求为止,从而避免了请求的发送者和接收者之间的耦合关系。以下是关于职责链模式的详细解释:
一、模式定义
职责链模式定义了一个请求处理的框架,请求沿着处理者链进行传递,每个处理者都有机会处理该请求,如果某个处理者不能处理该请求,则将该请求传递给链中的下一个处理者。
二、模式结构
职责链模式通常包含以下角色:
- 抽象处理者(Handler):
- 定义了一个处理请求的接口,通常包含一个指向下一个处理者的引用。
- 具体处理者(Concrete Handler):
- 实现抽象处理者的接口,处理它负责的请求,或者将请求传递给链中的下一个处理者。
- 客户端(Client):
- 创建处理者链,并向链的第一个处理者发送请求。
三、模式优缺点
优点:
- 降低耦合度:职责链模式将请求的发送者和接收者解耦,发送者不需要知道具体的接收者是谁,降低了系统各模块之间的耦合度。
- 提高灵活性:可以动态地增加或删除处理者,修改处理链的结构,增强系统的灵活性。
- 符合开闭原则:可以通过扩展新的处理者类来增加新的请求处理逻辑,而不需要修改现有的代码,符合开闭原则。
缺点:
- 性能问题:请求在链中传递时,可能需要经过多个处理者,导致性能下降。特别是当职责链过长或请求在链中被频繁传递时,性能问题会更加明显。
- 调试困难:由于请求在链中的传递过程可能涉及多个处理者,调试时可能会比较复杂。
- 请求可能未被处理:如果不能保证每个请求都能被处理,可能会导致某些请求被忽略。
四、应用场景
职责链模式适用于以下场景:
- 多个对象可以处理同一个请求:当系统中有多个对象可以处理同一个请求时,可以使用职责链模式来组织这些对象,使得请求能够沿着链进行传递,直到被处理为止。
- 处理顺序不确定:当处理请求的对象顺序不确定时,可以使用职责链模式来动态地组织处理链。
- 请求处理流程需要灵活配置:当请求的处理流程需要根据不同的业务场景进行灵活配置时,职责链模式提供了一种灵活的解决方案。
五、示例
以下是一个简单的职责链模式示例,假设有一个日志系统,需要根据不同的日志级别(如INFO、DEBUG、ERROR)将日志消息传递给不同的处理器:
// 抽象处理者 abstract class Handler { protected Handler nextHandler; public void setNextHandler(Handler nextHandler) { this.nextHandler = nextHandler; } public abstract void handle(String logMessage); } // 具体处理者:INFO日志处理器 class InfoHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("INFO")) { System.out.println("INFO Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 具体处理者:DEBUG日志处理器 class DebugHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("DEBUG")) { System.out.println("DEBUG Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 具体处理者:ERROR日志处理器 class ErrorHandler extends Handler { @Override public void handle(String logMessage) { if (logMessage.startsWith("ERROR")) { System.out.println("ERROR Handler: " + logMessage); } else { if (nextHandler != null) { nextHandler.handle(logMessage); } } } } // 客户端 public class Client { public static void main(String[] args) { Handler infoHandler = new InfoHandler(); Handler debugHandler = new DebugHandler(); Handler errorHandler = new ErrorHandler(); infoHandler.setNextHandler(debugHandler); debugHandler.setNextHandler(errorHandler); infoHandler.handle("INFO: This is an informational message."); infoHandler.handle("DEBUG: This is a debug message."); infoHandler.handle("ERROR: This is an error message."); } }在这个示例中,不同的日志级别由不同的处理者处理。客户端将请求传递给链的第一个处理者(InfoHandler),如果InfoHandler不能处理该请求(即日志级别不是INFO),则将该请求传递给下一个处理者(DebugHandler),依此类推,直到找到能够处理该请求的处理者为止。
六、总结
职责链模式通过引入处理者链的概念,实现了请求的发送者和接收者之间的解耦,提高了系统的灵活性和可扩展性。然而,它也存在一些缺点,如性能问题和调试困难等。在实际应用中,需要根据具体的业务场景和需求来选择是否使用职责链模式。
6.享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享对象来减少内存使用和提高性能。它主要用于处理大量相似对象的场景,通过共享对象的相同部分来减少内存占用。
核心思想
享元模式的核心思想是将对象的状态分为内部状态和外部状态:
- 内部状态:对象可以共享的部分,存储在享元对象内部,不会随着外部环境的改变而改变。
- 外部状态:对象独有的部分,由客户端管理,在需要时传递给享元对象。
结构组成
享元模式通常包含以下几个角色:
- 抽象享元类(Flyweight):通常是接口或抽象类,声明了具体享元类的公共方法。通过这些方法可以向外界提供享元对象的内部状态和设置外部状态。
- 具体享元类(Concrete Flyweight):实现了抽象享元类所声明的方法,其实例称为享元对象,为内部状态提供存储空间。
- 非共享具体享元类(UnSharedConcreteFlyWeight):并非所有抽象享元类的子类都需要被共享,不需要被共享的外部状态可设计为非共享具体享元类,以参数的形式注入到具体享元的相关方法中。
- 享元工厂类(Flyweight Factory):用于创建和管理享元对象,维护一个享元池,存储已经创建的享元对象,并根据客户端请求共享已经存在的对象或创建新的享元对象。
适用场景
享元模式适用于以下场景:
- 大量相似对象:当系统中有大量相似对象,并且这些对象可以共享一部分状态时,使用享元模式可以节省内存和提高性能。
- 创建对象成本较高:当创建对象的成本较高,例如需要分配大量的内存或进行复杂的计算时,通过共享已经存在的对象可以减少这些开销。
- 系统性能要求高:在需要处理大量数据的系统中,使用享元模式可以减少对象的创建和销毁,从而提高系统的性能。
优点
- 减少内存占用:通过共享对象,减少了内存中对象的数量,降低了内存的占用。
- 提高性能:减少了对象的创建和销毁,提高了系统的性能。
- 简化代码:通过将对象的内部状态和外部状态分离,简化了对象的结构,使代码更加清晰和易于维护。
缺点
- 增加复杂性:享元模式要求将对象的内部状态和外部状态分离,这可能会增加系统的复杂性。
- 适用范围有限:享元模式适用于具有大量相似对象的场景,如果对象之间的差异较大,可能不适合使用享元模式。
- 外部状态管理:外部状态需要由客户端管理,在需要时传递给享元对象,这可能会增加客户端的复杂性。
实际应用
享元模式在软件开发中有广泛的应用,例如:
- 游戏开发:游戏中的许多对象如子弹、敌人、道具等可能具有相同的属性和行为,通过享元模式可以共享这些相同的部分,减少内存消耗。
- 图形渲染:在图形编辑软件中,每个图形对象(如圆形、矩形、直线等)可能有相同的样式或颜色,通过使用享元模式可以避免每个图形都存储一份相同的样式数据。
- 文本编辑:在富文本编辑器中,用户可以对文本应用不同的样式,如字体大小、颜色、粗体等。如果每个字符都创建一个新的样式对象,将会导致内存占用过高。使用享元模式可以优化这种情况。
总结
享元模式是一种有效的设计模式,它通过共享对象来减少内存使用和提高性能。然而,在使用享元模式时需要注意划分对象的内部状态和外部状态,并合理管理外部状态。同时,也需要根据具体的应用场景来评估是否适合使用享元模式。