设计模式的六大原则

1.单一职责原则

不要存在多余一个导致类变更的原因,每个类应该实现单一的职责

2.里氏替换原则(LSP)

任何一个基类出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当子类可以替换掉基类时,且软件功能单位不受影响时,基类才能真正被复用,而子类也能在基类的基础上增加新的功能。里氏替换原则是对“开-闭”原理的补充。

3.依赖倒转原则

面向接口编程,依赖于抽象而不依赖与具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。这个是开闭原则的基础。

4.接口隔离原则

每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。

5.迪米特法则

一个类对自己依赖的类知道的越少越好。要求陌生类不要出现在自己类的局部变量中。

6.合成复用原则

尽量首先使用合成/聚合的方式,而不是使用继承。

23种设计模式

分为三大部分

创建型模式(五种):工厂方法模式、抽象工厂模式 、单例模式、建造者模式、原型模式

结构型模式(七种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

行为型模式(十一中):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型模式:

工厂方法模式

Interface:method();

ClassA implements Class:method(){A};

ClassB implements Class:method(){B};

InterfaceFactory:produce():

ClassFactoryA implements InterfaceFactory:produce(){return new ClassA()};

ClassFactoryB implements InterfaceFactory:produce(){return new ClassB()};

Test:InterfaceFactory factory=new ClassFactoryA();factory.produce().method();

抽象工厂方法

Interface1:method1();

ClassA implements Interface1:method1(){A};

ClassB implements Interface1:method1(){B};

Interface2:method2();

ClassC implements Interface2:method2(){A};

ClassD implements Interface2:method2(){B};

InterfaceFactory:produce():

ClassFactoryA implements InterfaceFactory:produce1(){return new ClassA()};

produce2(){return new ClassC()};

ClassFactoryB implements InterfaceFactory:produce1(){return new ClassB()};

produce2(){return new ClassD()};

Test:InterfaceFactory factory=new ClassFactoryA();factory.produce1().method1();

factory.produce2().method2();

工厂方法和抽象工厂方法的区别:

1.工厂方法只有一个抽象产品类,而抽象工厂方法有多个。

2.工厂方法只能创建一个产品类的具体实现,而抽象工厂模式可以创建多个。

工厂方法创建一种产品。重点在于怎么创建

抽象工厂方法需要创建一系列的产品,重点在于创建哪些产品。

单例模式

class SingleInstance

private static SingleInstance instance = null;

private SingleInstance(){};

private static synchronized init(){

if(instance == null)

    instance = new SingleInstance();

private static getInstance(){

if(instance == null)

    init();

return instance;

}

单例模式的好处

1.创建类频繁,系统开销大

2.降低了系统内存,减轻了gc压力

3.有些类,如果创建多个使用,会造成系统的流程控制混乱。

建造者模式(Builder)

模式讲解:

1.指挥者(Director)直接和客户(Client)进行需求沟通。

2.沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder)。

3.将各个部件的建造请求委派到具体的建造者(ConcreteBuilder)。

4.各个具体建造者负责产品部件的构建。

5.最终构建成具体产品。

优点:

1.易于解耦

将产品本身与产品的创建过程解耦,可以使用相同的创建过程得到不同的产品

2.易于精确控制对象的创建

将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰

3.易于拓展

增加新的具体建造者无需修改原有代类库,易于扩展。符合开闭原则。

缺点:

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大则不适用建造者模式,因此使用范围受到一定的限制

如果产品的内部变化复杂,可能会导致需要定义很多具体的建造者类来实现这种变化,导致系统变得很庞大。

应用场景

1.需要生成的产品对象内部结构复杂,且这些产品具备共用性

2.隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

原型模式(Prototype)

将一个对象作为原型,对其进行复制,克隆,产生一个和原对象类似的新对象。

java中复制对象通过clone()方法实现。

深拷贝和浅拷贝

浅拷贝:

将一个对象复制后,基本数据类型的对象会重新创建,而引用类型,指向的还是原对象所指向的

具体实现:

class Prototype implements Cloneable{

 public Object clone() throws CloneNotSupportedException(){

      Prototype pro = (Prototype)super.clone();

      return pro;

 }

}

深拷贝:

将一个对象复制后,无论是基本类型还是引用类型,都会被重新创建

具体实现

class Prototype implements Cloneable,Serializable{

 public Object deepClone(){

     ByteArrayOutputStream bos = new ByteArrayOutputStream();

     ObjectOutputStream oos = new ObjectOutputStream(bos);

     oos.writeObject(this);

     ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

     ObjectInputStream ois = new ObjectInputStream(bis);

     return ois.readObject();

 }

}

同流的方式,写入对象然后读出对象。

结构型模式:

适配器模式

1.将某个类的接口转换成客户端所期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

类的适配器模式

具体实现:

Class Source{ method1(){}}

Interface Target{ abstract method1(); abstract method2();}

Class Adapter extends Source implements target{method2(){};}

对象的适配器模式

具体实现:

调整Adapter类

Class Wrapper implements Target{

 private Source source;

 public Wrapper(Source source){

      this.source = source;

 }

 method1(){

      source.method1();

 }

 method2(){

     //具体实现

 }

}

接口的适配器模式

具体实现:

调整Wrapper类

Abstract Class Wrapper1 implements Target(){

abstract method1(){};

abstract.method2(){};

}

装饰模式(Decorator Pattern)

不改变原有对象的前提下,动态地给一个对象增加一些额外的功能

实现:

装饰者基类,继承被装饰类的基类

抽象被装饰的方法类

装饰者实现类,实现装饰者基类

基类参数的构造方法

实现抽象方法

抽象方法中调用基类方法,并添加装饰实现。

优点:

1.比继承更加灵活:不同于在编译期起作用的继承;装饰者模式可以在运行时扩展一个对象的功能。另外也可以通过配置文件在运行时选择不同的装饰器,从而实现不同的行为。也可以通过不同的组合,可以实现不同效果

符合“开闭原则”:装饰者和被装饰者可以独立变化。用户可以根据需要增加新的装饰类,在使用时再对其进行组合,原有代码无须改变。

缺点

装饰者模式需要创建一些具体装饰类,会增加系统的复杂度。

代理模式(Proxy Pattern)

为某个对象提供一个代理,并由这个代理对象控制对原对象的访问

实现:

Proxy implements Service

private Service service = new ServiceImpl();

public void service(){

//被增强的方法

service.service();

//被增强的方法

}

main{

Service service = new Proxy();

service.service();

}

优点:

1.降低系统的耦合度:代理模式能够协调条用者和被调用者,在一定程度上降低了系统的耦合程度。

2.不同类型的代理可以对客户端对目标对象的访问进行不同的控制:

2.1远程代理,使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端的请求。

2.2虚拟代理通过使用一个小对象代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。

2.3保护代理可以控制客户端对真实对象的使用权限

缺点:

1.由于在客户端和被代理对象之间增加了代理对象,因此可能会让客户端请求的速度变慢。

桥接模式(Bridge Pattern)

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

优点:

1.扩展性好,符合开闭原则:将抽象与实现分离,让二者可以独立变化

缺点:

1.在设计之前,需要识别出两个独立变化的维度。

我们首先需要一个桥梁,它是一个接口,可以提供的接口方法。

BridgeInterface{

interfaceApi();

}

Object implements BridgeInterface{

interfaceApi(){

//doSomething;

}

}

AbstractClass{

BridgeInterface bridgeInterface;

AbstractClass(BridgeInterface bridgeInterface){

this.bridgeInterface = bridgeInterface;

}

abstract void interfaceApi();

}

ObjectClass extends AbstractClass{

ObjectClass(BridgeInterface bridgeInterface){

super(bridgeInterface);

}

interfaceApi{

bridgeInterface.interfaceApi();

}

}

外观模式(Facade Pattern)

外观模式定义了一个高层接口,为子系统中的一组接口提供一个统一的接口。外观模式又称门面模式,它是一种结构型设计模式。

实现:

class ObjectMaker{

private Object a1;

private Object a2;

ObjectMaker(){

a1 = new A1();

a2 = new A2();

}

methodA1(){

a1.method();

}

methodA2(){

a2.method();

}

}

优点:

1.实现了客户端与子系统间的解耦:客户端无需知道子系统的接口,简化了客户端调用子系统的调用过程,使得子系统使用起来更加容易。同时便于子系统的扩展和维护

2.符合迪米特法则:子系统只需将需要外部调用的接口暴露给外观类即可,而且他的接口则可以隐藏起来。

缺点:

1.违背了开闭原则:在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的代码。

享元模式(Flyweight Pattern)

运用共享技术复用大量细粒度的对象,降低程序内存的占用,提高程序的性能。

1.实现

复用对象的对象

优点:

1.使用享元模式可以减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份,降低系统的使用内存,也可以提升性能。

2.享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享

缺点:

1.使用享元模式需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

2.对象在缓冲池中的复用需要考虑线程问题。

行为型

责任链模式(Chain of Responsibility Pattern):

避免请求发送者与接受者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。责任链模式是一种对象行为型模式。

优点:

1.责任链模式使得一个对象无须知道其他哪一个对象处理其请求,对象仅需知道该请求会被处理即可,接收者和发送者都没有对方明确信息,且链中的对象不需知道链的结构,由客户端负责链的创建,降低了系统的耦合度。

2.请求处理对象仅需维持一个指向其后继者的引用,而不需要维持它对所有的候选处理者的引用,可简化对象的相互连接。

3.在给对象分派职责时,职责链可以给我们更多的灵活性,可以通过在运行时对该链进行动态的增加或修改来增加或改变处理一个请求的职责。

4.在系统中增加一个新的具体请求处理者时无须修改原有系统的代码,只需要在客户端重新建链即可,从这一点来看是符合“开闭原则”的。

缺点:

1.由于一个请求没有明确的接收者,那么就不能保证它一定会被处理,该请求可能一直到链的末端都得不到处理;一个请求也可能因职责链没有被正确配置而得不到处理。

2.对于比较长的责任链,请求的处理可能涉及到多个处理对象,系统性能将受到一定影响,而且在进行代码调试时不太方便。

3.如果建链不当,可能会造成循环调用,将导致系统陷入死循环。

命令模式(Command Pattern):

将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为模式,其别名为动作模式或事物模式。

优点:

1.降低系统的耦合度。由于请求者与接收者之间不存在直接引用,因此请求者与接收者之间实现完全解耦,相同的请求者可以对应不同的接收者,同样相同的接收者可可以供不同的请求者使用,两者之间具有良好的独立性。

2.新的命令可以很容易地加入到系统中。由于增加新的具体命令类不会影响到其他类,因此增加新的具体命令类很容易,无须修改原有系统源代码,甚至客户类代码,满足“开闭原则”的要求。

3.可以比较容易地设计一个命令队列或宏命令

4.为请求的撤销和恢复操作提供了一种设计和实现方案

缺点:

1.使用命令模式可能会导致某些系统有过多的具体命令类。因此针对每一个对请求接受者的调用操作都需要设计一个具体命令类,因此在某些系统中可能需要提供大量的具体命令类,这将影响命令模式的使用。

解释器模式(Interpreter Pattern):

定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为模式。

优点:

1.易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

2.每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

3.实现文法较为容易。在抽象语法树种每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些具体自动生成节点类代码。

4.增加新的解释表达方式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或者非终结符表达式类,原有表达式类代码无需修改,符合“开闭原则”。

缺点:

1.对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。

2.执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

迭代器模式(Iterator Pattern):

提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。迭代器模式是一种对象行为型模式。

优点:

1.它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式

2.迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。

3.在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须改变原有代码,满足“开闭原则”的要求

缺点:

1.由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,在一定程度上增加了系统的复杂性。

2.抽象迭代器的涉及难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类Listlterator等来实现,而Listlterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。

中介者模式(Mediator Pattern):

用一个中介对象来封装一系列的对象交互,中介者使个对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。

优点:

1.中介者模式简化了对象之间的交互,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互,一对多关系更容易理解、维护和扩展,将原本难以理解的网状结构转换成相对简单的星型结构。

2.中介者模式可将各同事对象解耦。中介者有利于各同事之间的松耦合,我们可以独立的改变和复用每一个同事和中介者,增加新的中介者和新的同事类都比较方便,更好地符合“开闭原则”。

3.可以减少子类生成,中介者将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成新的中介者子类即可,这使各个同事类可被重用,无须对同事类进行扩展。

缺点:

在具体中介者类中包含了大量同事之间的交互细节,可能会到导致具体中介者类非常复杂,使得系统难以维护。

备忘录模式(Memento Pattern):

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为模式,其别名为Token。

优点:

1.它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。

2.备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

缺点:

1.资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

观察者模式(Observer Pattern):

定义对象之间一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish-Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source-Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

优点:

1.观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。

2.观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维护一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密的耦合在一起,因此他们可以属于不同的抽象化层次。

3.观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系列设计的难度。

4.观察者模式满足“开闭原则”的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

缺点:

1.如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。

2.如果在观察者和观察目标之间存在循环依赖,观察目标会触发他们之间进行循环调用,可能导致系统崩溃

3.观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

状态模式(State Pattern):

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects For States),状态模式是一种对象行为型模式。

优点:

1.封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。

2.将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。

3.允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起

4.可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点:

1.状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。

2.状态模式的结构和实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。

3.状态模式对”开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需要修改对应类的源代码。

策略模式(Strategy Pattern):

定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。策略模式是一个对象行为模式。

优点:

1.策略模式提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。

2.策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族,恰当使用继承可以把公共的代码移动到抽象策略中,从而避免重复的代码。

3.策略模式提供了一种可以替换继承关系的办法。如果不使用策略模式,那么使用算法的环境类就可能会有一些子类,每一个子类提供一种不同的算法。但是,这样一来算法的使用就和算法本身混在一起,不符合“单一职责原则”,决定使用哪一种算法的逻辑和该算法本身混在一起,从而不可能再独立演化;而且使用继承无法实现算法或行为在程序运行时的动态切换。

4.使用策略模式可以避免多重复条件选择语句。多重复条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将他们全部硬编码在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。

5.策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。

缺点:

1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法。换言之,策略模式只适用于客户端知道所有的算法或行为的情况

2.策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加一个新的具体策略类。

3.无法同时在客户端使用多个策略类,也就是说,在使用策略模式时,客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类来完成剩余功能的情况。

模板模式(Template Pattern):

定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

优点:

1.在父类中形式化地定义一个算法,而由它的子类来实现细节的处理,在子类实现详细的处理算法时不会改变算法中步骤的执行次序。

2.模板方法模式是一种代码复用技术,它在类库设计中尤为重要,它提取了类库中的公共行为,将公共行为放在父类中,而通过其子类来实现不同的行为,它鼓励我们恰当使用继承来实现代码复用。

3.可实现一种反向控制结构,通过子类覆盖父类的钩子方法来决定某一特定步骤是否需要执行。

4.在模板方法模式中可以通过子类来覆盖父类的基本方法,不同的子类可以提供基本方法的不同实现,更换和增加新的子类很方便,符合单一职责原则和开闭原则

缺点:

1.需要为每一个基本方法的不同实现提供一个子类,如果父类中可变的基本方法太多,将会导致类的个数增加,系统更加庞大,设计也更加抽象,此时,可结合桥接模式来进行设计。

访问者模式(Vistor Pattern):

提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为模式。

优点:

1.增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问类,实现简单,无须修改源代码,符合“开闭原则”。

2.将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的对象访问者访问。

3.让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。

缺点:

1.增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求

2.破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法提供访问者访问。