设计模式分为三大类:
创建型模式,5种:
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
结构型模式,7种:
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
行为型模式,11种:
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介模式、解释器模式
并发模式:
线程池模式:
aHR0cDovL2RsLml0ZXllLmNvbS91cGxvYWQvYXR0YWNobWVudC8wMDgzLzExNzkvNTdhOTJkNDItNGQ4NC0zYWE5LWE4YjktNjNhMGIwMmMyYzM2LmpwZw.jpg

六大设计原则

  • Single Responsibility Principle  : 单一职责原则

    类的设计尽量做到只有一个原因可以引起它的改变

  • Liskov Substitution Principle : 里氏替换原则

    只要父类出现的地方子类就可以出现,且替换成子类也不会出现任何错误或者异常

  • Dependence Inversion Principle :依赖倒置原则

    针对接口编程,而不是针对实现编程

  • Interface Segregation Principle : 接口隔离原则

    不要建立臃肿庞大的接口。即接口尽量细化,同时接口中的方法尽量少

  • Law of Demeter     : 迪米特法则

    一个对象应该对其他对象有最少的了解,也就是说一个类要对自己需要耦合或者调用的类知道的最少

  • Open Closed Principle : 开闭原则

    一个软件实体,比如类,模块,函数应该对扩展开放,对修改关闭


单例模式

定义:
Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
通用代码:(是线程安全的)

  1. public class Singleton {
  2. private static final Singleton *singleton = new Singleton();
  3. //限制产生多个对象
  4. private Singleton(){
  5. }
  6. //通过该方法获得实例对象
  7. public static Singleton *getSingleton(){
  8. return singleton;
  9. }
  10. //类中其他方法,尽量是static
  11. public static void doSomething(){
  12. }
  13. }


使用场景:**

  • 要求生成唯一序列号的环境;
  • 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;
  • 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
  • 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。


线程不安全实例:**

  1. public class Singleton {
  2. private static Singleton *singleton = null;
  3. //限制产生多个对象
  4. private Singleton(){
  5. }
  6. //通过该方法获得实例对象
  7. public static Singleton *getSingleton(){
  8. if(singleton == null){
  9. singleton = new Singleton();
  10. }
  11. return singleton;
  12. }
  13. }

解决办法:
在getSingleton方法前加synchronized关键字,也可以在getSingleton方法内增加synchronized来实现。最优的办法是如通用代码那样写**

工厂模式

定义:
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)
Product为抽象产品类负责定义产品的共性,实现对事物最抽象的定义;
Creator为抽象创建类,也就是抽象工厂,具体如何创建产品类是由具体的实现工厂ConcreteCreator完成的。

  1. public class ConcreteCreator extends Creator {
  2. public <T extends Product> T createProduct(Class<T> c){
  3. Product product=null;
  4. try {
  5. product = (Product)Class.forName(c.getName()).newInstance();
  6. } catch (Exception e) {
  7. //异常处理
  8. }
  9. return (T)product;
  10. }
  11. }

简单工厂模式:
一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法
多个工厂类:
每个人种(具体的产品类)都对应了一个创建者,每个创建者独立负责创建对应的产品对象,非常符合单一职责原则
代替单例模式:
单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象
延迟初始化:
ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留
使用场景:jdbc连接数据库,硬件访问,降低对象的产生和销毁

抽象工厂模式

定义:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类。)
抽象工厂类代码:

  1. public abstract class AbstractCreator {
  2. //创建A产品家族
  3. public abstract AbstractProductA createProductA();
  4. //创建B产品家族
  5. public abstract AbstractProductB createProductB();
  6. }

用场景:
一个对象族(或是一组没有任何关系的对象)都有相同的约束。
涉及不同操作系统的时候,都可以考虑使用抽象工厂模式

模板方法模式(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关键字,不允许被覆写。

具体模板:ConcreteClass1和ConcreteClass2属于具体模板,实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现
使用场景:

  • 多个子类有公有的方法,并且逻辑基本相同时。
  • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。
  • 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。

建造者模式(Builder Pattern)

定义:
Separate the construction of a complex object from its representation so that the same construction process can create different representations.(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。)

  • Product产品类

通常是实现了模板方法模式,也就是有模板方法和基本方法,例子中的BenzModel和BMWModel就属于产品类。

  • Builder抽象建造者

规范产品的组建,一般是由子类实现。例子中的CarBuilder就属于抽象建造者。

  • ConcreteBuilder具体建造者

实现抽象类定义的所有方法,并且返回一个组建好的对象。例子中的BenzBuilder和BMWBuilder就属于具体建造者。

  • Director导演类

负责安排已有模块的顺序,然后告诉Builder开始建造
使用场景:

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。


    建造者模式与工厂模式的不同:
    建造者模式最主要的功能是基本方法的调用顺序安排,这些基本方法已经实现了,顺序不同产生的对象也不同;
    工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的。

    代理模式(Proxy Pattern)

    定义:
    Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

  • Subject抽象主题角色

抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。

  • RealSubject具体主题角色

也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。

  • Proxy代理主题角色

也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

普通代理和强制代理:

普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;
强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。

普通代理:
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。

强制代理:
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。

动态代理:
根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”。
两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

动态代理调用过程示意图:
动态代理的意图:横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。
首要条件:被代理的类必须要实现一个接口。

原型模式(Prototype Pattern)

定义:
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)

原型模式通用代码:**

  1. public class PrototypeClass implements Cloneable{
  2. //覆写父类Object方法
  3. @Override
  4. public PrototypeClass clone(){
  5. PrototypeClass prototypeClass = null;
  6. try {
  7. prototypeClass = (PrototypeClass)super.clone();
  8. } catch (CloneNotSupportedException e) {
  9. //异常处理
  10. }
  11. return prototypeClass;
  12. }
  13. }

原型模式实际上就是实现Cloneable接口,重写clone()方法。

使用原型模式的优点:

  • 性能优良

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

  • 逃避构造函数的约束

这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。

使用场景:

  • 资源优化场景

类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。

  • 性能和安全要求的场景

通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

  • 一个对象多个修改者的场景

一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

浅拷贝和深拷贝: 浅拷贝:Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,其他的原始类型比如int、long、char、string(当做是原始类型)等都会被拷贝。 注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。 深拷贝:对私有的类变量进行独立的拷贝
如:thing.arrayList = (ArrayList)this.arrayList.clone();

中介者模式

定义:
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.(用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互。)

  • Mediator 抽象中介者角色

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

  • Concrete Mediator 具体中介者角色

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

  • Colleague 同事角色

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

通用抽象中介者代码:

  1. public abstract class Mediator {
  2. //定义同事类
  3. protected ConcreteColleague1 c1;
  4. protected ConcreteColleague2 c2;
  5. //通过getter/setter方法把同事类注入进来
  6. public ConcreteColleague1 getC1() {
  7. return c1;
  8. }
  9. public void setC1(ConcreteColleague1 c1) {
  10. this.c1 = c1;
  11. }
  12. public ConcreteColleague2 getC2() {
  13. return c2;
  14. }
  15. public void setC2(ConcreteColleague2 c2) {
  16. this.c2 = c2;
  17. }
  18. //中介者模式的业务逻辑
  19. public abstract void doSomething1();
  20. public abstract void doSomething2();
  21. }

ps:使用同事类注入而不使用抽象注入的原因是因为抽象类中不具有每个同事类必须要完成的方法。即每个同事类中的方法各不相同。

问:为什么同事类要使用构造函数注入中介者,而中介者使用getter/setter方式注入同事类呢?

这是因为同事类必须有中介者,而中介者却可以只有部分同事类。

使用场景:

中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出现了蜘蛛网状结构,即每个类都与其他的类有直接的联系。

命令模式

定义:
Encapsulate a request as an object,thereby letting you parameterize clients with different requests,queue or log requests,and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。)

  • Receive接收者角色

该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的例子中就是Group的三个实现类(需求组,美工组,代码组)。

  • Command命令角色

需要执行的所有命令都在这里声明。

  • Invoker调用者角色

接收到命令,并执行命令。在例子中,我(项目经理)就是这个角色。

使用场景:

认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击是一个命令,可以采用命令模式;模拟DOS命令的时候,当然也要采用命令模式;触发-反馈机制的处理等。

责任链模式

定义:
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 objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)

抽象处理者的代码:

  1. public abstract class Handler {
  2. private Handler nextHandler;
  3. //每个处理者都必须对请求做出处理
  4. public final Response handleMessage(Request request){
  5. Response response = null;
  6. //判断是否是自己的处理级别
  7. if(this.getHandlerLevel().equals(request.getRequestLevel())){
  8. response = this.echo(request);
  9. }else{ //不属于自己的处理级别
  10. //判断是否有下一个处理者
  11. if(this.nextHandler != null){
  12. response = this.nextHandler.handleMessage(request);
  13. }else{
  14. //没有适当的处理者,业务自行处理
  15. }
  16. }
  17. return response;
  18. }
  19. //设置下一个处理者是谁
  20. public void setNext(Handler _handler){
  21. this.nextHandler = _handler;
  22. }
  23. //每个处理者都有一个处理级别
  24. protected abstract Level getHandlerLevel();
  25. //每个处理者都必须实现处理任务
  26. protected abstract Response echo(Request request);
  27. }

抽象的处理者实现三个职责:

  • 一是定义一个请求的处理方法handleMessage,唯一对外开放的方法;
  • 二是定义一个链的编排方法setNext,设置下一个处理者;
  • 三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLevel和具体的处理任务echo。

    注意事项: 链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。

装饰模式(Decorator Pattern)

定义:
Attach additional responsibilities to an object dynamically keeping the same interface.Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。)

  • Component抽象构件

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

  • ConcreteComponent 具体构件

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

  • Decorator装饰角色

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

  • 具体装饰角色

ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西,上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。

使用场景:

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

策略模式(Strategy Pattern)

定义
Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)

  • Context封装角色

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

  • Strategy抽象策略角色

策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。各位看官可能要问了,类图中的AlgorithmInterface是什么意思,嘿嘿,algorithm是“运算法则”的意思,结合起来意思就明白了吧。

  • ConcreteStrategy具体策略角色(多个)

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

使用场景:

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

    注意事项:具体策略数量超过4个,则需要考虑使用混合模式


    策略模式扩展:策略枚举

    1. public enum Calculator {
    2. //加法运算
    3. ADD("+"){
    4. public int exec(int a,int b){
    5. return a+b;
    6. }
    7. },
    8. //减法运算
    9. SUB("-"){
    10. public int exec(int a,int b){
    11. return a - b;
    12. }
    13. };
    14. String value = "";
    15. //定义成员值类型
    16. private Calculator(String _value){
    17. this.value = _value;
    18. }
    19. //获得枚举成员的值
    20. public String getValue(){
    21. return this.value;
    22. }
    23. //声明一个抽象函数
    24. public abstract int exec(int a,int b);
    25. }

定义:

  • 它是一个枚举。
  • 它是一个浓缩了的策略模式的枚举。

注意:
受枚举类型的限制,每个枚举项都是public、final、static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。
致命缺陷:
所有的策略都需要暴露出去,由客户端决定使用哪一个策略。

适配器模式(Adapter Pattern)

定义:
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目标角色

该角色定义把其他类转换为何种接口,也就是我们的期望接口,例子中的IUserInfo接口就是目标角色。

  • Adaptee源角色

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

  • Adapter适配器角色

适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换为目标角色,怎么转换?通过继承或是类关联的方式。
使用场景
你有动机修改一个已经投产中的接口时,适配器模式可能是最适合你的模式。比如系统扩展了,需要使用一个已有或新建立的类,但这个类又不符合系统的接口,怎么办?使用适配器模式,这也是我们例子中提到的。
注意事项:
详细设计阶段不要考虑使用适配器模式,使用主要场景为扩展应用中。

对象适配器:

对象适配器和类适配器的区别:
类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系,这是两者的根本区别。(实际项目中对象适配器使用到的场景相对比较多)。

迭代器模式(Iterator Pattern)

定义:
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.(它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。)

  • Iterator抽象迭代器

抽象迭代器负责定义访问和遍历元素的接口,而且基本上是有固定的3个方法:first()获得第一个元素,next()访问下一个元素,isDone()是否已经访问到底部(Java叫做hasNext()方法)。

  • ConcreteIterator具体迭代器

具体迭代器角色要实现迭代器接口,完成容器元素的遍历。

  • Aggregate抽象容器

容器角色负责提供创建具体迭代器角色的接口,必然提供一个类似createIterator()这样的方法,在Java中一般是iterator()方法。

  • Concrete Aggregate具体容器

具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。

ps:迭代器模式已经被淘汰,java中已经把迭代器运用到各个聚集类(collection)中了,使用java自带的迭代器就已经满足我们的需求了。