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):用于创建和管理享元对象,维护一个享元池,存储已经创建的享元对象,并根据客户端请求共享已经存在的对象或创建新的享元对象。
适用场景
享元模式适用于以下场景:
- 大量相似对象:当系统中有大量相似对象,并且这些对象可以共享一部分状态时,使用享元模式可以节省内存和提高性能。
- 创建对象成本较高:当创建对象的成本较高,例如需要分配大量的内存或进行复杂的计算时,通过共享已经存在的对象可以减少这些开销。
- 系统性能要求高:在需要处理大量数据的系统中,使用享元模式可以减少对象的创建和销毁,从而提高系统的性能。
优点
- 减少内存占用:通过共享对象,减少了内存中对象的数量,降低了内存的占用。
- 提高性能:减少了对象的创建和销毁,提高了系统的性能。
- 简化代码:通过将对象的内部状态和外部状态分离,简化了对象的结构,使代码更加清晰和易于维护。
缺点
- 增加复杂性:享元模式要求将对象的内部状态和外部状态分离,这可能会增加系统的复杂性。
- 适用范围有限:享元模式适用于具有大量相似对象的场景,如果对象之间的差异较大,可能不适合使用享元模式。
- 外部状态管理:外部状态需要由客户端管理,在需要时传递给享元对象,这可能会增加客户端的复杂性。
实际应用
享元模式在软件开发中有广泛的应用,例如:
- 游戏开发:游戏中的许多对象如子弹、敌人、道具等可能具有相同的属性和行为,通过享元模式可以共享这些相同的部分,减少内存消耗。
- 图形渲染:在图形编辑软件中,每个图形对象(如圆形、矩形、直线等)可能有相同的样式或颜色,通过使用享元模式可以避免每个图形都存储一份相同的样式数据。
- 文本编辑:在富文本编辑器中,用户可以对文本应用不同的样式,如字体大小、颜色、粗体等。如果每个字符都创建一个新的样式对象,将会导致内存占用过高。使用享元模式可以优化这种情况。
总结
享元模式是一种有效的设计模式,它通过共享对象来减少内存使用和提高性能。然而,在使用享元模式时需要注意划分对象的内部状态和外部状态,并合理管理外部状态。同时,也需要根据具体的应用场景来评估是否适合使用享元模式。
7.模式渊源分析
一 模式分析表(桥接6模式)
桥接模式和桥接模式衍生的模式都是应用非常广泛的模式,我认为是学习的重点。
模式名称 | 桥接对象 | 桥接数 | 桥接属性 | 桥接图形 | 桥接迭代 | |
1 | 装饰 | 自己 | 单个 | 静态 | 链表 | 有 |
2 | 组合 | 自己 | 多个 | 静态 | 树(图) | 有 |
3 | 观察者 | 观察者 | 多个 | 静态 | 点 | 没 |
4 | 职责链 | 职责者 | 单个 | 静态 | 链表 | 有 |
5 | 策略 | 策略者 | 单个 | 动态 | 点 | 没 |
6 | 状态 | 状态者 | 单个 | 动态 | 点 | 没 |
7 | 解释器 | 解释器 | 多个 | 静态 | 图 | 有 |
概念解释
桥接对象:桥接的对象
桥接数:桥接关系发生桥接对象的数量
桥接属性:这个桥接使动态还是静态的,比如状态模式中的状态桥接是不断的变化的,而组合模式的桥接,在桥接后就不会发生变化,桥接的关系会固定下来所以是静态的。
桥接图形:桥接关系形成的最终图形,装饰会形成一个装饰链。组合会形成一种图的结构。观察者因为桥接的是一个链表型的观察者,会形成链表,但是这个链表不是有桥接关系形成,桥接关系只形成了一个点。
桥接迭代:指桥接关系是否发生迭代。
二 中间件4模式
通用意图:承载关联,隔离关联,减少关联
序号 | 名称 | 代理的连接 | 各自特性 |
1 | 适配器 | 类(桥接) | 是解决接口不匹配的问题,有可能是一系列的接口 |
2 | 外观 | 类(桥接) | 将内部公共函数代理,对外部模块公开。消除外部模块的直接访问,减少了外部模块和内部个模块的连接。如果在更大一层的空间考虑。这个外观,也可以当做中介者。 |
3 | 代理 | 类(桥接) | 这个外观的差别不大,不过这个模式强调的不是减少关联,而是一个保护层,举个例子说,这个层可能比外观更外一层,对一些非亲近的模块执行一些访问接口的限制。这层的主要价值在于权限的控制。 |
4 | 中介者 | 函数(依赖) | 该模块的主要价值体系在对内部依赖级别的关系消除。不紧密的几个类之间有调用关系,或者几个类简单调用关系较少,可以用一个中介者还承担这些相互的调用,从而消除一些耦合,使他们只和中介者发生耦合。他的特点与外观相比,更大的 针对性,在内部,且他是对几个平行关系者的代理。而外观是一个模块公布初期的接口,是对外提供。虽然从结构的本质上没发做绝对的区分。但是使用意图是不一样的。 结构关系和价值是一样的。 但这不重要,举个例子吧,如果AF,是A模块的外观,复杂和BF和CF沟通,那么在A BF,CF间,他就相当于中介者。 不饶了,我想您应该理解了吧 |
二 模式分析表(其他模式)
名称 | 模板 | 迭代 | 接口 | 性能 | 分类 | |
1 | 迭代器 | 有 | 多 | 专用5模式 | ||
2 | 模板 | 有 | 多 | 专用5模式 | ||
3 | 命令 | 有 | 单 | 专用5模式 | ||
4 | 备忘录 | 多 | 专用5模式 | |||
5 | 解释器 | 单 | 专用5模式 | |||
6 | 单件 | 单 | 创建型模式 | |||
7 | 抽象工厂 | 有 | 多 | 创建型模式 | ||
8 | 工厂方法 | 单 | 创建型模式 | |||
9 | 创建者 | 多 | 创建型模式 | |||
10 | 原型 | 单 | 有 | 创建型模式 | ||
11 | 享元 | 单 | 有 | 创建型模式 | ||
12 | 访问者 | 单 | 双向依赖模式 |
8.面向对象五关系深入分析
五种关系:
一般化关系、关联关系、聚合关系、合成关系、依赖关系。
一般化关系(Generalization)
表示类与类之间的继承关系,接口与接口之间的继承关系,或类对接口的实现关系。
关联关系(Association)
是类与类之间的联接,它使一个类知道另一个类的属性和方法。可以单向和双向。
聚合关系(Aggregation)
关联关系的一种,是强关联关系。表示整体和个体之间的关系。
合成关系(Composition)
关联关系的一种。比聚合关系更强。要求整体对象负责部分对象的生命周期。
依赖关系(Dependency)
类与类之间的联接,单向。表示一个类依赖于另一个类的定义。
关系图
为什么化这样是一个图呢,因为我觉得,这五种关系是升级的关系。
也及是说:如果是聚会关系了,一定已经包含链接关系了,如果已经是链接关系了,那么不可能没有依赖关系
定义关系 | 调用关系 | 后于自己创建 | 生命周期相同 | 先于自己创建 | |
依赖关系 | A(B b) | ||||
链接关系 | A(B b) | A{ b.fun()} | |||
聚会关系 | A(B b) | A{ b.fun()} | A{ B mb=null } | ||
合成关系 | A(B b) | A{ b.fun()} | A{ B mb=null } | A{ B mb=new B() } | |
继承关系 | A(B b) b=super | A{ b.fun()} b = super | A{ B mb=null } b = super | A{ B mb=new B() } b = super | super(B).fun() b = super |
上面这表是从升级的角度对5种关系做了一下分析。
我不多说的,应该很容易理解。
但是在分析这个的过程中我 有发现了一个问题
A对B依赖的升级和生命周期的关系
定义关系:A先于B
调用关系:A先于B
聚合关系:A先于B
组合关系:A==B
继承关系:B先于A
有A对于B升级的依赖我们发现,关系越高越靠得住。
这也说明了,为什么设计时候几乎不用刻意的消除“聚会关系”关系以上的关系呢。
其实就从人与人之间的关系考虑,也是的,关系密切到一定的程度,关系反而简单了。
就这里聚合以上的关系我这里把他暂时定义为亲人的关系。
在亲人这个关系内处理问题是相对简单的,多密切都不会增加复杂度。
而链接和依赖这不同,因为可靠性降低了,所以导致了很多不确定性。
就社会而言,整个社会的复杂度增加也是在这个层面上。
那么这里我想,是不是觉得事情是否复杂的不是耦合度,而是可靠性?
这里暂时一想:我还没有深入的考虑这个问题。
9.设计模式对比
以下是36种设计模式的对比表格,按创建型、结构型、行为型分类,重点体现其共性和个性:
创建型模式(5种)
模式名称 | 意图 | 关键组件 | 适用场景 | 优点 | 缺点 | 类似模式 |
---|---|---|---|---|---|---|
单例模式 | 确保一个类只有一个实例 | 私有构造器、静态实例 | 全局访问点(如配置管理) | 控制资源消耗 | 扩展困难,可能隐藏依赖 | 工厂方法(控制实例) |
工厂方法 | 定义一个创建对象的接口 | 抽象工厂、具体子类 | 对象创建需子类化 | 符合开闭原则 | 增加类层次 | 抽象工厂(更通用) |
抽象工厂 | 创建一系列相关对象 | 抽象工厂、具体工厂 | 跨产品族的对象创建 | 隔离具体类 | 产品族扩展困难 | 工厂方法(单产品) |
建造者模式 | 分步构建复杂对象 | 指挥者、构建器组件 | 对象构造过程需多步骤 | 灵活构造过程 | 代码重复(无通用步骤时) | 工厂方法(简单对象) |
原型模式 | 通过复制生成新对象 | 原型接口、克隆方法 | 对象创建成本高的场景 | 避免重复初始化 | 深拷贝复杂 | 工厂方法(需继承时) |
共性:关注对象创建过程,解耦客户端与具体类。
个性:单例控制实例数量,工厂方法/抽象工厂通过继承,建造者通过组合,原型通过复制。
结构型模式(7种)
模式名称 | 意图 | 关键组件 | 适用场景 | 优点 | 缺点 | 类似模式 |
---|---|---|---|---|---|---|
适配器模式 | 兼容不兼容的接口 | 适配者、目标接口 | 接口转换(如旧系统升级) | 复用现有类 | 过度适配可能复杂 | 外观模式(简化接口) |
桥接模式 | 分离抽象与实现 | 抽象类、实现类接口 | 多维度变化(如跨平台UI) | 减少子类数量 | 增加设计复杂度 | 策略模式(行为抽象) |
组合模式 | 统一处理单个和组合对象 | 组件接口、叶子/组合类 | 树形结构(如文件系统) | 简化客户端代码 | 限制组件类型 | 装饰器(动态行为) |
装饰器模式 | 动态扩展对象功能 | 抽象组件、具体装饰类 | 灵活添加职责(如IO流) | 避免继承爆炸 | 多层装饰影响性能 | 代理模式(控制访问) |
外观模式 | 提供简化接口 | 外观类、子系统类 | 复杂子系统封装 | 降低耦合度 | 限制灵活性 | 适配器(接口转换) |
享元模式 | 共享细粒度对象 | 享元工厂、享元池 | 大量相似对象(如线程池) | 减少内存占用 | 外部状态管理复杂 | 原型模式(对象复制) |
代理模式 | 控制对象访问 | 代理类、真实对象 | 延迟加载、权限控制 | 增强安全性 | 增加响应延迟 | 装饰器(动态扩展) |
共性:关注类与对象的组合/关联关系。
个性:适配器转换接口,桥接分离层次,组合处理树形结构,装饰器动态扩展,代理控制访问。
行为型模式(11种)
模式名称 | 意图 | 关键组件 | 适用场景 | 优点 | 缺点 | 类似模式 |
---|---|---|---|---|---|---|
责任链模式 | 传递请求链式处理 | 处理者接口、具体处理类 | 多条件分支(如日志级别) | 解耦发送者与接收者 | 不保证执行顺序 | 命令模式(请求封装) |
命令模式 | 封装请求为对象 | 命令接口、接收者 | 队列请求、撤销操作 | 支持事务性行为 | 增加类数量 | 策略模式(算法封装) |
解释器模式 | 定义语法解析规则 | 抽象表达式、终结符 | 领域语言解析(如SQL) | 扩展语法灵活 | 性能较低 | 访问者模式(结构操作) |
迭代器模式 | 遍历集合元素 | 迭代器接口、具体迭代器 | 统一访问不同集合 | 隐藏底层结构 | 类间耦合度高 | 组合模式(递归遍历) |
中介者模式 | 集中管理对象交互 | 中介者类、同事类 | 多对象复杂交互(如聊天室) | 减少对象间依赖 | 中介者复杂 | 观察者模式(事件驱动) |
备忘录模式 | 捕获对象内部状态 | 备忘录、管理者 | 撤销/重做(如文本编辑器) | 保持对象封装 | 消耗内存 | 命令模式(历史记录) |
观察者模式 | 对象状态变化通知 | 主题、观察者 | 事件驱动系统(如GUI) | 松耦合 | 通知顺序不确定 | 发布-订阅模式 |
状态模式 | 根据状态改变行为 | 上下文、状态接口 | 对象行为随状态变化(如订单) | 符合开闭原则 | 类数量膨胀 | 策略模式(算法切换) |
策略模式 | 封装算法族 | 策略接口、具体策略 | 算法动态切换(如排序) | 灵活替换算法 | 客户端需了解策略 | 状态模式(状态驱动) |
模板方法模式 | 定义算法骨架 | 抽象类、具体子类 | 步骤固定但细节变化(如排序) | 复用公共代码 | 子类限制多 | 工厂方法(创建过程) |
访问者模式 | 分离数据结构操作 | 访问者接口、具体访问者 | 稳定结构+多变操作(如编译器) | 扩展操作容易 | 违反迪米特法则 | 解释器模式(语法解析) |
共性:关注对象间的通信与职责分配。
个性:责任链传递请求,命令封装操作,观察者发布-订阅,状态模式依赖状态,策略模式切换算法。
其他模式(13种)
模式名称 | 类型 | 核心思想 | 典型场景 | 与其他模式关系 |
---|---|---|---|---|
依赖注入 | 创建型 | 外部管理依赖 | 解耦组件(如Spring框架) | 与工厂模式互补 |
服务定位器 | 创建型 | 集中管理对象获取 | 替代直接依赖(如JDBC) | 类似抽象工厂 |
对象池 | 结构型 | 复用昂贵对象 | 数据库连接池 | 类似享元模式 |
规格模式 | 行为型 | 组合谓词逻辑 | 复杂查询条件(如电商筛选) | 类似组合模式 |
空对象模式 | 行为型 | 避免空指针检查 | 默认行为(如集合迭代) | 简化客户端代码 |
仓库模式 | 结构型 | 封装数据访问层 | ORM框架(如Hibernate) | 类似外观模式 |
懒加载模式 | 结构型 | 延迟初始化 | 资源密集型对象(如图片加载) | 类似代理模式 |
业务委托模式 | 结构型 | 分发职责到辅助类 | 复杂业务拆分(如订单处理) | 类似策略模式 |
黑板模式 | 行为型 | 共享知识中心 | 专家系统(如语音识别) | 类似观察者模式 |
裸体对象模式 | 结构型 | 最小化对象职责 | 领域模型设计 | 反对继承膨胀 |
模块模式 | 结构型 | 封装相关类 | 包/模块组织(如Java 9) | 类似外观模式 |
属性模式 | 结构型 | 统一管理对象属性 | 动态配置(如游戏角色属性) | 类似装饰器模式 |
执行者模式 | 行为型 | 异步任务执行 | 线程池管理 | 类似命令模式 |
共性与个性总结
- 共性:
- 目的统一:提高代码复用性、扩展性和可维护性。
- 设计原则:依赖倒置、开闭原则、单一职责等。
- 结构特征:通过接口、抽象类、组合等实现解耦。
- 个性:
- 关注点不同:创建型关注对象构造,结构型关注类/对象关系,行为型关注交互与职责。
- 适用场景差异:如工厂模式适合对象创建,观察者模式适合事件驱动。
- 扩展方式:继承(工厂方法) vs 组合(装饰器) vs 接口(适配器)。
通过此表格可快速对比模式的核心差异,实际应用中需结合具体场景权衡选择。