设计模式

尽量以简洁易懂的方式把常用的设计模式表达清楚,并指出各种设计模式的优缺点及应用场景。

6大设计原则

单一职责原则

What:There should never be more than one reason for a class to change(应该有且仅有一个原因引起类的变更)

Why

  • 类的复杂性降低,实现什么职责都有清晰明确的定义;
  • 可读性提高,复杂性降低,那当然可读性提高了;
  • 可维护性提高,可读性提高,那当然更容易维护了;
  • 变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

How

在设计类,接口,方法时,应该保证类,接口,方法的职责单一。

里氏替换原则(Liskov Substitution Principle,LSP)

What:

所有引用基类的地方必须能透明地使用其子类的对象。通俗来讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

Notice:在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。

Notice:如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。

Why

增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑。

How

  • 在父类出现的地方都可以用子类替换
  • 子类可以有自己的个性,但不要太强,最好没有!!!!
  • 子类中方法的入参必须与超类中被重载的方法的入参相同或者更宽松。
  • 重载或实现父类的方法时输出结果可以被缩小

尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准。

简单的说子类不能有个性!子类的功能实现不能超出父类定义的边界,子类最好不要做太多无谓的事情!

依赖倒置原则

What

实质上就是面向接口编程

  • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
  • 抽象不应该依赖细节;
  • 细节应该依赖抽象。

Why

减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。

How

  • 每个类尽量要有接口或抽象类,或者抽象类和接口两者都具备。
  • 变量的声明类型尽量是接口或者是抽象类
  • 尽量不要重写基类的方法,因为类间依赖的是抽象,重写了抽象方法,对依赖的稳定性会产生一定的影响。
  • 尽量面向接口编程

接口隔离原则

What

精简接口,不要向外暴露不需要的方法

Why:

接口是对外的承诺,承诺越少对系统的开发越有利,变更的风险也就越少,同时也有利于降低成本。

How

  • 根据接口隔离原则拆分接口时,首先必须满足单一职责原则。
  • 一个接口只服务于一个子模块或业务逻辑
  • 通过业务逻辑压缩接口中的public方法,接口时常去回顾,尽量让接口达到“满身筋骨肉”,而不是“肥嘟嘟”的一大堆方法
  • 已经被污染了的接口,尽量去修改,若变更的风险较大,则采用适配器模式进行转化处理。
  • 了解环境,拒绝盲从。

迪米特法则(Law of Demeter,LoD)

What

也称为最少知识原则(Least Knowledge Principle, LKP) 迪米特法则的英文解释是:Only talk to your immediate friends

Why

类间解耦,弱耦合

How

一个类应该只与朋友类交流,朋友类是指类的成员变量、方法的输入输出参数中的类,而出现在方法体内部的类不属于朋友类。一个类应该尽量缩小public属性和方法,这样修改涉及的面也就越小,变更引起的风险就越小。为了保持朋友类间的距离,在设计时需要反复衡量:是否还可以再减少public方法和属性,是否可以修改为private、package-private、protected等访问权限,是否可以加上final关键字。

开闭原则

What

Software entities like classes,modules and functions should be open for extension but closed for modifications.(一个软件实体如类、模块和函数应该对扩展开放,对修改关闭) 开闭原则是Java世界里最基础的设计原则

How

意思是使用扩展来实现变化,而不是通过修改已有的代码来实现变化。

设计模式

观察者模式

定义

观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe),它是一个在项目中经常使用的模式,其定义如下: Define a one-to-maney dependency between objects so that when one object changes state,all its dependens are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新)

通用类图

  • EventOrign事件源

或称为事件的触发者,或者是被观察者,当EventOrigin的某个行为执行时,将触发事件,由Subject发布事件

  • Subject事件监听管理者

或称为观察管理器,可以将它理解为观察模式中的中介,它负责关联观察者与被观察者,并维护两者之间的关系,它必须能够动态地增加、取消观察者,和发布事件。事件的发起者必须要实现这个接口,仅仅完成这样的的职责:管理观察者并通知观察者。Java中,已经封装了一个java.util.Observable的对象,被观察者直接继承即可。

  • Observer观察者

可以理解为事件处理器,当事件发生时,由Subject触发调用。Java中已经提供了一个java.util.Observer接口。

优点

  • 观察者和被观察者之间是抽象耦合
  • 建立一套触发机制
  • 根据单一职责原则,每个类的职责是单一的,而观察者模式可以完美地将各个职责单一的类或对象联系起来,形成触发链。

缺点

  • 由于观察者的是顺序执行的,当有多个观察者时,执行效率会很低。一般观察者的执行采用异步的方式。

适用场景

  • 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
  • 事件多级触发场景
  • 跨系统的消息交换场景,如消息队列的处理机制。

个人理解

观察者的update方法一般需要传递两个参数:一个是被观察者,一个是消息对象。 被观察者的业务行为方法最好添加重载方法,添加boolean入参决定是否通知观察者。 事件监听管理者Subject可以由事件源继承或聚合到事件源中。

注意事项

  • 广播链的问题

由于一个观察者模式中一个对象既可以是观察者也可以是被观察者,很容易造成死循环。根据经验建议,一个观察者模式中最多出现一个对象即是观察者也是被观察者。

常见用例

  • 文件系统中新建文件触发事件
  • 猫鼠游戏中猫叫老鼠就逃跑
  • 广播收音机,电台是被观察者,收音机是观察者

组合模式

定义

组合模式(Composite Pattern)也叫做合成模式,有时也叫做部分-整体模式(Part-Whole),主要是用来描述部分与整体的关系,其定义如下: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。)

通用类图

  • Componet抽象构件角色

Component就是“整体-部分”的抽象,可以为空接口

  • Leaf叶子构件

叶子对象,再下就没有其他分支,也就是遍历的最小单位。

  • Composite树枝构件

树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构

透明的组合模式中甚至不需要Leaf节点,Composite即为Leaf,getChildren为空即为叶子节点了。

优点

  • 高层模块调用简单,因为都抽象为Componet了。
  • 节点自由增加

缺点

当使用安全组合模式时,与依赖倒置原则冲突,因为高层模块需要直接引用到实现类

适用场景

  • 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。
  • 从一个整体中能够独立出部分模块或功能的场景

只要是树形结构,就要考虑使用组合模式,这个一定要记住,只要是体现局部和整体的关系的时候,而且这种关系还可能比较深,考虑一下组合模式。

个人理解

组合模式其实就是当一个模型结构符合部分-整体关系的时候,抽象出它们的共性,如Component,然后维护它们的关系即可。

个人更偏向于使用透明的组合模式

常见用例

  • 后台管理功能左侧的导航菜单
  • XML结构,如JDOM和DOM4J的实现

适配器模式

定义

适配器模式又叫做变压器模式,也叫做包装模式(Wrapper),包装模式也包括装饰模式。 将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

通用类图

  • Target目标角色

该角色定义把其他类转换为何种接口

  • Adaptee源角色

你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的,运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新、靓丽的角色。

  • Adapter适配器角色

适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:通过继承或类关联的方式把源角色转换为目标角色

优点

  • 适配器模式可以让两个没有任何关系的类在一起运行
  • 增加了类的透明性
  • 我们访问的依然是接口抽象。
  • 提高了类的复用度
  • 源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。
  • 灵活性非常好
  • 突然不想要适配器类了,删除掉即可。

适用场景

你有动机修改一个已经投产中的接口时,适配器可能是最适合你的模式。比如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接口。

个人理解

使用适配器模式的目的是为了令一个源对象满足目标对象的接口标准,而又能够保留源对象的功能不变。方法就是实现目标对象的接口,并通过继承或关联源对象的方法应用源对象的功能。

注意事项

适配器模式最好在详细设计阶段不要考虑它,它不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题。

备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

Without violating encapsulation,capture and externalize an object’s internal state so that the object can be restored to this state later.

通用类图

  • Originator发起人角色

所有实现Memoable接口都属于发起人角色,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。

  • SavePoint备忘录角色

也可以理解为保存点。负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

  • SavePointManager备忘录管理员角色

对备忘录进行管理、保存和提供备忘录。

使用场景

  • 需要保存和恢复数据的相关状态场景
  • 提供一个可回滚(rollback)的操作,如word中的ctrl+z组合键,IE的后退按钮。
  • 数据库连接的事务管理

个人理解

备忘录模式其实就是为一个对象或对象中的某些状态备份至一个保存点,由一个保存点管理对象对保存点进行增删改查的管理操作,那个需要备份的对象必须提供方法能够由保存点恢复数据。

策略模式

定义

策略模式(Strategy Pattern),也叫做政策模式(Policy Pattern)。定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)

Define a family of algorithms,encapsulate each one,and make them interchageable.

通用类图

  • Context封装角色

它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。

  • Strategy抽象策略角色

策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。

  • ConcreteStrategy具体策略角色

实现抽象策略中的操作,该类含有具体的算法

优点

  • 算法可以自由切换
  • 避免使用多重条件判断
  • 扩展性良好

缺点

  • 策略类数量增多,复用的可能性很少
  • 所有的策略类都需要对外暴露
  • 高层模块必须知道具体的策略实现类,封装类就变得没有意义了。可能采用工厂方法模式、代理模式享元模式修正这个缺陷。

使用场景

  • 多个类只有在算法或行为上稍有不同的场景。
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

代码示例

  • 策略枚举
public enum StrategyAlg {

	ADD{

		@Override
		public int exec(int a, int b) {
			return a+b;
		}
		
	},
	SUB{
		@Override
		public int exec(int a, int b) {
			return a-b;
		}
		
	};
	private StrategyAlg(){
	}
	public abstract int exec(int a,int b);
}

策略枚举是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举项都是public、final、static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。

装饰模式

定义

装饰模式(Decorator Pattern),动态地给一个对象添加一些额外的功能。就增加功能来说,装饰模式相比生成子类更为灵活。

Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provides a flexible alternative to subclassing for extending functionality.

通用类图

  • Component抽象构件

Component是一个接口或者是抽象类,就是定义我们核心的对象,也就是最原始的对象。 在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件。

  • ConcreteComponent具体构件

ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。

  • Decorator装饰角色

一般是一个抽象类,实现接口或者抽象方法,它里面可不一定有抽象的方法,在它的属性里必然有一个private变量指向Component抽象构件

  • 具体装饰角色

优点

  • 装饰类和被装饰类可以独立发展,而不会相互耦合。
  • Component类无须知道Decorator类,Decorator类从外部来扩展Component类,而Decorator也不用知道具体的构件。
  • 装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
  • 装饰模式可以动态地扩展一个实现类的功能

使用场景

  • 需要扩展一个类的功能,或给一个类增加附加功能。
  • 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  • 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。

个人理解

装饰模式是对继承的有力补充。继承是静态地给类增加功能,而装饰模式则是动态地增加功能。因为继承只能重写方法增强方法的功能,而装饰则可以通过新增装饰类,动态地在原有对象的功能基础上增强功能。

责任链模式

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。

Avoid coupling the sender of a request to ites receiver by giving more than one object a chance to handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.

通用类图

  • AbstractChainHandler责任链节点抽象类

AbstractChainHandler一般都应用了模板模式,主要实现三个职责:一是定义一个请求的处理方法handleRequest,唯一对外开放的方法;二是定义一个链的编排方法setNext,设置下一个处理者;三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的范围和具体的请求处理。前两个职责实现了请求传递的功能,第三个职责交由子类去完成

  • ChainHandlerA/ChainHandlerB责任链节点实现类

完成AbstractChainHandler的抽象方法,定义处理范围和处理请求。

  • ChainManager责任链管理器

负责维护责任链节点及责任链的执行

代码示例

  • 责任链节点抽象类
/**
 * @category 责任链节点抽象类,抽象每个责任链节点的处理逻辑
 * @author tim
 *
 */
public abstract class AbstractChainHandler {

	/**
	 * @category 下一个责任链节点
	 */
	private AbstractChainHandler nextHandler;
	
	/**
	 * @category 第一个责任链节点处理入口
	 * @param request
	 * @return
	 */
	public final Object handlerRequest(Object request){
		return handlerRequest(request,null);
	}
	
	/**
	 * @category 其他责任链节点处理入口
	 * @param request
	 * @param response
	 * @return
	 */
	private final Object handlerRequest(Object request,Object response){
		Object resp=null;
		boolean isMyBussiness=isMyBussiness(request);
		if(isMyBussiness){
			resp=doBussiness(request,response);
			//如果直接返回resp,就变成了标准责任链模式了
			//return resp;
		}
		
		if(this.nextHandler!=null){
			resp=this.nextHandler.handlerRequest(request,resp);
		}
		//当没有这段if语句时,就是单向责任链。现在是双向责任链了。
		if(isMyBussiness){
			resp=afterBussiness(request,resp);
		}
		
		return resp;
	}
	
	public void setNextHandler(AbstractChainHandler handler){
		this.nextHandler=handler;
	}
	
	
	/**
	 * @category 责任链业务处理入口(前),由子类实现
	 * @param request
	 * @param response
	 * @return
	 */
	public abstract Object doBussiness(Object request,Object response);
	/**
	 * @category 责任链业务处理入口(后),由子类实现
	 * @param request
	 * @param response
	 * @return
	 */
	public abstract Object afterBussiness(Object request,Object response);
	
	/**
	 * @category 当前责任链处理节点是否可以处理该请求。
	 * @param request
	 * @return
	 */
	public abstract boolean isMyBussiness(Object request);
	
}
  • 责任链节点实现类A/B
public class ChainHandlerA extends AbstractChainHandler{

	@Override
	public Object doBussiness(Object request, Object response) {
		System.out.println("A链业务处理前");
		return null;
	}

	@Override
	public Object afterBussiness(Object request, Object response) {
		System.out.println("A链业务处理后");
		return null;
	}

	@Override
	public boolean isMyBussiness(Object request) {
		return true;
	}
}
  • 责任链管理器接口
/**
 * @category 责任链管理器,负责维护责任链的关系和执行
 * @author tim
 *
 */
public interface IChainManager {
	/**
	 * @category 添加责任链节点
	 * @param handler
	 */
	public void addChain(AbstractChainHandler handler);
	/**
	 * @category 执行责任链
	 * @return
	 */
	public Object doChain();
}
  • 责任链管理器接口实现
public class ChainManager implements IChainManager{

	private List<AbstractChainHandler> chainList=new ArrayList<>();
	
	private AbstractChainHandler lastChain = null;
	
	@Override
	public void addChain(AbstractChainHandler handler) {
		chainList.add(handler);
		if(lastChain!=null){
			lastChain.setNextHandler(handler);
		}else{
			lastChain=handler;
		}
	}

	@Override
	public Object doChain() {
		Object resp=null;
		if(chainList.size()>0){
			resp=chainList.get(0).handlerRequest(null);
		}else{
			throw new RuntimeException();
		}
		return resp;
	}
}
  • 客户端使用示例
public class Client {
	public static void main(String[] args) {
		IChainManager chainManager = new ChainManager();
		chainManager.addChain(new ChainHandlerA());
		chainManager.addChain(new ChainHandlerB());
		chainManager.doChain();
	}
}

优点

将请求和处理分开,两者解耦,提高系统的灵活性。

缺点

  • 性能问题,需要从链头遍历到链尾
  • 调度不方便
  • 避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过阈值,超过则不允许该链建立,避免无意识地破坏系统性能。

命令模式

定义

命令模式是一个高内聚的模式,其定义为:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.

通用类图

优点

  • 类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行。
  • 可扩展性:Command子类可以非常容易地扩展,调用者Invoker和高层次的模块Client不产生严重的代码耦合。
  • 命令模式结合其他模式会更优秀:命令模式可以结合责任链模式,实现命令族解析任务;结合模板方法模式,则可以减少Command子类膨胀问题。

个人理解

命令角色在命令模式中其实就是充当了中介者的身份,把调用者与接收者联系起来了。

中介者模式

定义

用一个中介对象封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

Define an object that encapsulates how a set of objects interact.Mediator promotes loose coupling by keeping objects from referring to each other explicitly,and it lets you vary their interaction independently.

通用类图

  • Media抽象中介者角色

抽象中介者角色定义统一的接口,用于各同事角色之间的通信。

  • Concrete Mediator具体中介者角色

具体中介者角色通过协调各同事角色实现协作行为因此它必须依赖于各个同事角色。

  • Colleague同事角色

每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等,这种行为叫做自发行为(Self-Method),与其他的同事类或中介者没有任何的依赖;第二种是必须依赖中介者才能完成的行为,叫做依赖方法(Dep-Method)。

优点

减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者,减少了依赖,同时也降低了耦合。

缺点

中介者膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

使用场景

中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构。使用中介者模式,有利于把蜘蛛网梳理为星型结构,使原本复杂混乱的关系变得清晰简单。

常见用例

  • 机场调度中心 飞机需要询问调度中心是否可以降落或起飞,调度中心通过查看其他飞机的状态来回答询问。

  • MVC框架 V层需要通过C层来访问M层

  • 媒体网关 张三发消息给李四,张三发消息的动作需要使用媒体网关的接口转发给李四。

  • 中介服务 现实生活中的各种中介。

原型模式

定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.

在java中就是通过实现cloneable接口,就是原型模式了。

优点

  • 性能优良 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

  • 逃避构造函数的约束 既是优点也是缺点,直接在内存拷贝,构造函数不会执行。

使用场景

  • 资源优化场景
  • 性能和安全要求的场景
  • 一个对象多个修改者的场景

注意事项

  • 浅拷贝和深拷贝的问题 类的成员变量是一个可变的引用对象时,拷贝的只是该对象的引用。

  • 要使用clone方法,类的成员变量上不要增加final关键字。

代理模式

建造者模式

定义

也叫做生成器模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

通用类图

  • Product产品类

通常是实现了模板方法模式,也就是有模板方法和基本方法

  • Builder抽象建造者

规范产品的组建,一般是由子类实现。

  • ConcreteBuilder具体建造者

实现抽象类定义的所有方法,并且返回一个组建好的对象。

  • Director导演类

负责安排已有模块的顺序,然后告诉Builder开始建造。

优点

  • 封装性
  • 建造者独立,容易扩展:一个产品对应一个建造者
  • 便于控制细节风险

由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响

注意事项

  • 建造者模式关注的是零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但注重点不同。

模板方法模式

抽象工厂模式

工厂方法模式

单例模式

门面模式

访问者模式

定义

访问者模式(Visitor Pattern),封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

Represent an operation to be performed on the elements of an object structure.Visitor lets you define a new operation without changing the classes of the elements on which it operates.

通用类图

  • Visitor抽象访问者

抽象类或者接口,声明访问者可以访问哪些元素。

  • ConcreteVisitor具体访问者

它影响访问者访问到一个类后该怎么干,要做什么事情。

  • Element抽象元素

接口或抽象类,声明接受哪一类访问者访问

  • ObjectStruture结构对象

元素产生者,一般容纳在多个不同类、不同接口的容器,如List,Set,Map等,在项目中,一般很少抽象出这个角色。

优点

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活性非常高

缺点

  • 访问者依赖具体元素,违背了依赖倒置原则。
  • 具体元素变更比较困难,一般具体元素变更后,访问者也需要变更。

使用场景

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就说是用迭代器模式已经不能用途的场景。
  • 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。

状态模式

解析器模式

享元模式

桥接模式

定义

用抽象来桥接两者具有关联关系的对象。

Promise

Reactor

Fluent Interface

Monad

Lazy Loading

Mute Idiom