设计模式_设计模式设计模式 解释器 - CSDN
设计模式 订阅
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 展开全文
软件设计模式(Design pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
信息
特    点
每个模式都有各自的名字
别    名
设计模式
分    类
基础模式,委托模式,接口模式等
中文名
软件设计模式
外文名
Design pattern
软件设计模式简介
设计模式(英语 design pattern)是对面向对象设计中反复出现的问题的解决方案。这个术语是在1990年代由Erich Gamma等人从建筑设计领域引入到计算机科学中来的。这个术语的含义还存有争议。算法不是设计模式,因为算法致力于解决问题而非设计问题。设计模式通常描述了一组相互紧密作用的类与对象。设计模式提供一种讨论软件设计的公共语言,使得熟练设计者的设计经验可以被初学者和其他设计者掌握。设计模式还为软件重构提供了目标。随着软件开发社群对设计模式的兴趣日益增长,已经出版了一些相关的专著,定期召开相应的研讨会,而且Ward Cunningham为此发明了WikiWiki用来交流设计模式的经验。
收起全文
  • 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、无数工程师实践的代码设计经验的总 结,它是面向对象思想的高度提炼和模板化。使用设计模式将会让代码具有更高的可重用性、更好的灵 活性和可拓展性、更...
  • 设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式 结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 ...

    设计模式分为三大类:

    创建型模式,共五种:工厂方法模式抽象工厂模式单例模式建造者模式原型模式

    结构型模式,共七种:适配器模式装饰者模式代理模式外观模式桥接模式组合模式享元模式

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

    其实还有两类:并发型模式和线程池模式。

     

    设计模式的六大原则:

    总原则-开闭原则

    对扩展开放,对修改封闭。在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。

    想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。

     

    1、单一职责原则

    不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,否则就应该把类拆分。

     

    2、里氏替换原则(Liskov Substitution Principle)

    任何基类可以出现的地方,子类一定可以出现。里氏替换原则是继承复用的基石,只有当衍生类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

    里氏代换原则是对“开-闭”原则的补充。实现“开闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。里氏替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。

     

    3、依赖倒转原则(Dependence Inversion Principle)

    面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

     

    4、接口隔离原则(Interface Segregation Principle)

    每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

     

    5、迪米特法则(最少知道原则)(Demeter Principle)

    一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

    最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。

     

    6、合成复用原则(Composite Reuse Principle)

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

     

    之前已经陆续整理了9种设计模式,链接如下,接下来一段时间陆续把剩余的过一遍,整理出来,理解设计模式还是很重要的。

     

    创建型模式:工厂方法模式抽象工厂模式单例模式建造者模式原型模式

    结构型模式:适配器模式装饰者模式代理模式外观模式桥接模式组合模式享元模式

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

    还有两类:并发型模式和线程池模式。

     

     

    -------2017年8月31日更新----------------

    设计模式需要几个阶段的学习,

    没有大量项目经验的时候学习,可能只是了解,

    当有了一些项目场景的时候,才会深刻体会到其中的奥妙。

     

    上面文章有些在写的时候,“借鉴”甚至“抄袭”了很多其他博主的文章,主要也是当时自己理解的不够深刻,需要借助现有的场景去理解,接下来会抽时间梳理一下设计模式的东西,整理一遍,希望能帮到大家。

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

     

    展开全文
  • 本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,...
  • 本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,...
  • Java中常用的设计模式

    2019-03-15 17:30:53
    一、什么是设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、... 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块...

    文章转载借鉴:http://blog.csdn.net/zhangerqing

    一、什么是设计模式

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因

    二、设计模式的六大原则

    1、开闭原则(Open Close Principle)

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

    2、里氏代换原则(Liskov Substitution Principle)

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

    3、依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

    4、接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

    5、迪米特法则(最少知道原则)(Demeter Principle)

    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

    6、合成复用原则(Composite Reuse Principle)

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

    设计模式原则详细描述请见:http://www.cnblogs.com/pony1223/p/7594803.html

    三、设计模式的三大类

    总体来说设计模式分为三大类:

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

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

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

     其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:

    根据作用范围来分

    根据模式是主要用于类上还是主要用于对象上来分,这种方式可分为类模式和对象模式两种。

    1. 类模式:用于处理类与子类之间的关系,这些关系通过继承来建立,是静态的,在编译时刻便确定下来了。工厂方法、(类)适配器、模板方法、解释器属于该模式。
    2. 对象模式:用于处理对象之间的关系,这些关系可以通过组合或聚合来实现,在运行时刻是可以变化的,更具动态性。
    范围\目的 创建型模式 结构型模式 行为型模式
    类模式 工厂方法 (类)适配器 模板方法、解释器
    对象模式 单例
    原型
    抽象工厂
    建造者
    代理
    (对象)适配器
    桥接
    装饰
    外观
    享元
    组合
    策略
    命令
    职责链
    状态
    观察者
    中介者
    迭代器
    访问者
    备忘录

    四、Java的二十三种设计模式

    从这一块开始,我们详细介绍Java中23种设计模式的概念,应用场景等情况,并结合他们的特点及设计模式的原则进行分析。

    创建型模式(5种):用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。

    A、单例模式(Singleton)

    单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。

    这样的模式有几个好处:

    1. 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
    2. 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
    3. 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

    优点:只有一个实例,节约了内存资源,提高了系统性能

    缺点:
        没有抽象层,不能扩展
        职责过重,违背了单一性原则

    首先我们写一个简单的单例类:

    public class Singleton {
     
    	/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
    	private static Singleton instance = null;
     
    	/* 私有构造方法,防止被实例化 */
    	private Singleton() {
    	}
     
    	/* 静态工程方法,创建实例 */
    	public static Singleton getInstance() {
    		if (instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
     
    	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    	public Object readResolve() {
    		return instance;
    	}
    }

    这个类可以满足基本要求,但是,像这样毫无线程安全保护的类,如果我们把它放入多线程的环境下,肯定就会出现问题了,如何解决?我们首先会想到对getInstance方法加synchronized关键字,如下:

    public static synchronized Singleton getInstance() {
    		if (instance == null) {
    			instance = new Singleton();
    		}
    		return instance;
    	}

    但是,synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁,事实上,只有在第一次创建对象的时候需要加锁,之后就不需要了,所以,这个地方需要改进。我们改成下面这个:

    public static Singleton getInstance() {
    		if (instance == null) {
    			synchronized (instance) {
    				if (instance == null) {
    					instance = new Singleton();
    				}
    			}
    		}
    		return instance;
    	}

    似乎解决了之前提到的问题,将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:

    ①:A、B线程同时进入了第一个if判断

    ②:A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();

    ③:由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。

    ④:B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。

    ⑥:此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。

    所以程序还是有可能发生错误,其实程序在运行过程是很复杂的,从这点我们就可以看出,尤其是在写多线程环境下的程序更有难度,有挑战性。我们对该程序做进一步优化:

    private static class SingletonFactory{         
            private static Singleton instance = new Singleton();         
        }         
    public static Singleton getInstance(){         
            return SingletonFactory.instance;         
        } 

     实际情况是,单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。这样我们暂时总结一个完美的单例模式:

    public class Singleton {
     
    	/* 私有构造方法,防止被实例化 */
    	private Singleton() {
    	}
     
    	/* 此处使用一个内部类来维护单例 */
    	private static class SingletonFactory {
    		private static Singleton instance = new Singleton();
    	}
     
    	/* 获取实例 */
    	public static Singleton getInstance() {
    		return SingletonFactory.instance;
    	}
     
    	/* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */
    	public Object readResolve() {
    		return getInstance();
    	}
    }

    其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。所以说,十分完美的东西是没有的,我们只能根据实际情况,选择最适合自己应用场景的实现方法。也有人这样实现:因为我们只需要在创建类的时候进行同步,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字,也是可以的:

    public class SingletonTest {
     
    	private static SingletonTest instance = null;
     
    	private SingletonTest() {
    	}
     
    	private static synchronized void syncInit() {
    		if (instance == null) {
    			instance = new SingletonTest();
    		}
    	}
     
    	public static SingletonTest getInstance() {
    		if (instance == null) {
    			syncInit();
    		}
    		return instance;
    	}
    }

    考虑性能的话,整个程序只需创建一次实例,所以性能也不会有什么影响。

    public class SingletonTest {
     
    	private static SingletonTest instance = null;
    	private Vector properties = null;
     
    	public Vector getProperties() {
    		return properties;
    	}
     
    	private SingletonTest() {
    	}
     
    	private static synchronized void syncInit() {
    		if (instance == null) {
    			instance = new SingletonTest();
    		}
    	}
     
    	public static SingletonTest getInstance() {
    		if (instance == null) {
    			syncInit();
    		}
    		return instance;
    	}
     
    	public void updateProperties() {
    		SingletonTest shadow = new SingletonTest();
    		properties = shadow.getProperties();
    	}
    }

     通过单例模式的学习告诉我们:

    1. 单例模式理解起来简单,但是具体实现起来还是有一定的难度。
    2. synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。

    到这儿,单例模式基本已经讲完了,结尾处,笔者突然想到另一个问题,就是采用类的静态方法,实现单例模式的效果,也是可行的,此处二者有什么不同?

    首先,静态类不能实现接口。(从类的角度说是可以的,但是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使实现了也是非静态的)

    其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升性能。

    再次,单例类可以被继承,他的方法可以被覆写。但是静态类内部方法都是static,无法被覆写。

    最后一点,单例类比较灵活,毕竟从实现上只是一个普通的Java类,只要满足单例的基本需求,你可以在里面随心所欲的实现一些其它功能,但是静态类不行。从上面这些概括中,基本可以看出二者的区别,但是,从另一方面讲,我们上面最后实现的那个单例模式,内部就是用一个静态类来实现的,所以,二者有很大的关联,只是我们考虑问题的层面不同罢了。两种思想的结合,才能造就出完美的解决方案,就像HashMap采用数组+链表来实现一样,其实生活中很多事情都是这样,单用不同的方法来处理问题,总是有优点也有缺点,最完美的方法是,结合各个方法的优点,才能最好的解决问题!

    拓展:多例设计模式

    单例设计模式只留下一个类的一个实例化对象,而多例设计模式,会定义出多个对象。例如:定义一个表示星期的操作类,这个类的对象只能有7个实例化对象(星期一 ~ 星期日);定义一个表示性别的类,只能有2个实例化对象(男、女);定义一个表示颜色的操作类,只能有3个实例化对象(红、绿、蓝)。这种情况下,这样的类就不应该由用户无限制地去创造实例化对象,应该只使用有限的几个,这个就属于多例设计模式。不管是单例设计模式还是多例设计模式,有一个核心不可动摇,即构造器方法私有化。

    class Sex{
        private String title;
        private static final Sex MALE = new Sex("男");
        private static final Sex FEMALE = new Sex("女");
    
        private Sex(String title){        //构造器私有化
            this.title = title;
        }
    
        public String toString(){
            return this.title;
        }
        
        public static Sex getInstance(int ch){
            switch(ch){
                case 1:
                    return MALE;
                case 2:
                    return FEMALE;
                default:
                    return null;
            }
        }
    }
    
    public class TestDemo{
        public static void main(String args[]){
            Sex sex = Sex.getInstance(2);
            System.out.println(sex);
        }
    }
    
    
    ==========程序执行结果=========
    女

    B、工厂方法模式(Factory Method)

    工厂方法模式分为三种:

    1、普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:

    举例如下:(我们举一个发送邮件和短信的例子)

    首先,创建二者的共同接口:

    public interface Sender {
    	public void Send();
    }

    其次,创建实现类:

    public class MailSender implements Sender {
    	@Override
    	public void Send() {
    		System.out.println("this is mailsender!");
    	}
    }
    public class SmsSender implements Sender {
        @Override
        public void Send() {
            System.out.println("this is sms sender!");
        }
    }

    最后,建工厂类:

    public class SendFactory {
     
    	public Sender produce(String type) {
    		if ("mail".equals(type)) {
    			return new MailSender();
    		} else if ("sms".equals(type)) {
    			return new SmsSender();
    		} else {
    			System.out.println("请输入正确的类型!");
    			return null;
    		}
    	}
    }

    我们来测试下:

    public class FactoryTest {
     
    	public static void main(String[] args) {
    		SendFactory factory = new SendFactory();
    		Sender sender = factory.produce("sms");
    		sender.Send();
    	}
    }

    输出:this is sms sender!

    2、多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。关系图:

    将上面的代码做下修改,改动下SendFactory类就行,如下:

    public class SendFactory {
    	
    	public Sender produceMail(){
    		return new MailSender();
    	}
    	
    	public Sender produceSms(){
    		return new SmsSender();
    	}
    }

    测试类如下:

    public class FactoryTest {
     
    	public static void main(String[] args) {
    		SendFactory factory = new SendFactory();
    		Sender sender = factory.produceMail();
    		sender.Send();
    	}
    }

    输出:this is mailsender!

    3、静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

    public class SendFactory {
    	
    	public static Sender produceMail(){
    		return new MailSender();
    	}
    	
    	public static Sender produceSms(){
    		return new SmsSender();
    	}
    }
    public class FactoryTest {
     
    	public static void main(String[] args) {	
    		Sender sender = SendFactory.produceMail();
    		sender.Send();
    	}
    }

    输出:this is mailsender!

    总体来说,工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第三种相对于第二种,不需要实例化工厂类,所以,大多数情况下,我们会选用第三种——静态工厂方法模式。

    好处:客户端不需要创建对象,明确了各个类的职责
    缺点:该工厂类负责创建所有实例,如果有新的类加入,需要不断的修改工厂类,不利于后期的维护

    C、抽象工厂模式

    工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。因为抽象工厂不太好理解,我们先看看图,然后就和代码,就比较容易理解。

    请看例子:

    public interface Sender {
    	public void Send();
    }

    两个实现类:

    public class MailSender implements Sender {
    	@Override
    	public void Send() {
    		System.out.println("this is mailsender!");
    	}
    }
    public class MailSender implements Sender {
    	@Override
    	public void Send() {
    		System.out.println("this is mailsender!");
    	}
    }

    在提供一个接口:

    public interface Provider {
    	public Sender produce();
    }

    两个工厂类:

        public class SendMailFactory implements Provider {
        	
        	@Override
        	public Sender produce(){
        		return new MailSender();
        	}
        }
    public class SendSmsFactory implements Provider{
     
    	@Override
    	public Sender produce() {
    		return new SmsSender();
    	}
    }

    测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Provider provider = new SendMailFactory();
    		Sender sender = provider.produce();
    		sender.Send();
    	}
    }

    其实这个模式的好处就是,如果你现在想增加一个功能:发及时信息,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!

    好处:如果有新的类进来,只需要添加一个对应的具体工厂类,不影响现有代码,增加了程序的扩展性
    缺点:增加了代码量

    D、建造者模式(Builder)

    工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。我们看一下代码:

    还和前面一样,一个Sender接口,两个实现类MailSender和SmsSender。最后,建造者类如下:

    public class Builder {
    	
    	private List<Sender> list = new ArrayList<Sender>();
    	
    	public void produceMailSender(int count){
    		for(int i=0; i<count; i++){
    			list.add(new MailSender());
    		}
    	}
    	
    	public void produceSmsSender(int count){
    		for(int i=0; i<count; i++){
    			list.add(new SmsSender());
    		}
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Builder builder = new Builder();
    		builder.produceMailSender(10);
    	}
    }

    从这点看出,建造者模式将很多功能集成到一个类里,这个类可以创造出比较复杂的东西。所以与工程模式的区别就是:工厂模式关注的是创建单个产品,而建造者模式则关注创建符合对象,多个部分。因此,是选择工厂模式还是建造者模式,依实际情况而定。

    E、原型模式(Prototype)

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。本小结会通过对象的复制,进行讲解。在Java中,复制对象是通过clone()实现的,先创建一个原型类:

    public class Prototype implements Cloneable {
     
    	public Object clone() throws CloneNotSupportedException {
    		Prototype proto = (Prototype) super.clone();
    		return proto;
    	}
    }

    很简单,一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的,具体怎么实现,我会在另一篇文章中,关于解读Java中本地方法的调用,此处不再深究。在这儿,我将结合对象的浅复制和深复制来说一下,首先需要了解对象深、浅复制的概念:

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

    深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

    此处,写一个深浅复制的例子:

    public class Prototype implements Cloneable, Serializable {
     
    	private static final long serialVersionUID = 1L;
    	private String string;
     
    	private SerializableObject obj;
     
    	/* 浅复制 */
    	public Object clone() throws CloneNotSupportedException {
    		Prototype proto = (Prototype) super.clone();
    		return proto;
    	}
     
    	/* 深复制 */
    	public Object deepClone() throws IOException, ClassNotFoundException {
     
    		/* 写入当前对象的二进制流 */
    		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();
    	}
     
    	public String getString() {
    		return string;
    	}
     
    	public void setString(String string) {
    		this.string = string;
    	}
     
    	public SerializableObject getObj() {
    		return obj;
    	}
     
    	public void setObj(SerializableObject obj) {
    		this.obj = obj;
    	}
     
    }
     
    class SerializableObject implements Serializable {
    	private static final long serialVersionUID = 1L;
    }

    要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

    结构型模式(7种):用于描述如何将类或对象按某种布局组成更多的结构。

    F、适配器模式(Adapter)

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

    核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口时Targetable,通过Adapter类,将Source的功能扩展到Targetable里,看代码:

    public class Source {
     
    	public void method1() {
    		System.out.println("this is original method!");
    	}
    }
    public interface Targetable {
     
    	/* 与原类中的方法相同 */
    	public void method1();
     
    	/* 新类的方法 */
    	public void method2();
    }
    public class Adapter extends Source implements Targetable {
     
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
    }
    public class Adapter extends Source implements Targetable {
     
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
    }

    Adapter类继承Source类,实现Targetable接口,下面是测试类:

    public class AdapterTest {
     
    	public static void main(String[] args) {
    		Targetable target = new Adapter();
    		target.method1();
    		target.method2();
    	}
    }

    输出:

    this is original method!
    this is the targetable method!

    这样Targetable接口的实现类就具有了Source类的功能。

    对象的适配器模式

    基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。看图:

    只需要修改Adapter类的源码即可:

    public class Wrapper implements Targetable {
     
    	private Source source;
    	
    	public Wrapper(Source source){
    		super();
    		this.source = source;
    	}
    	@Override
    	public void method2() {
    		System.out.println("this is the targetable method!");
    	}
     
    	@Override
    	public void method1() {
    		source.method1();
    	}
    }

    测试类:

    public class AdapterTest {
     
    	public static void main(String[] args) {
    		Source source = new Source();
    		Targetable target = new Wrapper(source);
    		target.method1();
    		target.method2();
    	}
    }

    输出与第一种一样,只是适配的方法不同而已。

    第三种适配器模式是接口的适配器模式,接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。看一下类图:
     

    这个很好理解,在实际开发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些实现类中并不是都需要。看代码:

    public interface Sourceable {
    	
    	public void method1();
    	public void method2();
    }

    抽象类Wrapper2:

    public abstract class Wrapper2 implements Sourceable{
    	
    	public void method1(){}
    	public void method2(){}
    }
    public class SourceSub1 extends Wrapper2 {
    	public void method1(){
    		System.out.println("the sourceable interface's first Sub1!");
    	}
    }
    public class SourceSub2 extends Wrapper2 {
    	public void method2(){
    		System.out.println("the sourceable interface's second Sub2!");
    	}
    }
    public class WrapperTest {
     
    	public static void main(String[] args) {
    		Sourceable source1 = new SourceSub1();
    		Sourceable source2 = new SourceSub2();
    		
    		source1.method1();
    		source1.method2();
    		source2.method1();
    		source2.method2();
    	}
    }

    测试输出:

    the sourceable interface's first Sub1!
    the sourceable interface's second Sub2!

    达到了我们的效果!

     讲了这么多,总结一下三种适配器模式的应用场景:

    类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

    对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

    接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

    G、装饰模式

    顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口。装饰对象持有被装饰对象的实例,关系图如下:

    Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:

    public interface Sourceable {
    	public void method();
    }
    public class Source implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("the original method!");
    	}
    }
    public class Decorator implements Sourceable {
     
    	private Sourceable source;
    	
    	public Decorator(Sourceable source){
    		super();
    		this.source = source;
    	}
    	@Override
    	public void method() {
    		System.out.println("before decorator!");
    		source.method();
    		System.out.println("after decorator!");
    	}
    }

    测试类:

    public class DecoratorTest {
     
    	public static void main(String[] args) {
    		Sourceable source = new Source();
    		Sourceable obj = new Decorator(source);
    		obj.method();
    	}
    }

    输出:

    before decorator!
    the original method!
    after decorator!

    装饰器模式的应用场景:

    1. 需要扩展一个类的功能。
    2. 动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

    缺点:产生过多相似的对象,不易排错!

    H、代理者模式(Proxy)

    代理(Proxy)模式:为某对象提供一种代理以

    控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。比如我们在租房子的时候会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。先来看看关系图:

    根据上文的阐述,代理模式就比较容易的理解了,我们看下代码:

    public interface Sourceable {
    	public void method();
    }
    public class Source implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("the original method!");
    	}
    }
    public class Proxy implements Sourceable {
     
    	private Source source;
    	public Proxy(){
    		super();
    		this.source = new Source();
    	}
    	@Override
    	public void method() {
    		before();
    		source.method();
    		atfer();
    	}
    	private void atfer() {
    		System.out.println("after proxy!");
    	}
    	private void before() {
    		System.out.println("before proxy!");
    	}
    }

    测试类:

    public class ProxyTest {
     
    	public static void main(String[] args) {
    		Sourceable source = new Proxy();
    		source.method();
    	}
     
    }

    输出:

    before proxy!
    the original method!
    after proxy!

    代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

    1. 修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
    2. 就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

    使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

    I、外观模式(Facade)

    外观模式是为了解决类与类之家的依赖关系的,像spring一样,可以将类和类之间的关系配置到配置文件中,而外观模式就是将他们的关系放在一个Facade类中,降低了类类之间的耦合度,该模式中没有涉及到接口,看下类图:(我们以一个计算机的启动过程为例)

    我们先看下实现类:

    public class CPU {
    	
    	public void startup(){
    		System.out.println("cpu startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("cpu shutdown!");
    	}
    }
    public class Memory {
    	
    	public void startup(){
    		System.out.println("memory startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("memory shutdown!");
    	}
    }
    public class Disk {
    	
    	public void startup(){
    		System.out.println("disk startup!");
    	}
    	
    	public void shutdown(){
    		System.out.println("disk shutdown!");
    	}
    }
    public class Computer {
    	private CPU cpu;
    	private Memory memory;
    	private Disk disk;
    	
    	public Computer(){
    		cpu = new CPU();
    		memory = new Memory();
    		disk = new Disk();
    	}
    	
    	public void startup(){
    		System.out.println("start the computer!");
    		cpu.startup();
    		memory.startup();
    		disk.startup();
    		System.out.println("start computer finished!");
    	}
    	
    	public void shutdown(){
    		System.out.println("begin to close the computer!");
    		cpu.shutdown();
    		memory.shutdown();
    		disk.shutdown();
    		System.out.println("computer closed!");
    	}
    }

    User类如下:

    public class User {
     
    	public static void main(String[] args) {
    		Computer computer = new Computer();
    		computer.startup();
    		computer.shutdown();
    	}
    }

    输出:

    start the computer!
    cpu startup!
    memory startup!
    disk startup!
    start computer finished!
    begin to close the computer!
    cpu shutdown!
    memory shutdown!
    disk shutdown!
    computer closed!

    如果我们没有Computer类,那么,CPU、Memory、Disk他们之间将会相互持有实例,产生关系,这样会造成严重的依赖,修改一个类,可能会带来其他类的修改,这不是我们想要看到的,有了Computer类,他们之间的关系被放在了Computer类里,这样就起到了解耦的作用,这就是外观模式!

    J、桥接模式

    桥接模式就是把事物和其具体实现分开,使他们可以各自独立的变化。桥接的用意是:将抽象化与实现化解耦,使得二者可以独立变化,它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不用动,原因就是JDBC提供统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:

    实现代码:

    先定义接口:

    public interface Sourceable {
    	public void method();
    }

    分别定义两个实现类:

    public class SourceSub1 implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("this is the first sub!");
    	}
    }
    public class SourceSub2 implements Sourceable {
     
    	@Override
    	public void method() {
    		System.out.println("this is the second sub!");
    	}
    }

     定义一个桥,持有Sourceable的一个实例:

    public abstract class Bridge {
    	private Sourceable source;
     
    	public void method(){
    		source.method();
    	}
    	
    	public Sourceable getSource() {
    		return source;
    	}
     
    	public void setSource(Sourceable source) {
    		this.source = source;
    	}
    }
    public class MyBridge extends Bridge {
    	public void method(){
    		getSource().method();
    	}
    }

     测试类:

    public class BridgeTest {
    	
    	public static void main(String[] args) {
    		
    		Bridge bridge = new MyBridge();
    		
    		/*调用第一个对象*/
    		Sourceable source1 = new SourceSub1();
    		bridge.setSource(source1);
    		bridge.method();
    		
    		/*调用第二个对象*/
    		Sourceable source2 = new SourceSub2();
    		bridge.setSource(source2);
    		bridge.method();
    	}
    }

    output:

    this is the first sub!
    this is the second sub!

    这样,就通过对Bridge类的调用,实现了对接口Sourceable的实现类SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接的原理,有数据库学习基础的,一结合就都懂了。
     

    K、组合模式(Composite)

    组合模式有时又叫部分-整体模式在处理类似树形结构的问题时比较方便,看看关系图:

     直接来看代码:

    public class TreeNode {
    	
    	private String name;
    	private TreeNode parent;
    	private Vector<TreeNode> children = new Vector<TreeNode>();
    	
    	public TreeNode(String name){
    		this.name = name;
    	}
     
    	public String getName() {
    		return name;
    	}
     
    	public void setName(String name) {
    		this.name = name;
    	}
     
    	public TreeNode getParent() {
    		return parent;
    	}
     
    	public void setParent(TreeNode parent) {
    		this.parent = parent;
    	}
    	
    	//添加孩子节点
    	public void add(TreeNode node){
    		children.add(node);
    	}
    	
    	//删除孩子节点
    	public void remove(TreeNode node){
    		children.remove(node);
    	}
    	
    	//取得孩子节点
    	public Enumeration<TreeNode> getChildren(){
    		return children.elements();
    	}
    }
    public class Tree {
     
    	TreeNode root = null;
     
    	public Tree(String name) {
    		root = new TreeNode(name);
    	}
     
    	public static void main(String[] args) {
    		Tree tree = new Tree("A");
    		TreeNode nodeB = new TreeNode("B");
    		TreeNode nodeC = new TreeNode("C");
    		
    		nodeB.add(nodeC);
    		tree.root.add(nodeB);
    		System.out.println("build the tree finished!");
    	}
    }

    使用场景:将多个对象组合在一起进行操作,常用于表示树形结构中,例如二叉树,数等。

    L、享元模式(Flyweight)

    享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

    FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

    看个例子:

    看下数据库连接池的代码:

    public class ConnectionPool {
    	
    	private Vector<Connection> pool;
    	
    	/*公有属性*/
    	private String url = "jdbc:mysql://localhost:3306/test";
    	private String username = "root";
    	private String password = "root";
    	private String driverClassName = "com.mysql.jdbc.Driver";
     
    	private int poolSize = 100;
    	private static ConnectionPool instance = null;
    	Connection conn = null;
     
    	/*构造方法,做一些初始化工作*/
    	private ConnectionPool() {
    		pool = new Vector<Connection>(poolSize);
     
    		for (int i = 0; i < poolSize; i++) {
    			try {
    				Class.forName(driverClassName);
    				conn = DriverManager.getConnection(url, username, password);
    				pool.add(conn);
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    	}
     
    	/* 返回连接到连接池 */
    	public synchronized void release() {
    		pool.add(conn);
    	}
     
    	/* 返回连接池中的一个数据库连接 */
    	public synchronized Connection getConnection() {
    		if (pool.size() > 0) {
    			Connection conn = pool.get(0);
    			pool.remove(conn);
    			return conn;
    		} else {
    			return null;
    		}
    	}
    }

    通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能! 

    行为型模式(11种):用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。

    M、策略模式(strategy)

    策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数,关系图如下:

     图中ICalculator提供统一的方法,AbstractCalculator是辅助类,提供辅助方法,接下来,依次实现下每个类:

    首先统一接口:

    public interface ICalculator {
    	public int calculate(String exp);
    }

     辅助类:

    public abstract class AbstractCalculator {
    	
    	public int[] split(String exp,String opt){
    		String array[] = exp.split(opt);
    		int arrayInt[] = new int[2];
    		arrayInt[0] = Integer.parseInt(array[0]);
    		arrayInt[1] = Integer.parseInt(array[1]);
    		return arrayInt;
    	}
    }

     三个实现类:

    public class Plus extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"\\+");
    		return arrayInt[0]+arrayInt[1];
    	}
    }
    public class Minus extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"-");
    		return arrayInt[0]-arrayInt[1];
    	}
     
    }
    public class Multiply extends AbstractCalculator implements ICalculator {
     
    	@Override
    	public int calculate(String exp) {
    		int arrayInt[] = split(exp,"\\*");
    		return arrayInt[0]*arrayInt[1];
    	}
    }

    简单的测试类: 

    public class StrategyTest {
     
    	public static void main(String[] args) {
    		String exp = "2+8";
    		ICalculator cal = new Plus();
    		int result = cal.calculate(exp);
    		System.out.println(result);
    	}
    }

    输出:10

    策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

    N、模板方法模式(Template Method)

    模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。即:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:

     就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用,看下面的例子:

    public abstract class AbstractCalculator {
    	
    	/*主方法,实现对本类其它方法的调用*/
    	public final int calculate(String exp,String opt){
    		int array[] = split(exp,opt);
    		return calculate(array[0],array[1]);
    	}
    	
    	/*被子类重写的方法*/
    	abstract public int calculate(int num1,int num2);
    	
    	public int[] split(String exp,String opt){
    		String array[] = exp.split(opt);
    		int arrayInt[] = new int[2];
    		arrayInt[0] = Integer.parseInt(array[0]);
    		arrayInt[1] = Integer.parseInt(array[1]);
    		return arrayInt;
    	}
    }
    public class Plus extends AbstractCalculator {
     
    	@Override
    	public int calculate(int num1,int num2) {
    		return num1 + num2;
    	}
    }

     测试类:

    public class StrategyTest {
     
    	public static void main(String[] args) {
    		String exp = "8+8";
    		AbstractCalculator cal = new Plus();
    		int result = cal.calculate(exp, "\\+");
    		System.out.println(result);
    	}
    }

    我跟踪下这个小程序的执行过程:首先将exp和"\\+"做参数,调用AbstractCalculator类里的calculate(String,String)方法,在calculate(String,String)里调用同类的split(),之后再调用calculate(int ,int)方法,从这个方法进入到子类中,执行完return num1 + num2后,将值返回到AbstractCalculator类,赋给result,打印出来。正好验证了我们开头的思路。

    O、观察者模式(Observer)

    观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,会把这种改变通知给其他多个对象,从而影响其他对象的行为。先来看看关系图:

    我解释下这些类的作用:MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。我们看实现代码:

    一个Observer接口:

    public interface Observer {
    	public void update();
    }

     两个实现类:

    public class Observer1 implements Observer {
     
    	@Override
    	public void update() {
    		System.out.println("observer1 has received!");
    	}
    }
    public class Observer2 implements Observer {
     
    	@Override
    	public void update() {
    		System.out.println("observer2 has received!");
    	}
    }

     Subject接口及实现类:

    public interface Subject {
    	
    	/*增加观察者*/
    	public void add(Observer observer);
    	
    	/*删除观察者*/
    	public void del(Observer observer);
    	
    	/*通知所有的观察者*/
    	public void notifyObservers();
    	
    	/*自身的操作*/
    	public void operation();
    }
    public abstract class AbstractSubject implements Subject {
     
    	private Vector<Observer> vector = new Vector<Observer>();
    	@Override
    	public void add(Observer observer) {
    		vector.add(observer);
    	}
     
    	@Override
    	public void del(Observer observer) {
    		vector.remove(observer);
    	}
     
    	@Override
    	public void notifyObservers() {
    		Enumeration<Observer> enumo = vector.elements();
    		while(enumo.hasMoreElements()){
    			enumo.nextElement().update();
    		}
    	}
    }
    public class MySubject extends AbstractSubject {
     
    	@Override
    	public void operation() {
    		System.out.println("update self!");
    		notifyObservers();
    	}
     
    }
    public class MySubject extends AbstractSubject {
     
    	@Override
    	public void operation() {
    		System.out.println("update self!");
    		notifyObservers();
    	}
     
    }

     测试类:

    public class ObserverTest {
     
    	public static void main(String[] args) {
    		Subject sub = new MySubject();
    		sub.add(new Observer1());
    		sub.add(new Observer2());
    		
    		sub.operation();
    	}
     
    }

    输出:

    update self!
    observer1 has received!
    observer2 has received!

    P、迭代子模式(Iterator)

    迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图:

    这个思路和我们常用的一模一样,MyCollection中定义了集合的一些操作,MyIterator中定义了一系列迭代操作,且持有Collection实例,我们来看看实现代码:

    两个接口:

    public interface Collection {
    	
    	public Iterator iterator();
    	
    	/*取得集合元素*/
    	public Object get(int i);
    	
    	/*取得集合大小*/
    	public int size();
    }
    public interface Iterator {
    	//前移
    	public Object previous();
    	
    	//后移
    	public Object next();
    	public boolean hasNext();
    	
    	//取得第一个元素
    	public Object first();
    }

     两个实现:

    public class MyIterator implements Iterator {
     
    	private Collection collection;
    	private int pos = -1;
    	
    	public MyIterator(Collection collection){
    		this.collection = collection;
    	}
    	
    	@Override
    	public Object previous() {
    		if(pos > 0){
    			pos--;
    		}
    		return collection.get(pos);
    	}
     
    	@Override
    	public Object next() {
    		if(pos<collection.size()-1){
    			pos++;
    		}
    		return collection.get(pos);
    	}
     
    	@Override
    	public boolean hasNext() {
    		if(pos<collection.size()-1){
    			return true;
    		}else{
    			return false;
    		}
    	}
     
    	@Override
    	public Object first() {
    		pos = 0;
    		return collection.get(pos);
    	}
     
    }
    public class MyCollection implements Collection {
     
    	public String string[] = {"A","B","C","D","E"};
    	@Override
    	public Iterator iterator() {
    		return new MyIterator(this);
    	}
     
    	@Override
    	public Object get(int i) {
    		return string[i];
    	}
     
    	@Override
    	public int size() {
    		return string.length;
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Collection collection = new MyCollection();
    		Iterator it = collection.iterator();
    		
    		while(it.hasNext()){
    			System.out.println(it.next());
    		}
    	}
    }

    输出:A B C D E

    此处我们貌似模拟了一个集合类的过程,感觉是不是很爽?其实JDK中各个类也都是这些基本的东西,加一些设计模式,再加一些优化放到一起的,只要我们把这些东西学会了,掌握好了,我们也可以写出自己的集合类,甚至框架!

    Q、责任链模式(Chain of Responsibility)

    职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图:

    Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核心,实例化后生成一系列相互持有的对象,构成一条链。

    public interface Handler {
    	public void operator();
    }
    public abstract class AbstractHandler {
    	
    	private Handler handler;
     
    	public Handler getHandler() {
    		return handler;
    	}
     
    	public void setHandler(Handler handler) {
    		this.handler = handler;
    	}
    	
    }
    public class MyHandler extends AbstractHandler implements Handler {
     
    	private String name;
     
    	public MyHandler(String name) {
    		this.name = name;
    	}
     
    	@Override
    	public void operator() {
    		System.out.println(name+"deal!");
    		if(getHandler()!=null){
    			getHandler().operator();
    		}
    	}
    }
    public class Test {
     
    	public static void main(String[] args) {
    		MyHandler h1 = new MyHandler("h1");
    		MyHandler h2 = new MyHandler("h2");
    		MyHandler h3 = new MyHandler("h3");
     
    		h1.setHandler(h2);
    		h2.setHandler(h3);
     
    		h1.operator();
    	}
    }

    输出:

    h1deal!
    h2deal!
    h3deal!

    此处强调一点就是,链接上的请求可以是一条链,可以是一个树,还可以是一个环,模式本身不约束这个,需要我们自己去实现,同时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

    R、命令模式(Command)

    命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。我们看看关系图:
     

    Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象,看实现代码:

    public interface Command {
    	public void exe();
    }
    public class MyCommand implements Command {
     
    	private Receiver receiver;
    	
    	public MyCommand(Receiver receiver) {
    		this.receiver = receiver;
    	}
     
    	@Override
    	public void exe() {
    		receiver.action();
    	}
    }
    public class Receiver {
    	public void action(){
    		System.out.println("command received!");
    	}
    }
    public class Invoker {
    	
    	private Command command;
    	
    	public Invoker(Command command) {
    		this.command = command;
    	}
     
    	public void action(){
    		command.exe();
    	}
    }
    public class Test {
     
    	public static void main(String[] args) {
    		Receiver receiver = new Receiver();
    		Command cmd = new MyCommand(receiver);
    		Invoker invoker = new Invoker(cmd);
    		invoker.action();
    	}
    }

    输出:command received!

    这个很哈理解,命令模式的目的就是将命令的发出者和执行者之间解耦,实现请求和执行分开,熟悉Struts的同学应该知道,Struts其实就是一种将请求和呈现分离的技术,其中必然涉及命令模式的思想!

    S、备忘录模式(Memento)

    备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。

    Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例,该模式很好理解。直接看源码

    public class Original {
    	
    	private String value;
    	
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
     
    	public Original(String value) {
    		this.value = value;
    	}
     
    	public Memento createMemento(){
    		return new Memento(value);
    	}
    	
    	public void restoreMemento(Memento memento){
    		this.value = memento.getValue();
    	}
    }
    public class Memento {
    	
    	private String value;
     
    	public Memento(String value) {
    		this.value = value;
    	}
     
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
    }
    public class Storage {
    	
    	private Memento memento;
    	
    	public Storage(Memento memento) {
    		this.memento = memento;
    	}
     
    	public Memento getMemento() {
    		return memento;
    	}
     
    	public void setMemento(Memento memento) {
    		this.memento = memento;
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		// 创建原始类
    		Original origi = new Original("egg");
     
    		// 创建备忘录
    		Storage storage = new Storage(origi.createMemento());
     
    		// 修改原始类的状态
    		System.out.println("初始化状态为:" + origi.getValue());
    		origi.setValue("niu");
    		System.out.println("修改后的状态为:" + origi.getValue());
     
    		// 回复原始类的状态
    		origi.restoreMemento(storage.getMemento());
    		System.out.println("恢复后的状态为:" + origi.getValue());
    	}
    }

    输出:

    初始化状态为:egg
    修改后的状态为:niu
    恢复后的状态为:egg

    简单描述下:新建原始类时,value被初始化为egg,后经过修改,将value的值置为niu,最后倒数第二行进行恢复状态,结果成功恢复了。其实我觉得这个模式叫“备份-恢复”模式最形象。

    T、状态模式(State)

    状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。比如QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。看图:

    State类是个状态类,Context类可以实现切换,我们来看看代码:

    /**
     * 状态类的核心类
     */
    public class State {
    	
    	private String value;
    	
    	public String getValue() {
    		return value;
    	}
     
    	public void setValue(String value) {
    		this.value = value;
    	}
     
    	public void method1(){
    		System.out.println("execute the first opt!");
    	}
    	
    	public void method2(){
    		System.out.println("execute the second opt!");
    	}
    }
    /**
     * 状态模式的切换类
     */
    public class Context {
     
    	private State state;
     
    	public Context(State state) {
    		this.state = state;
    	}
     
    	public State getState() {
    		return state;
    	}
     
    	public void setState(State state) {
    		this.state = state;
    	}
     
    	public void method() {
    		if (state.getValue().equals("state1")) {
    			state.method1();
    		} else if (state.getValue().equals("state2")) {
    			state.method2();
    		}
    	}
    }

     测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		State state = new State();
    		Context context = new Context(state);
    		
    		//设置第一种状态
    		state.setValue("state1");
    		context.method();
    		
    		//设置第二种状态
    		state.setValue("state2");
    		context.method();
    	}
    }

    输出:

    execute the first opt!
    execute the second opt!

    根据这个特性,状态模式在日常开发中用的挺多的,尤其是做网站的时候,我们有时希望根据对象的某一属性,区别开他们的一些功能,比如说简单的权限控制等。

    U、访问者模式(Visitor)

    访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。

    访问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。访问者模式适用于数据结构相对稳定算法又易变化的系统。因为访问者模式使得算法操作增加变得容易。若系统数据结构对象易于变化,经常有新的数据对象增加进来,则不适合使用访问者模式。访问者模式的优点是增加操作很容易,因为增加操作意味着增加新的访问者。访问者模式将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困难。—— From 百科

    简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。简单关系图:

    来看看原码:一个Visitor类,存放要访问的对象,

    public interface Visitor {
    	public void visit(Subject sub);
    }
    public class MyVisitor implements Visitor {
     
    	@Override
    	public void visit(Subject sub) {
    		System.out.println("visit the subject:"+sub.getSubject());
    	}
    }

    Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性,

    public interface Subject {
    	public void accept(Visitor visitor);
    	public String getSubject();
    }
    public class MySubject implements Subject {
     
    	@Override
    	public void accept(Visitor visitor) {
    		visitor.visit(this);
    	}
     
    	@Override
    	public String getSubject() {
    		return "love";
    	}
    }

    测试:

    public class Test {
     
    	public static void main(String[] args) {
    		
    		Visitor visitor = new MyVisitor();
    		Subject sub = new MySubject();
    		sub.accept(visitor);	
    	}
    }

     输出:visit the subject:love

    该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:

    1. 新功能会不会与现有功能出现兼容性问题?
    2. 以后会不会再需要添加?
    3. 如果类不允许修改代码怎么办?

    面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦。

    V、中介者模式(Mediator)

    中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用。先看看图:

     User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!基本实现:

    public interface Mediator {
    	public void createMediator();
    	public void workAll();
    }
    public class MyMediator implements Mediator {
     
    	private User user1;
    	private User user2;
    	
    	public User getUser1() {
    		return user1;
    	}
     
    	public User getUser2() {
    		return user2;
    	}
     
    	@Override
    	public void createMediator() {
    		user1 = new User1(this);
    		user2 = new User2(this);
    	}
     
    	@Override
    	public void workAll() {
    		user1.work();
    		user2.work();
    	}
    }
    public abstract class User {
    	
    	private Mediator mediator;
    	
    	public Mediator getMediator(){
    		return mediator;
    	}
    	
    	public User(Mediator mediator) {
    		this.mediator = mediator;
    	}
     
    	public abstract void work();
    }
    public class User1 extends User {
     
    	public User1(Mediator mediator){
    		super(mediator);
    	}
    	
    	@Override
    	public void work() {
    		System.out.println("user1 exe!");
    	}
    }
    public class User2 extends User {
     
    	public User2(Mediator mediator){
    		super(mediator);
    	}
    	
    	@Override
    	public void work() {
    		System.out.println("user2 exe!");
    	}
    }

    测试类:

    public class Test {
     
    	public static void main(String[] args) {
    		Mediator mediator = new MyMediator();
    		mediator.createMediator();
    		mediator.workAll();
    	}
    }

    输出:

    user1 exe!
    user2 exe!

    W、解释器模式(Interpreter)

    解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。

    Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下:

    public interface Expression {
    	public int interpret(Context context);
    }
    public class Plus implements Expression {
     
    	@Override
    	public int interpret(Context context) {
    		return context.getNum1()+context.getNum2();
    	}
    }
    public class Minus implements Expression {
     
    	@Override
    	public int interpret(Context context) {
    		return context.getNum1()-context.getNum2();
    	}
    }
    public class Context {
    	
    	private int num1;
    	private int num2;
    	
    	public Context(int num1, int num2) {
    		this.num1 = num1;
    		this.num2 = num2;
    	}
    	
    	public int getNum1() {
    		return num1;
    	}
    	public void setNum1(int num1) {
    		this.num1 = num1;
    	}
    	public int getNum2() {
    		return num2;
    	}
    	public void setNum2(int num2) {
    		this.num2 = num2;
    	}
    	
    	
    }
    public class Test {
     
    	public static void main(String[] args) {
     
    		// 计算9+2-8的值
    		int result = new Minus().interpret((new Context(new Plus()
    				.interpret(new Context(9, 2)), 8)));
    		System.out.println(result);
    	}
    }

    最后输出正确的结果:3。

    基本就这样,解释器模式用来做各种各样的解释器,如正则表达式等的解释器等等!

    展开全文
  • 常用设计模式

    2018-08-25 23:56:19
    单例模式(Singleton)--单线程 保证一个类仅有一个实例,并提供一个访问它的全局访问点,避免一个全局使用的类频繁的创建和销毁,节省系统资源,提高程序效率。怎么创建唯一的实例?Java是这么创建实例的 Person p =...

    单例模式(Singleton)--单线程

    保证一个类仅有一个实例,并提供一个访问它的全局访问点,避免一个全局使用的类频繁的创建和销毁,节省系统资源,提高程序效率。怎么创建唯一的实例?Java是这么创建实例的 Person p = new Person();但是这么创建会创建多个实例,所以我们必须把构造器设为私有,这样其他类就不能使用new来实例化一个类。

    1. //单线程实现单例模式
    2. class Singleton {
    3.     private static Singleton instance;
    4.     
    5.     public Singleton() {};
    6.     
    7.     public static Singleton getInstance (){
    8.         instance = new Singleton();
    9.         
    10.         return instance;
    11.     }
    12. }
    13.  
    14. public class Main {
    15.  
    16.     public static void main(String[] args) {
    17.         Singleton s1 = Singleton.getInstance();
    18.         Singleton s2 = Singleton.getInstance();
    19.         //判断两个实例s1 s2是否为同一个实例
    20.         System.out.println(s1 == s2);
    21.     }
    22.  
    23. }public class Singleton {
    24.  
    25.     //定义一个属性,用来保存Singleton类对象的实例
    26.     private static Singleton instance;
    27.  
    28.     //私有构造器,该类不能被外部类使用new方式实例化
    29.     private Singleton(){
    30.  
    31.     }
    32.  
    33.     //外部通过该方法获取Singleton类的唯一实例
    34.     public static Singleton getInstance(){
    35.         if (instance == null) {
    36.             instance = new Singleton();
    37.         }
    38.         return instance;
    39.     }
    40. }

    这种实现方式并不是线程安全的,当有多个线程同时调用Singleton.getInstance()方法时会产生多个实例。下节我们来学习多线下如何实现单例模式。

    Java多线程程序,线程执行顺序是不确定的,所以在同时多个线程调用Singleton.getInstance()方法时,存在创建多个实例的可能,会引起程序执行错误。那我们该如何实现多线程下安全的创建一个唯一的实例呢?锁,加锁。在线程调用Singleton.getInstance()方法时,判断instance == null ? 是,加锁,其他线程这时只能等待这个线程释放锁,才能进入临界区。那如何加锁,可以使用synchronized。

    1.  public static Singleton getInstance() {
    2.         //synchronized加锁同步会降低效率,这里先判断是否为空
    3.         //不为空则不需要加锁,提高程序效率
    4.         if (instance == null) {
    5.             synchronized (Singleton.class) {
    6.                 if (instance == null) {
    7.                     instance = new Singleton();
    8.                 }
    9.             }
    10.         }
    11.         return instance;
    12.     }

    单例模式优点

    • 1 在内存中只有一个对象,节省内存空间。
    • 2 避免频繁的创建销毁对象,可以提高性能。
    • 3 避免对共享资源的多重占用。
    • 4 可以全局访问。

    适用场景

    • 1 需要频繁实例化然后销毁的对象。
    • 2 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
    • 3 有状态的工具类对象。
    • 4 频繁访问数据库或文件的对象。
    • 5 以及其他我没用过的所有要求只有一个对象的场景。

    工厂方法模式(Factory Method)

    定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

    类图:

     

    • 1.很多工厂都有一些相同的行为,比如汽车工厂。我们需要抽象这些相同的行为成接口,每个工厂都实现这个接口。
    1. public interface IFactory {
    2.  
    3.     public void createProduct();
    4. }
    • 2.生产相同的产品每个工厂所使用的方法可能不同,所以具体如何生产产品由具体工厂实现。
    1. public class Factory implements IFactory {
    2.  
    3.     @Override
    4.     public void createProduct() {
    5.  
    6.     }
    7. }

    工厂模式两要点:

    •  

    1.工厂接口是工厂方法模式的核心,与调用者直接交互用来提供产品。

    2.工厂实现决定如何实例化产品,是实现扩展的途径,需要有多少种产品,就需要有多少个具体的工厂实现。

    •  

    适用场景:

    •  

    1.在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过new就可以完成创建的对象,无需使用工厂模式。

    2.工厂模式是一种典型的解耦模式,迪米特法则在工厂模式中表现的尤为明显。假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。将会大大降低对象之间的耦合度。

    3.当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。

     

     

    抽象工厂模式(Abstract Factory)

    为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。

    抽象工厂是工厂模式的升级版,他用来创建一组相关或者相互依赖的对象。来看下抽象工厂模式的类图:

     

    上节学习了工厂模式,类的创建依赖工厂类,程序需要扩展时,我们必须创建新的工厂类。工厂类是用来生产产品的,那我们也可以把“工厂类当成我们要生产的产品”,所以抽象工厂就是“工厂的工厂”,即生产工厂的工厂。下面通过一个例子来深入理解。

    通过一个例子,来加深对抽象工厂的理解。

    1. //CPU工厂接口
    2. public interface CPUFactory {
    3.     public void createCPU();
    4. }
    1. //IntelCPU工厂
    2. public class IntelCPU implements CPUFactory {
    3.     @Override
    4.     public void createCPU() {
    5.         System.out.println("Intel CPU");
    6.     }
    7. }
    1. //AMDCPU工厂
    2. public class AMDCPU implements CPUFactory {
    3.     @Override
    4.     public void createCPU() {
    5.         System.out.println("AMD CPU");
    6.     }
    7. }
    1. //创建抽象工厂类接口
    2. public interface Provider {
    3.     public CPUFactory createCPUFactory();
    4. }
    1. public class InterCPUFactory implements Provider {
    2.     @Override
    3.     public CPUFactory createCPUFactory() {
    4.         return new InterCPU();
    5.     }
    6. }
    1. public class AMDCPUFactory implements Provider {
    2.     @Override
    3.     public CPUFactory createCPUFactory() {
    4.         return new AMDCPU();
    5.     }
    6. }
    1. public static void main(String[] args) {
    2.         //创建一个生产CPU工厂的工厂
    3.         Provider cpufactory = new InterCPUFactory();
    4.         //通过CPU工厂的工厂创建一个IntelCPU工厂
    5.         CPUFactory intelcpu = cpufactory.createCPUFactory();
    6.         //IntelCPU工厂生产intelCPU
    7.         intelcpu.createCPU();

    抽象工厂的优点:

    抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联(例如不同厂商生产CPU)。

    适用场景:

    一个继承体系中,如果存在着多个等级结构(即存在着多个抽象类),并且分属各个等级结构中的实现类之间存在着一定的关联或者约束,就可以使用抽象工厂模式。

     

    建造者模式(Builder)

    将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,由于需求的变化,这个复杂对象的某些部分经常面临着剧烈的变化,一些基本部件不会变。所以需要将变与不变分离。与抽象工厂的区别:在建造者模式里,有个指导者(Director),由指导者来管理建造者,用户是与指导者联系的,指导者联系建造者最后得到产品。即建造者模式可以强制实行一种分步骤进行的建造过程。

    建造者类图:

     

    建造者模式四要素:

    •  

    1.产品类Product:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。​​​​​​​

    2.抽象建造者类Builder: 将建造的具体过程交与它的子类来实现,这样更容易扩展。​​​​​​​

    3.建造者类ConcreteBuilder: 组建产品;返回组建好的产品。​​​​​​​

    4.指导类Director: 负责调用适当的建造者来组建产品,指导类一般不与产品类发生依赖关系,与指导类直接交互的是建造者类。

    似乎很抽象。举个例子:前面你创建了一个生产保时捷的工厂,生产一台保时捷911需要的主要部件都一样(引擎,轮子,方向盘...)和流程是不变的,变的是引擎,轮子,控制系统等等部件具体实现,这些部件的生产交由具体的builder去生产。

    1. /抽象生产者
    2. public interface Builder {
    3.  
    4.     void buildPartA();
    5.     void buildPartB();
    6.     void buildPartC();
    7.  
    8.     Product buildProduct();
    9. }
    1. //具体生产者
    2. public class ConcreteBuilder implements Builder {
    3.  
    4.     Product product;
    5.  
    6.     @Override
    7.     public void buildPartA() {
    8.  
    9.     }
    10.  
    11.     @Override
    12.     public void buildPartB() {
    13.  
    14.     }
    15.  
    16.     @Override
    17.     public void buildPartC() {
    18.  
    19.     }
    20.  
    21.     @Override
    22.     public Product buildProduct() {
    23.         return product;
    24.     }
    25. }
    1. //产品由各个组件组成
    2. public class Product {
    3.  
    4.     //partA
    5.     //partB
    6.     //partC
    7. }
    1. //指导者,产品生产流程规范
    2. public class Director {
    3.  
    4.     Builder builder;
    5.     //由具体的生产者来生产产品
    6.     public Director(Builder builder) {
    7.         this.builder = builder;
    8.     }
    9.     //生产流程
    10.     public void buildProduct(){
    11.         builder.buildPartA();
    12.         builder.buildPartB();
    13.         builder.buildPartC();
    14.     }
    15. }
    1. public static void main(String[] args) {
    2.         //只需要关心具体建造者,无需关心产品内部构建流程。
    3.         //如果需要其他的复杂产品对象,只需要选择其他的建造者.
    4.         Builder builder = new ConcreteBuilder();
    5.         //把建造者注入指导者
    6.         Director director = new Director(builder);
    7.         //指导者负责流程把控
    8.         director.buildProduct();
    9.         // 建造者返回一个组合好的复杂产品对象
    10.         Product product = builder.buildProduct();
    11.     }

    建造者模式优点:

    •  

    1.建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指导者类中对整体而言可以取得比较好的稳定性。​​​​​​​

    2.建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成。

     

    适用场景:需要生成的对象具有复杂的内部结构;需要生成的对象内部属性本身相互依赖。

     

    模板方法模式(Template Method)

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

    类图:

     

    模板方法模式是编程中经常用到的模式,其非常简单,AbstractClass叫抽象模板,其方法分为3类:

    •  

    1.抽象方法:父类中只声明但不加以实现,而是定义好规范,然后由它的子类去实现。

    •  
    •  

    2.模版方法:由抽象类声明并加以实现。一般来说,模版方法调用抽象方法来完成主要的逻辑功能,并且,模版方法大多会定义为final类型,指明主要的逻辑功能在子类中不能被重写。

    •  
    •  

    3.钩子方法:由抽象类声明并加以实现。但是子类可以去扩展,子类可以通过扩展钩子方法来影响模版方法的逻辑。

    •  

    实现类用来实现细节。抽象类中的模版方法正是通过实现类扩展的方法来完成业务逻辑。

    现在要实现一个对无序数组从小到大排序并打印数组的类。排序算法有很多种,打印功能固定的。定义一个AbstractClass定义抽象排序方法由子类去实现;模板类实现打印方法。

    1. //抽象模板类
    2. public abstract class AbstractSort {
    3.  
    4.     public abstract void sort(int[] array);
    5.     //防止子类覆盖使用final修饰
    6.     public final void printArray(int[] array) {
    7.         sort(array);
    8.         for (int i = 0; i < array.length; i++) {
    9.             System.out.println(array[i]);
    10.         }
    11.     }
    12. }
    1. //具体实现类
    2. public class QuickSort extends AbstractSort {
    3.     @Override
    4.     public void sort(int[] array) {
    5.         //使用快排算法实现
    6.     }
    7. }
    1. public class MergeSort extends AbstractSort {
    2.     @Override
    3.     public void sort(int[] array) {
    4.         //使用归并排序算法实现
    5.     }
    6. }
    1. public static void main(String[] args) {
    2.         int[] arr = {3,5,2,45,243,341,111,543,24};
    3.         //AbstractSort s = new MergeSort();
    4.         AbstractSort s = new QuickSort();
    5.         s.printArray(arr);
    6.     }
    7.  

    模板方法模式优点:

    •  

    1.容易扩展。一般来说,抽象类中的模版方法是不易反生改变的部分,而抽象方法是容易反生变化的部分,因此通过增加实现类一般可以很容易实现功能的扩展,符合开闭原则。

    •  
    •  

    2.便于维护。对于模版方法模式来说,正是由于他们的主要逻辑相同,才使用了模版方法。

    •  

    适用场景:

    在多个子类拥有相同的方法,并且这些方法逻辑相同时,可以考虑使用模版方法模式。在程序的主框架相同,细节不同的场合下,也比较适合使用这种模式。

    上节你生产了保时捷911和Cayma,现在要对其进行检测。检测程序顺序:能否启动start,加油门speeup,刹车brake,熄火stop。由于检测标准不一样,请你用模板方法模式来实现。

     

    适配器模式(Adapter Class/Object)

    是指将一个接口转换成客户端希望的另外一个接口,该模式使得原本不兼容的类可以一起工作。

    举个例子:macbook pro有一个HDMI接口,一条HDMI接口的数据线,现在要外接显示器,而显示器只有VGI接口,我们需要一个HDMI-VGI转换器,这个转换器其实起到的作用就是适配器,让两个不兼容的接口可以一起工作。

    类图:

    适配器有4种角色:

    •  

    1.目标抽象角色(Target):定义客户所期待的使用接口。(GVI接口)

    •  
    •  

    2.源角色(Adaptee):需要被适配的接口。(HDMI接口)

    •  
    •  

    3.适配器角色(Adapter):把源接口转换成符合要求的目标接口的设备。(HDMI-VGI转换器)

    •  
    •  

    4.客户端(client):例子中指的VGI接口显示器。

    •  

    继续学习 

    把HDMI接口转换成VGI接口,使得macbook pro可以外接显示器。

    1. //HDMI接口,需要被适配的接口
    2. public interface HDMIPort {
    3.     void workByHDMI();
    4. }
    1. //VGI接口,客户端所期待的接口
    2. public interface VGIPort {
    3.     void workByVGI();
    4. }
    1. //将HDMI接口转换为VGI,这就是适配器
    2. public class HDMIToVGI implements VGIPort{
    3.  
    4.     HDMIPort hdmiPort;
    5.  
    6.     public HDMIToVGI(HDMIPort hdmiPort) {
    7.         this.hdmiPort = hdmiPort;
    8.     }
    9.     //将HDMI接口转换为VGI接口
    10.     @Override
    11.     public void workByVGI() {
    12.         hdmiPort.workByHDMI();
    13.     }
    14. }
    1.  public static void main(String[] args) {
    2.         //定义一个HDMI接口
    3.         HDMIPort hdmiPort = new HDMIPort() {
    4.             @Override
    5.             public void workByHDMI() {
    6.                 //hdmi接口工作方式
    7.             }
    8.         };
    9.         //将HDMI接口转换为VGI接口
    10.         VGIPort vgiPort = new HDMIToVGI(hdmiPort);
    11.         //经过转换HDMI接口变成了VGI接口
    12.         vgiPort.workByVGI();
    13.     }

    适配器模式优点:

    •  

    1.可以让任何两个没有关联的类一起运行。​​​​​​​

    2.提高了类的复用。​​​​​​​

    3.增加了类的透明度。​​​​​​​

    4.灵活性好。

    适配器模式缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。

    适用场景:

    1.系统需要使用现有的类,而此类的接口不符合系统的需要。​​​​​​​

    2.想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。​​​​​​​

    3.通过接口转换,将一个类插入另一个类系中。

    •  

    外观模式(Facade)

    为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。降低访问复杂系统的内部子系统时的复杂度。

    类图:

     

    在客户端和复杂系统之间再加一层,将调用顺序、依赖关系等处理好。举个例子:我们经常用的电脑,开机其实是个非常复杂的过程,而我们只需要按开机按钮就可以了。

    模拟电脑启动,假设电脑启动顺序:启动CPU,启动内存,启动硬盘,加载数据等。

    1. public class CPU {
    2.  
    3.     public void startup(){
    4.         System.out.println("启动CPU");
    5.     }
    6. }
    1. public class Memory {
    2.  
    3.     public void startup(){
    4.         System.out.println("启动内存");
    5.     }
    6. }
    1. public class Disk {
    2.  
    3.     public void startup(){
    4.         System.out.println("启动硬盘");
    5.     }
    6. }
    1. //facade
    2. public class Computer {
    3.  
    4.     CPU cpu;
    5.     Memory memory;
    6.     Disk disk;
    7.  
    8.     public Computer(){
    9.         cpu = new CPU();
    10.         memory = new Memory();
    11.         disk = new Disk();
    12.     }
    13.  
    14.     public void start(){
    15.         cpu.startup();
    16.         memory.startup();
    17.         disk.startup();
    18.     }
    19. }
    1. public static void main(String[] args) {
    2.         Computer computer = new Computer();
    3.         //启动computer是个很复杂的过程,我们并不需要知道其启动各个子系统的加载过程
    4.         //只需要调用computer为各个子系统提供统一的一个接口start()就可以启动computer了
    5.         computer.start();
    6.     }

    外观模式优点:

    •  

    1.减少系统相互依赖。​​​​​​​​​​​​​​

    2.提高灵活性。

    ​​​​​​​3.提高了安全性。

    适用场景:

     

    1.为复杂的模块或子系统提供外界访问的模块。​​​​​​​

    2.客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade 将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。

    •  

    装饰器模式(Decorator)

    对客户透明的方式动态地给一个对象附加上更多的责任,同时又不改变其结构。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

    类图:

     

    1.抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。​​​​​​​

    2.具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。​​​​​​​

    3.装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。​​​​​​​

    4.具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

    Java IO中就是典型的装饰器

    1. //InputStream提供的基本方法(Component)
    2. public abstract class InputStream implements Closeable {
    3.  
    4. }
    1. //默认目标实现类(ConcreteComponent)
    2. public class FileInputStream extends InputStream {
    3.  
    4. }
    1. /*装饰实现类(FilterInputStream)一定是继承或实现原始接口(InputStream)的,内部有包含一个原始接口的超类(其实就是某个默认目标实现类)*/
    2. //Decorator
    3. public class FilterInputStream extends InputStream {
    4.     /**
    5.      * The input stream to be filtered.
    6.      */
    7.     protected volatile InputStream in;
    8.  
    9.     protected FilterInputStream(InputStream in) {
    10.         this.in = in;
    11.     }
    12. }
    1. //具体装饰类(ConcreteDecorator)
    2. public class BufferedInputStream extends FilterInputStream {
    3.  
    4.     public BufferedInputStream(InputStream in) {
    5.         this(in, DEFAULT_BUFFER_SIZE);
    6.     }
    7. }
    1. //具体装饰类(ConcreteDecorator)
    2. public class DataInputStream extends FilterInputStream implements DataInput {
    3.  
    4.     public DataInputStream(InputStream in) {
    5.         super(in);
    6.     }
    7. }

    装饰器模式优点:

    1.装饰类和被装饰类可以独立发展,不会相互耦合。​​​​​​​

    2.装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。就增加功能来说,装饰器模式相比生成子类更为灵活。

    适用场景:

    1.扩展一个类的功能。​​​​​​​

    2.动态增加功能,动态撤销。

    观察者模式(Observer)

    对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

    类图:

     

     

    1.抽象主题(Subject)角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。​​​​​​​

    2.抽象观察者(Observer)角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。​​​​​​

    3.具体主题(ConcreteSubject)角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。​​​​​​​

    4.具体观察者(ConcreteObserver)角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

    控件按钮、报警器等都是观察者模式。

    1. public interface Subject {
    2.     //添加观察者
    3.     void attach(Observer o);
    4.     //删除观察者
    5.     void detach(Observer o);
    6.     //通知观察者
    7.     void notifyObservers();
    8.     //发生某事
    9.     void doSomeThings()
    10. }
    1. //观察者
    2. public interface Observer {
    3.  
    4.     void update();
    5. }
    1. public class ConcreteSubject implements Subject {
    2.  
    3.     ArrayList<Observer> observers = new ArrayList<>();
    4.  
    5.     @Override
    6.     public void attach(Observer o) {
    7.         observers.add(o);
    8.     }
    9.  
    10.     @Override
    11.     public void detach(Observer o) {
    12.         observers.remove(o);
    13.     }
    14.  
    15.     @Override
    16.     public void notifyObservers() {
    17.         for (Observer o : observers) {
    18.             o.update();
    19.         }
    20.     }
    21.  
    22.     public void doSomeThings(){
    23.         //doSomeThings
    24.         notifyObservers();//通知观察者
    25.     }
    26. }
    1. //具体观察者
    2. public class ConcreteObserver implements Observer {
    3.     @Override
    4.     public void update() {
    5.         System.out.println("我观察到subject发生了某事");
    6.     }
    7. }
    1. public static void main(String[] args) {
    2.         Subject cs = new ConcreteSubject();
    3.         //添加观察者
    4.         cs.attach(new ConcreteObserver());
    5.         //subject发生了某事,通知观察者
    6.         cs.doSomeThings();
    7.     }

    观察者模式优点:

    •  

    1.观察者和被观察者是抽象耦合的。​​​​​​​

    2.建立一套触发机制。

    观察者模式缺点:

     

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

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

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

    •  

    适用场景:

    1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。​​​​​​​

    2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

    3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

    策略模式(Strategy)

    定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

    类图:

     

    1.Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略,实现定义的策略。​​​​​​

    2.ConcreteStrategy:具体的策略实现,也就是具体的算法实现。​​​​​​​

    3.Context:上下午,负责与具体的策略交互,通常上下文会持有一个真正的策略实现。

    策略模式是把一个类中经常改变或者将来可能改变的部分提取出来作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。

    现在我们要根据不同需求,计算两个数的四则运算( + - * /)

    1. //策略定义算法的接口
    2. public interface Strategy {
    3.     int calculate(int num1,int num2);
    4. }
    1. //具体算法,加法
    2. public class OperationAdd implements Strategy {
    3.     @Override
    4.     public int calculate(int num1, int num2) {
    5.         return num1 + num2;
    6.     }
    7. }
    1. //具体算法,减法
    2. public class OperationSubstract implements Strategy {
    3.     @Override
    4.     public int calculate(int num1, int num2) {
    5.         return num1 - num2;
    6.     }
    7. }
    1. //具体算法,乘法
    2. public class OperationMultiply implements Strategy {
    3.     @Override
    4.     public int calculate(int num1, int num2) {
    5.         return num1 * num2;
    6.     }
    7. }
    1. //具体算法,除法
    2. public class OperationDivide implements Strategy {
    3.     @Override
    4.     public int calculate (int num1, int num2){
    5.         int res = 0;
    6.         try {
    7.             res = num1 / num2;
    8.         }catch (Exception e) {
    9.             e.printStackTrace();
    10.         }
    11.         return res;
    12.     }
    13. }
    1. //上下文
    2. public class Context {
    3.     //持有一个具体策略对象
    4.     private Strategy strategy;
    5.  
    6.     //传入一个具体策略对象
    7.     public Context(Strategy strategy) {
    8.         this.strategy =strategy;
    9.     }
    10.  
    11.     public int calculate(int num1,int num2){
    12.         //调用具体策略对象进行算法运算
    13.         return strategy.calculate(num1,num2);
    14.     }
    15. }
    1.  public static void main(String[] args) {
    2.         //计算 1 + 1
    3.         Context context = new Context(new OperationAdd());
    4.         System.out.println("1 + 1 = " + context.calculate(1,1));
    5.         //计算 1 - 1
    6.         context = new Context(new OperationSubstract());
    7.         System.out.println("1 - 1 = " +context.calculate(1,1));
    8.     }

    策略模式优点:

    1.算法可以自由切换。​​​​​​​

    2.避免使用多重条件判断。​​​​​​​

    3.扩展性良好。

    策略模式缺点:

    1.策略类会增多。​​​​​​​

    2.所有策略类都需要对外暴露。

    适用场景:

    1.如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。​​​​​​​

    2.一个系统需要动态地在几种算法中选择一种。​​​​​​​​​​​​​​

    3.一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。

    •  

     

     

    展开全文
  • 23种设计模式总结

    2020-07-10 17:29:44
    【转】https://www.cnblogs.com/geek6/p/3951677.html 【转】https://www.cnblogs.com/tongkey/p/7170826.html 【转】... 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式...

    【参考】https://www.cnblogs.com/geek6/p/3951677.html

    【参考】https://www.cnblogs.com/tongkey/p/7170826.html

    【参考】https://blog.csdn.net/xiao190128/article/details/80533324

    【参考】http://c.biancheng.net/design_pattern/

    【参考】https://baijiahao.baidu.com/s?id=1639044219412817957&wfr=spider&for=pc

    【参考】https://blog.csdn.net/A1342772/article/details/91349142

     

    总体来说设计模式分为三大类:

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

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

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

    其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:

    6大设计原则

      Single Responsibility Principle  : 单一职责原则

      Liskov Substitution Principle     : 里氏替换原则

      Dependence Inversion Principle :依赖倒置原则

      Interface Segregation Principle  : 接口隔离原则

      Law of Demeter               : 迪米特法则

      Open Closed Principle               : 开闭原则

    单一职责原则:类的设计尽量做到只有一个原因可以引起它的改变
    里氏替换原则:只要父类出现的地方子类就可以出现,且替换成子类也不会出现任何错误或者异常
    依赖倒置原则:针对接口编程,而不是针对实现编程
    接口隔离原则:不要建立臃肿庞大的接口。即接口尽量细化,同时接口中的方法尽量少
    迪米特法则:  一个对象应该对其他对象有最少的了解,也就是说一个类要对自己需要耦合或者调用的类知道的最少
    开闭原则:    一个软件实体,比如类,模块,函数应该对扩展开放,对修改关闭

    1.单例模式(Singleton Pattern)

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

    通用代码:(是线程安全的)

     

    public class Singleton {
         private static final Singleton singleton = new Singleton();
    //限制产生多个对象
         private Singleton(){
         }
         //通过该方法获得实例对象
         public static Singleton getSingleton(){
                 return singleton;
         }  
         //类中其他方法,尽量是static
         public static void doSomething(){
         }
    }

     

     

    使用场景:

    ● 要求生成唯一序列号的环境;

    ● 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器,可以不用把每次刷新都记录到数据库中,使用单例模式保持计数器的值,并确保是线程安全的;

    ● 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;

    ● 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当然,也可以直接声明为static的方式)。

     

    线程不安全实例:

      

     

    public class Singleton {
         private static Singleton singleton = null; 
         //限制产生多个对象
         private Singleton(){
         }  
         //通过该方法获得实例对象
         public static Singleton getSingleton(){
                 if(singleton == null){
                        singleton = new Singleton();
                 }
                 return singleton;
         }
    }

     

     

     

     

    解决办法:

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

    2.工厂模式

    定义: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完成的。

    具体工厂类代码:

     

    public class ConcreteCreator extends Creator {
    public <T extends Product> T createProduct(Class<T> c){
                 Product product=null;
                 try {
                        product = (Product)Class.forName(c.getName()).newInstance();
                 } catch (Exception e) {
                        //异常处理
                 }
                 return (T)product;         
         }
    }

     

     

    简单工厂模式:

    一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法

    多个工厂类:

    每个人种(具体的产品类)都对应了一个创建者,每个创建者独立负责创建对应的产品对象,非常符合单一职责原则

    代替单例模式:

    单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象

    延迟初始化:

    ProductFactory负责产品类对象的创建工作,并且通过prMap变量产生一个缓存,对需要再次被重用的对象保留

    使用场景:jdbc连接数据库,硬件访问,降低对象的产生和销毁

    3.抽象工厂模式(Abstract Factory Pattern)

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

    抽象工厂模式通用类图:

    抽象工厂模式通用源码类图:

    抽象工厂类代码:

    public abstract class AbstractCreator {
         //创建A产品家族
         public abstract AbstractProductA createProductA(); 
         //创建B产品家族
         public abstract AbstractProductB createProductB();
    }

    使用场景:

    一个对象族(或是一组没有任何关系的对象)都有相同的约束。

    涉及不同操作系统的时候,都可以考虑使用抽象工厂模式

     

    4.模板方法模式(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属于具体模板,实现父类所定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现

    使用场景:

    ● 多个子类有公有的方法,并且逻辑基本相同时。

    ● 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现。

    ● 重构时,模板方法模式是一个经常使用的模式,把相同的代码抽取到父类中,然后通过钩子函数(见“模板方法模式的扩展”)约束其行为。

     

    5.建造者模式(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开始建造

    使用场景:

    ● 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。

    ● 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。

    ● 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。

     

    建造者模式与工厂模式的不同:

    建造者模式最主要的功能是基本方法的调用顺序安排,这些基本方法已经实现了,顺序不同产生的对象也不同;

    工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的。

     

     

     

    6.代理模式(Proxy Pattern)

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

    ● Subject抽象主题角色

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

    ● RealSubject具体主题角色

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

    ● Proxy代理主题角色

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

     

    普通代理和强制代理:

    普通代理就是我们要知道代理的存在,也就是类似的GamePlayerProxy这个类的存在,然后才能访问;

    强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的。

    普通代理:

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

     

    强制代理:

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

     

    动态代理:

    根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”。

    两条独立发展的线路。动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。

     

    动态代理调用过程示意图:

    动态代理的意图:横切面编程,在不改变我们已有代码结构的情况下增强或控制对象的行为。 

    首要条件:被代理的类必须要实现一个接口。

    7.原型模式(Prototype Pattern)

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

     

    原型模式通用代码:

     

    public class PrototypeClass  implements Cloneable{
         //覆写父类Object方法
         @Override
         public PrototypeClass clone(){
                 PrototypeClass prototypeClass = null;
                 try {
                        prototypeClass = (PrototypeClass)super.clone();
                 } catch (CloneNotSupportedException e) {
                        //异常处理
                 }
                 return prototypeClass;
         }
    }

     

     

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

    使用原型模式的优点:

    ● 性能优良

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

    ● 逃避构造函数的约束

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

    使用场景:

    ● 资源优化场景

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

    ● 性能和安全要求的场景

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

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

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

     

    浅拷贝和深拷贝:

    浅拷贝:Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝,其他的原始类型比如int、long、char、string(当做是原始类型)等都会被拷贝。

    注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。

    深拷贝:对私有的类变量进行独立的拷贝    

      如:thing.arrayList = (ArrayList<String>)this.arrayList.clone();

    8.中介者模式

    定义: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)。

     

    通用抽象中介者代码:

     

    public abstract class Mediator {
         //定义同事类
         protected ConcreteColleague1 c1;
         protected ConcreteColleague2 c2;
         //通过getter/setter方法把同事类注入进来
         public ConcreteColleague1 getC1() {
                 return c1;
         }
         public void setC1(ConcreteColleague1 c1) {
                 this.c1 = c1;
         }
         public ConcreteColleague2 getC2() {
                 return c2;
    }
         public void setC2(ConcreteColleague2 c2) {
                 this.c2 = c2;
         }
         //中介者模式的业务逻辑
         public abstract void doSomething1();
         public abstract void doSomething2();
    }

     

     

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

     

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

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

     

    使用场景:

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

    9.命令模式

    定义: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命令的时候,当然也要采用命令模式;触发-反馈机制的处理等。

     

    10.责任链模式

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

     

    抽象处理者的代码:

     

    public abstract class Handler {
         private Handler nextHandler;
         //每个处理者都必须对请求做出处理
         public final Response handleMessage(Request request){
                 Response response = null;  
                 //判断是否是自己的处理级别
                 if(this.getHandlerLevel().equals(request.getRequestLevel())){
                        response = this.echo(request);
                 }else{  //不属于自己的处理级别
                        //判断是否有下一个处理者
                        if(this.nextHandler != null){
                                response = this.nextHandler.handleMessage(request);
                        }else{
                                //没有适当的处理者,业务自行处理
                        }
                 }
                 return response;
         }
         //设置下一个处理者是谁
         public void setNext(Handler _handler){
                 this.nextHandler = _handler;
         }
         //每个处理者都有一个处理级别
         protected abstract Level getHandlerLevel();
         //每个处理者都必须实现处理任务
         protected abstract Response echo(Request request);
    }

     

     

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

    一是定义一个请求的处理方法handleMessage,唯一对外开放的方法;

    二是定义一个链的编排方法setNext,设置下一个处理者;

    三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLevel和具体的处理任务echo。

    注意事项:

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

     

    11.装饰模式(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是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西,上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单。

    使用场景:

    ● 需要扩展一个类的功能,或给一个类增加附加功能。

    ● 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

    ● 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。

     

    12.策略模式(Strategy Pattern)

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

     

    ● Context封装角色

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

    ● Strategy抽象策略角色

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

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

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

    使用场景:

    ● 多个类只有在算法或行为上稍有不同的场景。

    ● 算法需要自由切换的场景。

    ● 需要屏蔽算法规则的场景。

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

     

    策略模式扩展:策略枚举

     

    public enum Calculator {
         //加法运算
         ADD("+"){
                 public int exec(int a,int b){
                        return a+b;
                 }
         },
         //减法运算
         SUB("-"){
                 public int exec(int a,int b){
                        return a - b;
                 }
         };
         String value = "";
         //定义成员值类型
         private Calculator(String _value){
                 this.value = _value;
         }
         //获得枚举成员的值
         public String getValue(){
                 return this.value;
         }
         //声明一个抽象函数
         public abstract int exec(int a,int b);
    }

     

     

    定义:

    ● 它是一个枚举。

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

    注意:

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

    致命缺陷:

    所有的策略都需要暴露出去,由客户端决定使用哪一个策略。

     

    13.适配器模式(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适配器角色

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

    使用场景

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

    注意事项:

    详细设计阶段不要考虑使用适配器模式,使用主要场景为扩展应用中。

     

    对象适配器:

     对象适配器和类适配器的区别:

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

     

     

    14.迭代器模式(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自带的迭代器就已经满足我们的需求了。

    15.组合模式((Composite Pattern))

    定义:Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。)

     

    ● Component抽象构件角色

    定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性,比如我们例子中的getInfo就封装到了抽象类中。

    ● Leaf叶子构件

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

    ● Composite树枝构件

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

     

    树枝构件的通用代码:

     

    public class Composite extends Component {
         //构件容器
         private ArrayList<Component> componentArrayList = new ArrayList<Component>();
         //增加一个叶子构件或树枝构件
         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;
         }
    }

     

     

    使用场景:

    ● 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理。

    ● 从一个整体中能够独立出部分模块或功能的场景。

    注意:

    只要是树形结构,就考虑使用组合模式。

    16.观察者模式(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观察者

    观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。

    ● ConcreteSubject具体的被观察者

    定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

    ● ConcreteObserver具体的观察者

    每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

     

    被观察者通用代码:

     

    public abstract class Subject {
         //定义一个观察者数组
         private Vector<Observer> obsVector = new Vector<Observer>();
         //增加一个观察者
         public void addObserver(Observer o){
                 this.obsVector.add(o);
         }
         //删除一个观察者
         public void delObserver(Observer o){
                 this.obsVector.remove(o);
         }
         //通知所有观察者
         public void notifyObservers(){
                 for(Observer o:this.obsVector){
                         o.update();
    }
         }
    }

     

     

    使用场景:

    ● 关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。

    ● 事件多级触发场景。

    ● 跨系统的消息交换场景,如消息队列的处理机制。

    注意:

    ● 广播链的问题

    在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次)。

    ● 异步处理问题

    观察者比较多,而且处理时间比较长,采用异步处理来考虑线程安全和队列的问题。

     

    17.门面模式(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子系统角色
    可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道门面的存在。对于子系统而言,门面仅仅是另外一个客户端而已。

     

    使用场景:

    ● 为一个复杂的模块或子系统提供一个供外界访问的接口

    ● 子系统相对独立——外界对子系统的访问只要黑箱操作即可

    ● 预防低水平人员带来的风险扩散

    注意:

    ●一个子系统可以有多个门面

    ●门面不参与子系统内的业务逻辑

    18.备忘录模式(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备忘录角色(简单的javabean)

    负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。

    ● Caretaker备忘录管理员角色(简单的javabean)

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

     

    使用场景:

    ● 需要保存和恢复数据的相关状态场景。

    ● 提供一个可回滚(rollback)的操作。

    ● 需要监控的副本场景中。

    ● 数据库连接的事务管理就是用的备忘录模式。

    注意:

    ●备忘录的生命期

    ●备忘录的性能

       不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中)。    

     

    clone方式备忘录:

     

    ● 发起人角色融合了发起人角色和备忘录角色,具有双重功效

     

    多状态的备忘录模式

     

    ● 增加了一个BeanUtils类,其中backupProp是把发起人的所有属性值转换到HashMap中,方便备忘录角色存储。restoreProp方法则是把HashMap中的值返回到发起人角色中。

     

    BeanUtil工具类代码:

     

    public class BeanUtils {
         //把bean的所有属性及数值放入到Hashmap中
         public static HashMap<String,Object> backupProp(Object bean){
                 HashMap<String,Object> result = new HashMap<String,Object>();
                 try {
                         //获得Bean描述
                         BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());
                         //获得属性描述
                         PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();
                         //遍历所有属性
                         for(PropertyDescriptor des:descriptors){
                                 //属性名称
                                 String fieldName = des.getName();
                                 //读取属性的方法
                                 Method getter = des.getReadMethod();
                                 //读取属性值
                                 Object fieldValue=getter.invoke(bean,new Object[]{});
                        if(!fieldName.equalsIgnoreCase("class")){
                                 result.put(fieldName, fieldValue);
                        }
                   }
              } catch (Exception e) {
                   //异常处理
              }
              return result;
         }
         //把HashMap的值返回到bean中
         public static void restoreProp(Object bean,HashMap<String,Object> propMap){
    try {
                   //获得Bean描述
                   BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
                   //获得属性描述
                   PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                   //遍历所有属性
                   for(PropertyDescriptor des:descriptors){
                        //属性名称
                        String fieldName = des.getName();
                        //如果有这个属性
                        if(propMap.containsKey(fieldName)){
                             //写属性的方法
                             Method setter = des.getWriteMethod();
                             setter.invoke(bean, new Object[]{propMap.get(fieldName)});
                        }
                   }
              } catch (Exception e) {
                   //异常处理
                   System.out.println("shit");
                   e.printStackTrace();
              }
         }
    }

     

     

    多备份的备忘录:略

    封装得更好一点:保证只能对发起人可读

     

    ●建立一个空接口IMemento——什么方法属性都没有的接口,然后在发起人Originator类中建立一个内置类(也叫做类中类)Memento实现IMemento接口,同时也实现自己的业务逻辑。

     

    19.访问者模式(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——抽象访问者

    抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

    ● ConcreteVisitor——具体访问者

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

    ● Element——抽象元素

    接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。

    ● ConcreteElement——具体元素

    实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了。

    ● ObjectStruture——结构对象

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

     

    使用场景:

    ● 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就说是用迭代器模式已经不能胜任的情景。

    ● 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。

    20.状态模式(复杂)

    定义:Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。)

    ● State——抽象状态角色

    接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换。

    ● ConcreteState——具体状态角色

    每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态。

    ● Context——环境角色

    定义客户端需要的接口,并且负责具体状态的切换。

     

    使用场景:

    ● 行为随状态改变而改变的场景

    这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式。

    ● 条件、分支判断语句的替代者

     

    注意:

    状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。

     

    21.解释器模式(Interpreter Pattern)(少用)

    定义:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。)

     

    ● AbstractExpression——抽象解释器

    具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和Non-terminalExpression完成。

    ● TerminalExpression——终结符表达式

    实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在栈中产生了一个VarExpression对象。

    ● NonterminalExpression——非终结符表达式

    文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

    ● Context——环境角色

    具体到我们的例子中是采用HashMap代替。

     

    使用场景:

    ● 重复发生的问题可以使用解释器模式

    ● 一个简单语法需要解释的场景

     

    注意:

    尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。

    22.享元模式(Flyweight Pattern)

    定义:Use sharing to support large numbers of fine-grained objects efficiently.(使用共享对象可有效地支持大量的细粒度的对象。)

    对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。

    ● 内部状态

    内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境改变而改变。

    ● 外部状态

    外部状态是对象得以依赖的一个标记,是随环境改变而改变的、不可以共享的状态。

     

    ● Flyweight——抽象享元角色

    它简单地说就是一个产品的抽象类,同时定义出对象的外部状态和内部状态的接口或实现。

    ● ConcreteFlyweight——具体享元角色

    具体的一个产品类,实现抽象角色定义的业务。该角色中需要注意的是内部状态处理应该与环境无关,不应该出现一个操作改变了内部状态,同时修改了外部状态,这是绝对不允许的。

    ● unsharedConcreteFlyweight——不可共享的享元角色

    不存在外部状态或者安全要求(如线程安全)不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。

    ● FlyweightFactory——享元工厂

    职责非常简单,就是构造一个池容器,同时提供从池中获得对象的方法。

     

    享元工厂的代码:

     

    public class FlyweightFactory {
         //定义一个池容器
         private static  HashMap<String,Flyweight> pool= new HashMap<String,Flyweight>();
         //享元工厂
         public static Flyweight getFlyweight(String Extrinsic){
                 //需要返回的对象
                 Flyweight flyweight = null;
                 //在池中没有该对象
                 if(pool.containsKey(Extrinsic)){
                         flyweight = pool.get(Extrinsic);
                 }else{
                         //根据外部状态创建享元对象
                         flyweight = new ConcreteFlyweight1(Extrinsic);
                         //放置到池中
                         pool.put(Extrinsic, flyweight);
                 }
                 return flyweight;
         }
    }

     

     

    使用场景:

    ● 系统中存在大量的相似对象。

    ● 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份。

    ● 需要缓冲池的场景。

    注意:

    ● 享元模式是线程不安全的,只有依靠经验,在需要的地方考虑一下线程安全,在大部分场景下不用考虑。对象池中的享元对象尽量多,多到足够满足为止。

    ● 性能安全:外部状态最好以java的基本类型作为标志,如String,int,可以提高效率。

    23.桥梁模式(Bridge Pattern)

    定义:Decouple an abstraction from its implementation so that the two can vary independently.(将抽象和实现解耦,使得两者可以独立地变化。)

     

    ● Abstraction——抽象化角色

    它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。

    ● Implementor——实现化角色

    它是接口或者抽象类,定义角色必需的行为和属性。

    ● RefinedAbstraction——修正抽象化角色

    它引用实现化角色对抽象化角色进行修正。

    ● ConcreteImplementor——具体实现化角色

    它实现接口或抽象类定义的方法和属性。

     

    使用场景:

    ● 不希望或不适用使用继承的场景

    ● 接口或抽象类不稳定的场景

    ● 重用性要求较高的场景

     

    注意:

    发现类的继承有N层时,可以考虑使用桥梁模式。桥梁模式主要考虑如何拆分抽象和实现。

     

     

    设计原则:

    ●Single Responsibility Principle:单一职责原则

    单一职责原则有什么好处: 

    ● 类的复杂性降低,实现什么职责都有清晰明确的定义;

    ● 可读性提高,复杂性降低,那当然可读性提高了; 

    ● 可维护性提高,可读性提高,那当然更容易维护了; 

    ●变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。

     

    ps:接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。

           单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。

    ● Liskov Substitution Principle:里氏替换原则

    定义:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

    (所有引用基类的地方必须能透明地使用其子类的对象。)

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

    定义中包含的四层含义:

    1.子类必须完全实现父类的方法

    2.子类可以有自己的个性

    3.覆盖或实现父类的方法时输入参数可以被放大

            如果父类的输入参数类型大于子类的输入参数类型,会出现父类存在的地方,子类未必会存在,因为一旦把子类作为参数传入,调用者很可能进入子类的方法范畴。

     

    4. 覆写或实现父类的方法时输出结果可以被缩小

          父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类。

    ● Interface Segregation Principle:接口隔离原则

     

    接口分为两种:

    实例接口(Object Interface):Java中的类也是一种接口

    类接口(Class Interface): Java中经常使用Interface关键字定义的接口

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

    接口隔离原则与单一职责原则的不同:接口隔离原则与单一职责的审视角度是不相同的,单一职责要求的是类和接口职责单一,注重的是职责,这是业务逻辑上的划分,而接口隔离原则要求接口的方法尽量少。


    ● Dependence Inversion Principle:依赖倒置原则

    原始定义:

    ①高层模块不应该依赖低层模块,两者都应该依赖其抽象; 

    ②抽象不应该依赖细节(实现类);  

    ③细节应该依赖抽象。

    依赖倒置原则在java语言中的体现:

    ①模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的;

    ②接口或抽象类不依赖于实现类;

    ③实现类依赖接口或抽象类。

    依赖的三种写法

    ①构造函数传递依赖对象(构造函数注入)

    ②Setter方法传递依赖对象(setter依赖注入)

    ③接口声明依赖对象(接口注入)

    使用原则:

    依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合,我们怎么在项目中使用这个规则呢?只要遵循以下的几个规则就可以:

    ①每个类尽量都有接口或抽象类,或者抽象类和接口两者都具备

    ②变量的表面类型尽量是接口或者是抽象类

    ③任何类都不应该从具体类派生(只要不超过两层的继承是可以忍受的)

    ④尽量不要复写基类的方法

    ⑤结合里氏替换原则使用

     

     

    ●Open Closed Principle:开闭原则

    定义:软件实体应该对扩展开放,对修改关闭。

    其含义是说一个软件实体应该通过扩展来实现变化,而不是通过修改已有的代码来实现变化。

    软件实体:项目或软件产品中按照一定的逻辑规则划分的模块、抽象和类、方法。

    变化的三种类型:

    ①逻辑变化 

    只变化一个逻辑,而不涉及其他模块,比如原有的一个算法是a*b+c,现在需要修改为a*b*c,可以通过修改原有类中的方法的方式来完成,前提条件是所有依赖或关联类都按照相同的逻辑处理。

    ②子模块变化 

    一个模块变化,会对其他的模块产生影响,特别是一个低层次的模块变化必然引起高层模块的变化,因此在通过扩展完成变化时,高层次的模块修改是必然的。

    ③可见视图变化

    可见视图是提供给客户使用的界面,如JSP程序、Swing界面等,该部分的变化一般会引起连锁反应(特别是在国内做项目,做欧美的外包项目一般不会影响太大)。可以通过扩展来完成变化,这要看我们原有的设计是否灵活。

     

    展开全文
  • 设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式...
  • 常见的几种设计模式

    2019-02-28 09:45:27
    文章目录单例模式为什么会有单例设计模式?应用spring中IOC解决的问题设计思想工厂模式简单工厂模式工厂方法模式观察者模式观察者模式的定义装饰模式模板方法定义:主要的作用:优点:缺点:应用场景:适配器模式...
  • 本文由 玉刚说写作平台 提供写作赞助 原作者:却把清梅嗅 ... 版权声明:本文版权归微信公众号 玉刚...数据结构,算法,设计模式被认为是程序员必备技能的三叉戟,如果说编程语言的语法特性和业务编码能力是【术】,...
  • 根据对设计模式的学习,总结出各类设计模式的使用场景,了解哪些场景下适合使用哪种设计模式来解决该场景的问题,这样才能学而致用,仅仅了解设计模式但不能实践那学了又有什么用呢?下面来看看各种设计模式的使用...
  • 设计模式(一):策略模式 本文将以一个小Demo及其新需求来分析使用策略模式的好处。 设计模式简述: 设计模式: 1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用的经验。模式不是代码,而是某类...
  • 本文继续介绍23种设计模式系列之单例模式。 概念:  java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类...
  • 如何让孩子爱上设计模式 ——14.策略模式(Strategy Pattern)标签: 设计模式初涉描述性文字本节讲解的是行为型设计模式中的第一个模式: 策略模式, 这个模式非常简单,也很好理解。 定义一系列的算法,把每个算法...
  • 几种常用的设计模式介绍 1.  设计模式的起源 最早提出“设计模式”概念的是建筑设计大师亚力山大Alexander。在1970年他的《建筑的永恒之道》里描述了投计模式的发现,因为它已经存在了千百年之久,而现代才被...
  • 设计模式(Design Patterns) ——可复用面向对象软件的基础设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人...
  • 如何让孩子爱上设计模式 ——23.状态模式(State Pattern)标签: 设计模式初涉描述性文字分离状态,选择实现定义当一个对象的内在状态发生改变时允许改变其行为,这个对象看起来像是改变了它的类三个角色 Context:上...
  • 一、设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式...
  • 快速过一过设计模式 —— 3.建造者模式(Builder Pattern)标签: 设计模式初涉应用场景将复杂对象的构建与它的表示分离开来,使得同样的构建过程可以 创建不同的表示。举个简单例子:自定义游戏角色时,游戏角色由...
  •  2012年-2013年,Sunny在CSDN技术博客中陆续发表了100多篇与设计模式相关的文章,涵盖了七个面向对象设计原则和24个设计模式(23个GoF设计模式 + 简单工厂模式),为了方便大家学习,现将所有与设计模式学习相关...
1 2 3 4 5 ... 20
收藏数 1,620,901
精华内容 648,360
关键字:

设计模式