摘自《设计模式之禅》(第2版)
设计原则
单一职责原则(Single Responsibility Principle, SRP)
- There should never be more than one reason for a class to change
- 有且仅有一个原因引起类的变更
作用
- 类复杂性降低
- 可读性提高
- 可维护性高
- 变更引起的风险降低
单一职责不仅仅是业务上的单一,还需要透过业务关注具体实现是否属于单一职责
有相同生命周期的业务可以通过继承传递行为实现
其实类的单一职责确实会受非常多因素的制约,纯理论地来讲,这个原则是非常优秀的,但是现实有现实的难处,你必须去考虑项目工期、成本、人员技术水平、硬件情况、网络情况等等...等等。对于单一职责原则,接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化
里氏替换原则(Liskov Substitution Principle, LSP)
- If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged where o1 is substituted for o2 then S is a subtype of T
- 如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型
作用
- 增强程序健壮性
- 版本升级时可以保持很好的兼容性
java中的继承是单继承,但可以通过子类多层延展继承可以实现子类的更灵活、更多样化的个性(o2 extents o1, o3 extents o2, o3则是在o2的基础上进行延展的)
如果子类不能完整地实现父类的方法,或者父类的划些方法在子类中已经发生“畸变”,则建议断开父子关系,采用依赖、聚集、组合等关系代替继承
依赖倒置原则(Dependence Inversion Principle, DIP)
- High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions
- 高层模块不应该依赖低层模块,两者都可应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象
作用
- 减少类间的耦合性
- 提高系统的稳定性
- 降低并行开发引起的风险
- 提高代码的可读性和可维护性
依赖倒置原则是6个设计原则中最难以实现的原则,它是实现开闭原则的重要途径(对扩展开放,对修改关闭)
接口隔离原则
- Clients should not be forced to depend upon interfaces that they don't use
- The dependency of one class to another one should depend on the smallest possible interface
- 客户端不应该依赖它不需要的接口
- 类间的依赖关系应该建立在最小的接口上
作用
- 增加了系统扩展的灵活性
- 提高了可维护性
依据接口隔离原则接口粒度越细越好,但不能一味的去贯彻粒度更细的接口定义,我们需要根据经验和常识来决定业务上接口定义的合适粒度
迪米特法则(Law of Demeter, LoD)
- 也称为最少知识原则(Least Knowledge Principle, LKP)
- 一个类应该对自己需要耦合或调用的类知道的最少
- 一个对象应该对其他对象有最少的了解
作用
- 类间解耦,弱耦合,提高类的复用率
迪米特法则要求类间解耦,但解耦是有限度的,需要适度地考虑这个原则,不能为了套用原则而做项目。因为过度的解耦会产生大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度
开闭原则(Open Close Principle, OCP)
- Software entities like classes, modules and functions should be open for extension but closed for midifications
- 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭
作用
- 对单元测试的最少修改
- 提高类的复用率
- 提高可维护性
- 提高了项目的健壮性
如何应用开闭原则?向上抽象、封装变化
设计模式
单例模式(Singleton Pattern)
- Ensure a class has only one instance, and provide a global point of access to it
- 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
优点
- 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显了
- 单例模式可以避免对相同资源的多重占用,例如一个写文件动作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
缺点
- 单例模式一般没有接口,扩展很困难。
- 单例模式与单一职责原则稍有冲突,在一个单例类中即实现了“单例”的功能,又结合了业务逻辑
高并发的情况下,要注意单例模式的线程同步问题
单例实例的创建结合在web容器初始化时,可以不用考虑重复创建实例的问题,因为web容器初始化是单线程
工厂方法模式(Factory Method Pattern)
- Define an interface for creating an object, but let subclass decide which class to instantiate. Factory Method lets a class defer instantiation to sub classes
- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
Product负责定义产品的共性,实现对事物的抽象定义;Creator抽象工厂,实现创建事物的抽象定义(实现创建事物时所需参数的约束);ConcreteProduct、ConcreteCreator具体实现类。
优点
- 良好的封装性,代码结构清晰
- 降低模块间的耦合
- 易扩展,屏蔽了业务逻辑实现
符合迪米特原则,也符合依赖倒置原则,当然也符合里氏替换原则
模板方法模式(Template Method Pattern)
- Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure
- 定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
AbstractClass是抽象模板,它的方法分为两类:
- 基本方法 —— 由子类实现的方法,并且在模板方法中被调用
- 模板方法 —— 可以有一个或几个,一般是一个具体方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑(一般模板方法都要加上final关键字,不允许被覆写)
优点
- 封装不变部分,扩展可变部分
- 提取公共部分代码,便于维护
- 行为流程由父类控制,子类实现具体操作(基本方法是由
子类实现的,因此子类可以通过扩展的方式增加相应的功能)
建造者模式(Builder Pattern)
- 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建造不同的表示类
优点
- 封装性
- 建造者独立,容易扩展
- 便于控制细节风险
应用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果
- 产品类非常复杂,或者产品类中的高用顺序不同产生了不同的效能
建造者模式关注的是零件类型和装配顺序,这是它与工厂方法模式最大不同的地方,虽然同为创建类模式,但是注重点不同
代理模式(Proxy Pattern)
- Provide a surrogate or placeholder for another object to control access to it
- 为其它对象提供一种代理以控制对这个对象的访问
代理模式也叫做委托模式,它是一项基本设计技巧。许多其他的模式,如状态模式、策略模式、访问模式本质上是在更特殊的场景中对委托模式的升级应用。
- Subject抽象类 —— 普通业务类型定义
- RealSubject具体实现类 —— 被委托角色、被代理角色。业务逻辑的具体执行者
- Proxy代理类 —— 委托类、代理类。把抽象类需要执行的方法调用委托给具体实现类,并且在执行前后做预处理和善后处理。可以代理多个被委托类或代理者。
优点
- 职责清晰(被代理可以纯粹的只关注本职业务的实现,其它处理交由代理对象完成)
- 高扩展性
原型模式(Prototype Pattern)
- Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype
- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型模式的核心是一个clone方法,能过该方法进行对象的拷贝
优点
- 性能优良(原型模式是对内存二进制流的拷贝,要比直接new一个对象性能好很多)
- 逃避构造函数的约束
中介者模式(Mediator Pattern)
- 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 is lets you vary their interaction independently
- 用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互
- Mediator抽象中介者角色(定义统一的接口,用于各同事角色之间的通信)
- Colleague同事角色(调动中介者中的行为)
- ConcreteMediator具体中介者角色(协调各同事角色实现协作)
优点
- 减少类间的依赖
缺点
- 创建的中介者类会膨胀的很大,而且逻辑复杂
应用
- 项目中的调度中心
- MVC框架中的C(Controller)层
- 项目中的网关
命令模式(Command Pattern)
- Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log request, and support undoable operations
- 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能
示例代码
public class CommandImpl {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Receiver receiver = new ConcreteReceiver1();
Command command = new ConcreteCommand1(receiver);
invoker.setCommand(command);
invoker.action();
}
public static abstract class Receiver {
public abstract void doSomething();
}
public static class ConcreteReceiver1 extends Receiver {
@Override
public void doSomething() {
}
}
public static abstract class Command {
public abstract void execute();
}
public static class ConcreteCommand1 extends Command{
private Receiver receiver;
public ConcreteCommand1(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
this.receiver.doSomething();
}
}
public static class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action(){
this.command.execute();
}
}
}
优点
- 类间解耦
- 扩展性高
缺点
- 类膨胀
责任链模式(Chain of Responsibility Pattern)
- Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving object and pass the request along the chain until an object handles it
- 使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止
优点
- 请求和处理者的解耦
缺点
- 调试不方便(当责任链太长或者每个结点都可以执行部分请求处理,遇到错误的时候会是一件很头疼的事)
装饰模式(Decorator Pattern)
- Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality
- 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活
- Component抽象构件(最核心对象的抽象类或接口)
- ConcreteComponent具体构件(被装饰类)
- Decorator装饰角色(必然有一个变量指向Component抽象构件)
- ConcreteDecorator具体装饰角色
示例代码
public class DecoratorImpl {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Decorator decorator = new ConcreteDecorator(component);
decorator.operate();
}
public static abstract class Component{
public abstract void operate();
}
public static class ConcreteComponent extends Component{
@Override
public void operate() {
System.out.println("concrete component operate");
}
}
public static abstract class Decorator extends Component{
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operate() {
this.component.operate();
}
}
public static class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operate() {
System.out.println("concrete decorator before");
super.operate();
System.out.println("concrete decorator after");
}
}
}
优点
- 装饰类和被装饰类都可以独立扩展,互不干预
- 继承关系的一个替代方案
- 动态地扩展一个实现类的功能
策略模式(Strategy Pattern)
- Define a family of algorithms, encapsulate each one, and make them
- 定义一组算法,将每个算法都封装起来,并且使它们之间可以互换
- Context封装角色(上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化)
- Strategy抽象策略角色(抽象类或接口,定义每个策略或算法必须具有的方法和属性)
- ConcreteStrategy具体策略角色
示例代码
public class StrategyImpl {
public static void main(String[] args) {
Boolean strategyFlag = Boolean.TRUE;
Strategy strategy = null;
if (Boolean.FALSE.equals(strategyFlag)) {
strategy = new ConcreteStrategy();
}
if(Boolean.TRUE.equals(strategyFlag)){
strategy = new ConcreteStrategyOther();
}
Context context = new Context(strategy);
context.doThing();
}
public interface Strategy{
void firstDoSomething();
void secondDoSomething();
}
public static class ConcreteStrategy implements Strategy{
@Override
public void firstDoSomething() {
System.out.println("concrete strategy first");
}
@Override
public void secondDoSomething() {
System.out.println("concrete strategy second");
}
}
public static class ConcreteStrategyOther implements Strategy{
@Override
public void firstDoSomething() {
System.out.println("other concrete strategy first");
}
@Override
public void secondDoSomething() {
System.out.println("other concrete strategy second");
}
}
public static class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void doThing() {
this.strategy.firstDoSomething();
this.strategy.secondDoSomething();
}
}
}
优点
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
扩展(策略枚举)
public enum StrategyEnum{
ARITHMETIC_ADD(){
@Override
public int exec(int a, int b) {
return a + b;
}
},
ARITHMETIC_SUB(){
@Override
public int exec(int a, int b) {
return a - b;
}
};
public abstract int exec(int a, int b);
}
适配器模式(Adapter Pattern)
- Convert the interface of a class into another interface clients expect. Adapter lets class work together that couldn't otherwise because of incompatible interfaces
- 将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作
示例代码
public class AdapterImpl {
public static void main(String[] args) {
//原有的业务逻辑
Target target = new ConcreteTarget();
target.request();
//现有业务适配原有业务
Target target1 = new Adapter();
target1.request();
}
public interface Target{
void request();
}
public static class ConcreteTarget implements Target{
@Override
public void request() {
System.out.println("If you need any help, pls call me");
}
}
public static class Adaptee {
public void doSomething() {
System.out.println("I'm kind of busy, leave me alone, pls");
}
}
public static class Adapter extends Adaptee implements Target{
@Override
public void request() {
super.doSomething();
}
}
}
其中Adapter中的request方法就展现了适配器的作用,虽然调用的是原业务Target#request方法,实际执行的却是现有Adaptee中的逻辑
优点
- 可以让两个没有任何关系的类在一起运行
- 增加了类的透明性(对于高层的接口调用没有变化)
- 提高了类的复用度
- 灵活性好
适配器模式一般是为了解决正在服役项目的扩展问题,而不是初期整体详细设计阶段需要考虑的设计模式
迭代器模式(Iterator Pattern)
目前已经是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发
- Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation
- 提供一种方法访问一个容器对象中的各个元素,而又不需暴露该对象的内部细节
- Iterator抽象迭代器(定义访问和遍历元素的接口,基本都会有3个固定的方法,first(),next(),hasNext()是否已经访问至底部)
- ConcreteIterator具体的迭代器
- Aggregate抽象容器(负责提供创建具体迭代器角色的接口,提供iterator()方法)
- ConcreteAggregate具体容器
使用Java提供的Iterator一般是可以满足需求的
组合模式(Composite Pattern)
- Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly
- 将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性
- Composite抽象构件角色(定义参加组合对象的共有方法和属性)
- Leaf叶子构件(其下再也没有其他分支,也就是遍历的最小单位)
- Composite树枝构件(组合树支节点和叶子节点形成一个树形结构)
示例代码
public class CompositeImpl {
public static void main(String[] args) {
//创建根节点
Composite root = new Composite();
root.doSomething();
//创建树枝构件
Composite branch = new Composite();
//创建叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
root.add(leaf);
display(root);
}
public static void display(Composite composite) {
for (Component c : composite.getChildren()) {
if (c instanceof Leaf) {
c.doSomething();
continue;
}
display((Composite)c);
}
}
public static abstract class Component{
public void doSomething(){
//树枝和叶子的共性逻辑也可写在这里
}
}
public static class Composite extends Component{
//构件容器
private ArrayList<Component> componentArrayList = new ArrayList<>();
public void add(Component component) {
this.componentArrayList.add(component);
}
public void remove(Component component) {
this.componentArrayList.remove(component);
}
public ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}
public static class Leaf extends Component{
@Override
public void doSomething() {
System.out.println("I'm Leaf");
}
}
}
优点
- 高层模块调用简单(一棵树形机构中的所有节点都是Componet,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码)
- 节点自由增加
缺点
- 树叶和树枝的调用直接使用了实现类,它限制了接口的影响范围
应用场景
- 维护和展示“部分-整体”关系的场景,如树形菜单、文件和文件夹管理
观察者模式(Observer Pattern)
- Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically
- 定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新
- Subject被观察者(管理观察者并通知观察者)
- Observer观察者(对接收到的信息进行处理)
- ConcreteSubject具体的被观察者(定义被观察者自己的业务逻辑, 同时定义对哪些事件进行通知)
- ConcreteObserver具体的观察者(实现当前观察者在接收到信息后的业务处理逻辑)
示例代码
public class ObserverImpl {
public static void main(String[] args) {
//创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
ConcreteObserver observer = new ConcreteObserver();
subject.addObserver(observer);
//被观察者在执行完自己的业务逻辑后会自行通知被观察者
subject.doSomething();
}
public interface Observer {
void update();
}
public static class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("update data");
}
}
public static abstract class Subject {
//观察者数组
private Vector<Observer> obsVector = new Vector<>();
public void addObserver(Observer observer) {
this.obsVector.add(observer);
}
public void delObserver(Observer observer) {
this.obsVector.remove(observer);
}
//通知所有观察者
public void notifyObservers() {
for (Observer obs : this.obsVector) {
obs.update();
}
}
}
public static class ConcreteSubject extends Subject {
public void doSomething() {
System.out.println("concrete subject");
super.notifyObservers();
}
}
}
优点
- 观察者和被观察者之前是抽象耦合,容易扩展
- 建立一套触发机制
应用场景
- 关联行为且不是“组合”关系的场景
- 事件多级触发场景
- 跨系统的消息交换场景
多组观察者会存在广播链的问题,所以建议消息接收最多再被转发一次
扩展(jdk自有观察者模式代码实现)
public class ObserverJavaImpl {
public static void main(String[] args) {
//创建一个被观察者
SubjectJava subjectJava = new SubjectJava();
//创建一个观察者
ObserverJava observerJava = new ObserverJava();
//观察者加入被观察者的通知队列
subjectJava.addObserver(observerJava);
subjectJava.doSomething();
}
/**
* 被观察者类
*/
public static class SubjectJava extends Observable {
public void doSomething() {
System.out.println("oberver java");
super.setChanged();
super.notifyObservers();
}
}
/**
* 观察者类
*/
public static class ObserverJava implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.println("update data");
}
}
}
门面模式(Facade Pattern)
- Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use
- 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用
门面模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生
- Facade门面角色(客户端可以调用这个角色方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类)
- Subsystem子系统角色
示例代码
public class FacadeImpl {
public static void main(String[] args) {
Facade facade = new Facade();
facade.dothing1();
facade.dothing2();
facade.dothing3();
}
public static class Subsystem1{
public void doSomething1() {
System.out.println("do thing 1");
}
}
public static class Subsystem2{
public void doSomething2() {
System.out.println("do thing 2");
}
}
public static class SubSystem3 {
public void doSomething3() {
System.out.println("do thing 3");
}
}
public static class Facade{
private Subsystem1 subsystem1 = new Subsystem1();
private Subsystem2 subsystem2 = new Subsystem2();
private SubSystem3 subSystem3 = new SubSystem3();
public void dothing1() {
this.subsystem1.doSomething1();
}
public void dothing2() {
this.subsystem2.doSomething2();
}
public void dothing3() {
this.subSystem3.doSomething3();
}
}
}
优点
- 减少系统的相互依赖
- 提高了灵活性
- 提高安全性
备忘录模式(Memento Pattern)
- Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later
- 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法
- Originator发起人角色(记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据)
- Memento备忘录角色(负责存储Originator发起人对象的内部状态)
- Caretaker备忘录管理员角色(对备忘录进行管理、保存和提供备忘录)
示例代码
public class MementoImpl {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
//创建一个备忘录
caretaker.setMemento(originator.createMemento());
//恢复一个备忘录
originator.restoreMemento(caretaker.getMemento());
}
public static class Memento{
private String state = "";
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
public static class Originator{
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento createMemento() {
return new Memento(this.state);
}
public void restoreMemento(Memento memento) {
this.setState(memento.getState());
}
}
public static class Caretaker{
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
}
由于备忘录模式有太多的变形和处理方式,每种方式都有它自己的优点和缺点,标准的备忘录模式很难在项目中遇到,基本上都有一些变换处理方式。因此,我们在使用备忘录模式时主要了解中何应用以及需要注意哪些事项就成了。备忘录模式需要注意的事项:1.备忘录的生命周期;备忘备创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。2.备忘录的性能;不要频繁建立备份的场景中使用备忘录模式
访问者模式(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 with it
- 封装一些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
- Visitor抽象访问者(抽象类或接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的)
- ConcreteVisitor具体的访问者(它影响访问者访问到一个类后该怎么干,要做什么事情)
- Element抽象元素(接口或抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的)
- ConcreteElement具体元素(实现accept方法,通常是visitor.visit(this),基本上形成了一种模式)
- ObjectStruture结构对象(元素产生者,一般容纳在多个不同类、不同接口的容器;它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或一个聚集)
示例代码
public class VisitorImpl {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Element element = ObjectStruture.createElement();
element.accept(new Visitor());
}
}
public interface IVisitor {
void visit(ConcreteElement1 element1);
void visit(ConcreteElement2 element2);
}
public static class Visitor implements IVisitor {
@Override
public void visit(ConcreteElement1 element1) {
element1.doSomething();
}
@Override
public void visit(ConcreteElement2 element2) {
element2.doSomething();
}
}
public static abstract class Element {
public abstract void doSomething();
public abstract void accept(IVisitor visitor);
}
public static class ConcreteElement1 extends Element {
@Override
public void doSomething() {
System.out.println("element 1");
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
public static class ConcreteElement2 extends Element {
@Override
public void doSomething() {
System.out.println("element 2");
}
@Override
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
public static class ObjectStruture {
public static Element createElement() {
return new Random().nextInt(100) > 50 ? new ConcreteElement1() : new ConcreteElement2();
}
}
}
优点
- 符合单一职责原则
- 优秀的扩展性
- 灵活性非常高
缺点
- 具体元素对访问者公布细节
- 具体元素变更比较困难
- 违背了依赖倒置原则
状态模式(State Pattern)
- Allow an object to alter its behavior when its internal state changes. The object will appear to change its class
- 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类
- State抽象状态角色(接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换)
- ConcreteState具体状态角色(每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态)
- Context环境角色(定义客户端需要的接口,并且负责具体状态的切换)
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样;状态模式主要应用在当控制一个对象状态的条件表达式过于复杂的场景,可以把状态的判断逻辑转移到表示不同状态的一系列类中,把复杂的判断逻辑简化
示例代码
public class StateImpl {
public static void main(String[] args) {
Context context = new Context();
context.setCurrentState(new ConcreteState1());
context.handle1();
context.handle2();
}
/**
* 环境角色不成文的约束
* 1.把状态对象声明为静态常量,有几个状态对象就声明几个静态常量
* 2.环境角色具有抽象角色定义的所有行为,具体执行使用委托方式
*/
public static class Context{
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
private State currentState;
public State getCurrentState() {
return currentState;
}
public void setCurrentState(State currentState) {
this.currentState = currentState;
this.currentState.setContext(this);
}
/**
* 行为委托
*/
public void handle1() {
this.currentState.handle1();
}
/**
* 行为委托
*/
public void handle2() {
this.currentState.handle2();
}
}
public static abstract class State{
protected Context context;
public void setContext(Context context) {
this.context = context;
}
public abstract void handle1();
public abstract void handle2();
}
public static class ConcreteState1 extends State{
@Override
public void handle1() {
System.out.println("state1 handle1");
}
@Override
public void handle2() {
super.context.setCurrentState(Context.STATE2);
super.context.handle2();
}
}
public static class ConcreteState2 extends State{
@Override
public void handle1() {
super.context.setCurrentState(Context.STATE1);
super.context.handle1();
}
@Override
public void handle2() {
System.out.println("state2 handle2");
}
}
}
Context环境角色是执行状态变更的委托类,而ConcreteState具体状态类中只实现自己当前状态应该执行的业务逻辑,其余行为都委托给Context去执行
优点
- 结构清晰
- 遵循设计原则
- 封装性非常好
解释器模式(Interpreter Pattern)
- Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the lanaguage
- 给定一门语言,定义它的方法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子
- AbstractExpression抽象解释器
- TerminalExpression终结符表达式(实现与方法中的元素关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多实例,对应不同的终结符)
- NonterminalExpression非终结符表达式(方法中的每条规则对应于一个非终结表达式)
- Context环境角色
示例代码
public class InterpreterImpl {
public static void main(String[] args) {
PlusExpression plusExpression = new PlusExpression(new TerminalExpression(), new TerminalExpression());
Integer i = plusExpression.interpreter(new Context("3", "4"));
System.out.println(i);
}
public static class Context{
private LinkedList<String> linkedList;
public Context(String... s) {
this.linkedList = new LinkedList<>(Arrays.asList(s));
}
public LinkedList<String> getLinkedList() {
return linkedList;
}
}
public static abstract class Expression{
public abstract Object interpreter(Context ctx);
}
public static class TerminalExpression extends Expression{
@Override
public Object interpreter(Context ctx) {
return ctx.linkedList.pop();
}
}
public static abstract class NonterminalExpression extends Expression{
Expression[] expressions = null;
public NonterminalExpression(Expression... expressions) {
this.expressions = expressions;
}
}
public static class PlusExpression extends NonterminalExpression{
public PlusExpression(Expression... expressions) {
super(expressions);
}
@Override
public Integer interpreter(Context ctx) {
Integer i = 0;
for (Expression expression : super.expressions) {
i += Integer.valueOf((String) expression.interpreter(ctx));
}
return i;
}
}
}
优点
- 解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了
缺点
- 会引起类膨胀
- 采用递归的调用方法
- 效率问题
享元模式(Flyweight Pattern)
- Use sharing to support large numbers of fine-grained objects efficiently
- 使用共享对象可有效地支持大量的细粒度的对象
享元模式是池技术的重要实现方式
- Flyweight抽象享元角色(定义出对象的外部状态和内部状态的接口或实现)
- ConcreteFlyweight具体享元角色(需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的)
- unsharedConcreteFlyweight不可共享的享元角色(不能够使用共享技术的对象)
- FlyweightFactory享元工厂(构造一个池容器,同时提供从池中获得对象的方法)
示例代码
public class FlyweightImpl {
public static void main(String[] args) {
Flyweight red = FlyweightFactory.getFlyweight("RED");
red.operate();
}
public static abstract class Flyweight {
//内部状态
private String intrinsic;
//外部状态
private final String extrinsic;
public Flyweight(String extrinsic) {
this.extrinsic = extrinsic;
}
public abstract void operate();
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
public static class ConcreteFlyweight extends Flyweight{
public ConcreteFlyweight(String extrinsic) {
super(extrinsic);
}
@Override
public void operate() {
System.out.println("operate 1 " + super.getIntrinsic());
}
}
public static class FlyweightFactory {
private static HashMap<String, Flyweight> pool = new HashMap<>();
public static Flyweight getFlyweight(String extrinsic) {
//需要返回的对象
Flyweight flyweight = null;
if (pool.containsKey(extrinsic)) {
flyweight = pool.get(extrinsic);
}else {
flyweight = new ConcreteFlyweight(extrinsic);
flyweight.setIntrinsic("RUNNING");
pool.put(extrinsic, flyweight);
}
return flyweight;
}
}
}
享元模式中的内部状态可以理解为,在对象创建成功后就不再进行变更的状态或属性,可以存在多个;外部状态则会随着不同场景或不同需求的调用而改变;(绘制图像像素的位置信息是外部状态,而颜色可以是内部状态)
优点
- 减少应用程序创建的对象
桥梁模式(Bridge Pattern)
- Decouple an abstraction from its implementation so that the two can vary independently
- 将抽象和实现解耦,使得两者可以独立变化
示例代码
public class BridgeImpl {
public static void main(String[] args) {
Implementor implementor = new ConcreteImplementor();
RefineAbstraction refineAbstraction = new RefineAbstraction(implementor);
refineAbstraction.request();
}
public interface Implementor {
void doSomething();
void doAnything();
}
public static class ConcreteImplementor implements Implementor{
@Override
public void doSomething() {
System.out.println("concrete implementor do something");
}
@Override
public void doAnything() {
System.out.println("concrete implementor do anything");
}
}
public static abstract class Abstraction {
private Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public void request() {
this.implementor.doSomething();
}
public Implementor getImplementor() {
return implementor;
}
}
public static class RefineAbstraction extends Abstraction {
public RefineAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void request() {
System.out.println("new something");
super.request();
super.getImplementor().doAnything();
}
}
}
优点
- 抽象和实现分离
- 优秀的扩充能力
- 实现细节对客户透明
应用场景
- 不希望或不适用使用继承
- 接口或抽象不稳定
- 重用性要求较高