精华内容
下载资源
问答
  • 二十三种设计模式
    千次阅读
    2017-08-09 23:40:35

    java 23种设计模式 深入理解
    以下是学习过程中查询的资料,别人总结的资料,比较容易理解(站在各位巨人的肩膀上,望博主勿究)

    创建型
    抽象工厂模式 http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html
    工厂方法 http://www.cnblogs.com/java-my-life/archive/2012/03/25/2416227.html
    建造者模式 http://www.cnblogs.com/java-my-life/archive/2012/04/07/2433939.html
    原型模式 http://www.cnblogs.com/java-my-life/archive/2012/04/11/2439387.html
    单态模式 http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html

    结构型
    适配器模式 http://www.cnblogs.com/java-my-life/archive/2012/04/13/2442795.html
    桥接模式 http://blog.csdn.net/jason0539/article/details/22568865
    组合模式 http://blog.csdn.net/jason0539/article/details/22642281
    外观模式 http://blog.csdn.net/jason0539/article/details/22775311
    装饰者模式 http://www.cnblogs.com/java-my-life/archive/2012/04/20/2455726.html
    享元模式 http://www.cnblogs.com/java-my-life/archive/2012/04/26/2468499.html
    代理模式 http://www.cnblogs.com/java-my-life/archive/2012/04/23/2466712.html

    行为型
    责任链模式 http://blog.csdn.net/zhouyong0/article/details/7909456
    命令模式 http://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html
    解释器模式 http://www.cnblogs.com/java-my-life/archive/2012/06/19/2552617.html
    迭代模式 http://www.cnblogs.com/java-my-life/archive/2012/05/22/2511506.html
    中介者模式 http://blog.csdn.net/chenhuade85/article/details/8141831
    备忘录模式 http://www.cnblogs.com/java-my-life/archive/2012/06/06/2534942.html
    观察者模式 http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html
    状态模式 http://www.cnblogs.com/java-my-life/archive/2012/06/08/2538146.html
    策略模式 http://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html
    模板方法模式 http://www.cnblogs.com/java-my-life/archive/2012/05/14/2495235.html
    访问者模式 http://www.cnblogs.com/java-my-life/archive/2012/06/14/2545381.html

    更多相关内容
  • 二十三种设计模式(白话版)

    千次阅读 2018-01-16 18:11:57
    二十三种设计模式(白话版) tips:作者以尽量精简的语言让大家得到对二十三种设计模式的理解,但是对于这些设计模式,还是得多敲多看代码才能领悟其中的精髓! ================================================...

    二十三种设计模式(白话版)

    tips:作者以尽量精简的语言让大家得到对二十三种设计模式的理解,但是对于这些设计模式,还是得多敲多看代码才能领悟其中的精髓!

    ======================================================================

    一、设计模式的分类

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

    创建型模式,顾名思义:把new的过程交给第三方来帮忙创建!共五种:

    工厂方法模式:A、B类交给C类创建,C类按条件去new他们.

    抽象工厂模式:A、B类均有对应的工厂类去创建,而工厂类实现自同一接口!优点:当有C类加入时,只需要再增加一个工厂类,扩展性强!

    单例模式:在内存中只存在一个对象,1.恶汉式:在调用时已经new好,2.懒汉式:访问才加载,为了避免多线程问题需要加锁!

    建造者模式:增强版工厂,可以建造更复杂的对象、控制个数,eg:需要A类1个、B类4个,此时运用建造者模式更妥!

    原型模式:简而言之就是克隆,A类就是实现Cloneable接口的clone方法调用super.clone(this)便可以实现浅拷贝,也可以实现Serializable接口将this用流的方式进行深拷贝!
    深浅拷贝传送门

    ======================================================================

    结构型模式,共七种:

    适配器模式
    问题:A类中的B方法返回值是C类,一个接口D的E方法的入口是F类?
    答:此时,创建一个G类(适配类)继承A类的实现D接口,得到了A的方法,也能实现D的方法!适配器就像转接口把二项转成三项接入!

    装饰器模式:A类被装饰者,B类持有A类引用,B类能在A类调用方法前后做一些事情,并且能够动态取消这些事情,返回A类对象。此时A类对象已经被装饰过了!

    代理模式:这个模式大家并不会陌生,proxy代理对象持有A的引用,并在A调用方法前后做事情,并返回A对象。感觉跟装饰者模式没区别?有什么不一样请大神指点指点。

    外观模式:一个调用过程需要A、B、C三个类,ABC又会相互持有对方,这时候,不如用D类持有ABC三个类,既能降低ABC的耦合程度,又能将过程统一归类!

    桥接模式:A、B、C均继承D接口,此时就可以用E类来承接D接口,并且做一些均有的特性。通过放入的实现类的不一样,就可以调用不用的具体实现方法!

    组合模式:目标A.B两类,C类持有A.B类引用,所以C为组合类!

    享元模式:共享池,A类维护一个List,当使用B时,直接在A类取,用完还能放回去!

    ======================================================================

    行为型模式,共十一种:

    策略模式:A.B.C三类均实现了D接口,用户可以根据喜好,选择不同的类,只要NEW不一样的就OK,接口都是一样的!

    模板方法模式:抽象类A实现了B方法,并且在B方法调用了C方法(未实现),实体类D继承A重写C方法,调用方只要调用B方法边可以根据抽象类A的B模板方法去调用具体实现类的C方法!

    观察者模式:类似监视的功能,当具体类A操作的时候,调用相应的观察者接口B。

    迭代子模式:类似集合迭代器,区别就是新建类B迭代器来入口就是集合A,而集合是使用内部类去实现!

    责任链模式:A.B.C相互引用,最后调用者不知道谁调用谁,不知道这个过程,所以用D类持有A.B.C的接口类,提供get,set和相应的方法,所以只要new D类,并将要调用的类set进去并调用就可以得到调用顺序。

    命令模式:将请求和执行者解耦,执行者A类,B类持有A类对象实现C命令行接口拥有D方法可以生成命令行到具体的类中,E类具体执行者持有C类接口引用,调用其具体方法!具体:new A类,new B类并将A类set进去,new E类,执行方法!命令可以有很多种,调用方法也可以很多!

    备忘录模式:目标类A,在类B中持有A引用。当想恢复时,直接去B类中还原A。

    状态模式:红灯A,绿灯B,均继承一个接口C,类D持有接口C提供set,get方法,此时只要new A,B,D,就可以在类D中为所欲为了。

    访问者模式:目标类A,类B中C方法有A的入口,放入A就能调用A的方法,适合不改变A类的场景!

    中介者模式:目标A.B,类C持有A.B引用,并能实例化,调用响应方法进行工作!类C只是提供A.B的工作.

    解释器模式:比如正则表达式的解释器!

    ======================================================================

    二、设计模式的六大原则

    1、开闭原则
    开闭原则就是说对扩展开放,对修改关闭;想要达到这样的效果,我们需要使用接口和抽象类。

    2、里氏代换原则
    任何基类可以出现的地方,子类一定可以出现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

    3、依赖倒转原则
    真对接口编程,依赖于抽象而不依赖于具体。SPRING的DI

    4、接口隔离原则
    使用多个隔离的接口,比使用单个接口要好。

    5、迪米特法则(最少知道原则)
    一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

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

    展开全文
  • [设计模式]二十三种设计模式

    万次阅读 2016-08-09 23:54:31
    分类: ...总体来说设计模式分为大类: 创建型模式,共五:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七:适配器模式、装饰器模式、代理模式、外观模
      分类:

    目录(?)[+]

    原文地址:http://blog.csdn.net/zhangerqing

    一、设计模式的分类

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

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

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

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

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

     

    二、设计模式的六大原则

    总原则:开闭原则(Open Close Principle)

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

    1、单一职责原则

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


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

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

    历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。


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

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


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

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


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

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

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


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

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


    三、Java的23中设计模式

    A、创建模式

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


    首先,简单工厂模式不属于23中涉及模式,简单工厂一般分为:普通简单工厂、多方法简单工厂、静态方法简单工厂。

    0、简单工厂模式

    简单工厂模式模式分为三种:

    01、普通

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

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

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

    [java]  view plain copy
    1. public interface Sender {  
    2.     public void Send();  
    3. }  

    其次,创建实现类:

    [java]  view plain copy
    1. public class MailSender implements Sender {  
    2.     @Override  
    3.     public void Send() {  
    4.         System.out.println("this is mailsender!");  
    5.     }  
    6. }  
    [java]  view plain copy
    1. public class SmsSender implements Sender {  
    2.   
    3.     @Override  
    4.     public void Send() {  
    5.         System.out.println("this is sms sender!");  
    6.     }  
    7. }  

    最后,建工厂类:

    [java]  view plain copy
    1. public class SendFactory {  
    2.   
    3.     public Sender produce(String type) {  
    4.         if ("mail".equals(type)) {  
    5.             return new MailSender();  
    6.         } else if ("sms".equals(type)) {  
    7.             return new SmsSender();  
    8.         } else {  
    9.             System.out.println("请输入正确的类型!");  
    10.             return null;  
    11.         }  
    12.     }  
    13. }  

    我们来测试下:

    1. public class FactoryTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         SendFactory factory = new SendFactory();  
    5.         Sender sender = factory.produce("sms");  
    6.         sender.Send();  
    7.     }  
    8. }  

    输出:this is sms sender!

    02、多个方法

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

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

    [java]  view plain copy public   class  SendFactory {  
        public  Sender produceMail(){  
    1.         return new MailSender();  
    2.     }  
    3.       
    4.     public Sender produceSms(){  
    5.         return new SmsSender();  
    6.     }  
    7. }  

    测试类如下:

    [java]  view plain copy
    1. public class FactoryTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         SendFactory factory = new SendFactory();  
    5.         Sender sender = factory.produceMail();  
    6.         sender.Send();  
    7.     }  
    8. }  

    输出:this is mailsender!

    03、多个静态方法

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

    [java]  view plain copy
    1. public class SendFactory {  
    2.       
    3.     public static Sender produceMail(){  
    4.         return new MailSender();  
    5.     }  
    6.       
    7.     public static Sender produceSms(){  
    8.         return new SmsSender();  
    9.     }  
    10. }  
    [java]  view plain copy
    1. public class FactoryTest {  
    2.   
    3.     public static void main(String[] args) {      
    4.         Sender sender = SendFactory.produceMail();  
    5.         sender.Send();  
    6.     }  
    7. }  

    输出:this is mailsender!

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


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

    简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

    请看例子:

    [java]  view plain copy
    1. public interface Sender {  
    2.     public void Send();  
    3. }  

    两个实现类:

    [java]  view plain copy
    1. public class MailSender implements Sender {  
    2.     @Override  
    3.     public void Send() {  
    4.         System.out.println("this is mailsender!");  
    5.     }  
    6. }  
    [java]  view plain copy
    1. public class SmsSender implements Sender {  
    2.   
    3.     @Override  
    4.     public void Send() {  
    5.         System.out.println("this is sms sender!");  
    6.     }  
    7. }  

    两个工厂类:

    [java]  view plain copy
    1. public class SendMailFactory implements Provider {  
    2.       
    3.     @Override  
    4.     public Sender produce(){  
    5.         return new MailSender();  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class SendSmsFactory implements Provider{  
    2.   
    3.     @Override  
    4.     public Sender produce() {  
    5.         return new SmsSender();  
    6.     }  
    7. }  

    在提供一个接口:

    [java]  view plain copy
    1. public interface Provider {  
    2.     public Sender produce();  
    3. }  

    测试类:

    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Provider provider = new SendMailFactory();  
    5.         Sender sender = provider.produce();  
    6.         sender.Send();  
    7.     }  
    8. }  

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



    2、抽象工厂模式

    工厂方法模式和抽象工厂模式不好分清楚,他们的区别如下:

    <span style="background-color: rgb(255, 255, 255);">工厂方法模式:
    一个抽象产品类,可以派生出多个具体产品类。   
    一个抽象工厂类,可以派生出多个具体工厂类。   
    每个具体工厂类只能创建一个具体产品类的实例。
    
    抽象工厂模式:
    多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。   
    一个抽象工厂类,可以派生出多个具体工厂类。   
    每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品。   
        
    区别:
    工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。   
    工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。</span>
    <span style="background-color: rgb(255, 255, 255);"><span style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px;">工厂方法创建 "一种" 产品,他的着重点在于"怎么创建",也就是说如果你开发,你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随。 </span><br style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px;" /><br style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px;" /><span style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px;">抽象工厂需要创建一些列产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承。</span></span>

    对于java来说,你能见到的大部分抽象工厂模式都是这样的:
    ---它的里面是一堆工厂方法,每个工厂方法返回某种类型的对象。
    
    比如说工厂可以生产鼠标和键盘。那么抽象工厂的实现类(它的某个具体子类)的对象都可以生产鼠标和键盘,但可能工厂A生产的是罗技的键盘和鼠标,工厂B是微软的。
    
    这样A和B就是工厂,对应于抽象工厂;
    每个工厂生产的鼠标和键盘就是产品,对应于工厂方法;
    
    用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从罗技换到微软。但是用了抽象工厂模式,你只要换家工厂,就可以同时替换鼠标和键盘一套。如果你要的产品有几十个,当然用抽象工厂模式一次替换全部最方便(这个工厂会替你用相应的工厂方法)
    
    所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线


    3、单例模式(Singleton

    单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:

    1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

    2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。

    3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

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

    [java]  view plain copy
    1. public class Singleton {  
    2.   
    3.     /* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */  
    4.     private static Singleton instance = null;  
    5.   
    6.     /* 私有构造方法,防止被实例化 */  
    7.     private Singleton() {  
    8.     }  
    9.   
    10.     /* 静态工程方法,创建实例 */  
    11.     public static Singleton getInstance() {  
    12.         if (instance == null) {  
    13.             instance = new Singleton();  
    14.         }  
    15.         return instance;  
    16.     }  
    17.   
    18.     /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
    19.     public Object readResolve() {  
    20.         return instance;  
    21.     }  
    22. }  


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

    [java]  view plain copy
    1. public static synchronized Singleton getInstance() {  
    2.         if (instance == null) {  
    3.             instance = new Singleton();  
    4.         }  
    5.         return instance;  
    6.     }  

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

    [java]  view plain copy
    1. public static Singleton getInstance() {  
    2.         if (instance == null) {  
    3.             synchronized (instance) {  
    4.                 if (instance == null) {  
    5.                     instance = new Singleton();  
    6.                 }  
    7.             }  
    8.         }  
    9.         return instance;  
    10.     }  

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

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

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

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

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

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

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

    [java]  view plain copy
    1. private static class SingletonFactory{           
    2.         private static Singleton instance = new Singleton();           
    3.     }           
    4.     public static Singleton getInstance(){           
    5.         return SingletonFactory.instance;           
    6.     }   

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

    [java]  view plain copy
    1. public class Singleton {  
    2.   
    3.     /* 私有构造方法,防止被实例化 */  
    4.     private Singleton() {  
    5.     }  
    6.   
    7.     /* 此处使用一个内部类来维护单例 */  
    8.     private static class SingletonFactory {  
    9.         private static Singleton instance = new Singleton();  
    10.     }  
    11.   
    12.     /* 获取实例 */  
    13.     public static Singleton getInstance() {  
    14.         return SingletonFactory.instance;  
    15.     }  
    16.   
    17.     /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
    18.     public Object readResolve() {  
    19.         return getInstance();  
    20.     }  
    21. }  

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

    [java]  view plain copy
    1. public class SingletonTest {  
    2.   
    3.     private static SingletonTest instance = null;  
    4.   
    5.     private SingletonTest() {  
    6.     }  
    7.   
    8.     private static synchronized void syncInit() {  
    9.         if (instance == null) {  
    10.             instance = new SingletonTest();  
    11.         }  
    12.     }  
    13.   
    14.     public static SingletonTest getInstance() {  
    15.         if (instance == null) {  
    16.             syncInit();  
    17.         }  
    18.         return instance;  
    19.     }  
    20. }  

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

    补充:采用"影子实例"的办法为单例对象的属性同步更新

    [java]  view plain copy
    1. public class SingletonTest {  
    2.   
    3.     private static SingletonTest instance = null;  
    4.     private Vector properties = null;  
    5.   
    6.     public Vector getProperties() {  
    7.         return properties;  
    8.     }  
    9.   
    10.     private SingletonTest() {  
    11.     }  
    12.   
    13.     private static synchronized void syncInit() {  
    14.         if (instance == null) {  
    15.             instance = new SingletonTest();  
    16.         }  
    17.     }  
    18.   
    19.     public static SingletonTest getInstance() {  
    20.         if (instance == null) {  
    21.             syncInit();  
    22.         }  
    23.         return instance;  
    24.     }  
    25.   
    26.     public void updateProperties() {  
    27.         SingletonTest shadow = new SingletonTest();  
    28.         properties = shadow.getProperties();  
    29.     }  
    30. }  

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

    1、单例模式理解起来简单,但是具体实现起来还是有一定的难度。

    2、synchronized关键字锁定的是对象,在用的时候,一定要在恰当的地方使用(注意需要使用锁的对象和过程,可能有的时候并不是整个对象及整个过程都需要锁)。

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

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

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

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

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


    4、建造者模式(Builder)


    5、原型模式(Prototype)

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

    [java]  view plain copy
    1. public class Prototype implements Cloneable {  
    2.   
    3.     public Object clone() throws CloneNotSupportedException {  
    4.         Prototype proto = (Prototype) super.clone();  
    5.         return proto;  
    6.     }  
    7. }  

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

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

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

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

    [java]  view plain copy
    1. public class Prototype implements Cloneable, Serializable {  
    2.   
    3.     private static final long serialVersionUID = 1L;  
    4.     private String string;  
    5.   
    6.     private SerializableObject obj;  
    7.   
    8.     /* 浅复制 */  
    9.     public Object clone() throws CloneNotSupportedException {  
    10.         Prototype proto = (Prototype) super.clone();  
    11.         return proto;  
    12.     }  
    13.   
    14.     /* 深复制 */  
    15.     public Object deepClone() throws IOException, ClassNotFoundException {  
    16.   
    17.         /* 写入当前对象的二进制流 */  
    18.         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
    19.         ObjectOutputStream oos = new ObjectOutputStream(bos);  
    20.         oos.writeObject(this);  
    21.   
    22.         /* 读出二进制流产生的新对象 */  
    23.         ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
    24.         ObjectInputStream ois = new ObjectInputStream(bis);  
    25.         return ois.readObject();  
    26.     }  
    27.   
    28.     public String getString() {  
    29.         return string;  
    30.     }  
    31.   
    32.     public void setString(String string) {  
    33.         this.string = string;  
    34.     }  
    35.   
    36.     public SerializableObject getObj() {  
    37.         return obj;  
    38.     }  
    39.   
    40.     public void setObj(SerializableObject obj) {  
    41.         this.obj = obj;  
    42.     }  
    43.   
    44. }  
    45.   
    46. class SerializableObject implements Serializable {  
    47.     private static final long serialVersionUID = 1L;  
    48. }  
     
    要实现深复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。



    B、结构模式(7种)


    我们接着讨论设计模式,上篇文章我讲完了5种创建型模式,这章开始,我将讲下7种结构型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。其中对象的适配器模式是各种模式的起源,我们看下面的图:



    6、适配器模式

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

    01、类的适配器模式

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

    [java]  view plain copy
    1. public class Source {  
    2.   
    3.     public void method1() {  
    4.         System.out.println("this is original method!");  
    5.     }  
    6. }  
    [java]  view plain copy
    1. public interface Targetable {  
    2.   
    3.     /* 与原类中的方法相同 */  
    4.     public void method1();  
    5.   
    6.     /* 新类的方法 */  
    7.     public void method2();  
    8. }  
    [java]  view plain copy
    1. public class Adapter extends Source implements Targetable {  
    2.   
    3.     @Override  
    4.     public void method2() {  
    5.         System.out.println("this is the targetable method!");  
    6.     }  
    7. }  

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

    [java]  view plain copy
    1. public class AdapterTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Targetable target = new Adapter();  
    5.         target.method1();  
    6.         target.method2();  
    7.     }  
    8. }  

    输出:

    this is original method!
    this is the targetable method!

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

    02、对象的适配器模式

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

     

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

    [java]  view plain copy
    1. public class Wrapper implements Targetable {  
    2.   
    3.     private Source source;  
    4.       
    5.     public Wrapper(Source source){  
    6.         super();  
    7.         this.source = source;  
    8.     }  
    9.     @Override  
    10.     public void method2() {  
    11.         System.out.println("this is the targetable method!");  
    12.     }  
    13.   
    14.     @Override  
    15.     public void method1() {  
    16.         source.method1();  
    17.     }  
    18. }  

    测试类:

    [java]  view plain copy
    1. public class AdapterTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Source source = new Source();  
    5.         Targetable target = new Wrapper(source);  
    6.         target.method1();  
    7.         target.method2();  
    8.     }  
    9. }  

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


    03、接口的适配器模式

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

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

    [java]  view plain copy
    1. public interface Sourceable {  
    2.       
    3.     public void method1();  
    4.     public void method2();  
    5. }  

    抽象类Wrapper2:

    [java]  view plain copy
    1. public abstract class Wrapper2 implements Sourceable{  
    2.       
    3.     public void method1(){}  
    4.     public void method2(){}  
    5. }  
    [java]  view plain copy
    1. public class SourceSub1 extends Wrapper2 {  
    2.     public void method1(){  
    3.         System.out.println("the sourceable interface's first Sub1!");  
    4.     }  
    5. }  
    [java]  view plain copy
    1. public class SourceSub2 extends Wrapper2 {  
    2.     public void method2(){  
    3.         System.out.println("the sourceable interface's second Sub2!");  
    4.     }  
    5. }  
    [java]  view plain copy
    1. public class WrapperTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Sourceable source1 = new SourceSub1();  
    5.         Sourceable source2 = new SourceSub2();  
    6.           
    7.         source1.method1();  
    8.         source1.method2();  
    9.         source2.method1();  
    10.         source2.method2();  
    11.     }  
    12. }  

    测试输出:

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

    达到了我们的效果!

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

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

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

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



    7、装饰模式(Decorator)

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

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

    [java]  view plain copy
    1. public interface Sourceable {  
    2.     public void method();  
    3. }  
    [java]  view plain copy
    1. public class Source implements Sourceable {  
    2.   
    3.     @Override  
    4.     public void method() {  
    5.         System.out.println("the original method!");  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class Decorator implements Sourceable {  
    2.   
    3.     private Sourceable source;  
    4.       
    5.     public Decorator(Sourceable source){  
    6.         super();  
    7.         this.source = source;  
    8.     }  
    9.     @Override  
    10.     public void method() {  
    11.         System.out.println("before decorator!");  
    12.         source.method();  
    13.         System.out.println("after decorator!");  
    14.     }  
    15. }  

    测试类:

    [java]  view plain copy
    1. public class DecoratorTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Sourceable source = new Source();  
    5.         Sourceable obj = new Decorator(source);  
    6.         obj.method();  
    7.     }  
    8. }  

    输出:

    before decorator!
    the original method!
    after decorator!

    装饰器模式的应用场景:

    1、需要扩展一个类的功能。

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

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



    8、代理模式(Proxy)

    其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。先来看看关系图:

     

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

    [java]  view plain copy
    1. public interface Sourceable {  
    2.     public void method();  
    3. }  
    [java]  view plain copy
    1. public class Source implements Sourceable {  
    2.   
    3.     @Override  
    4.     public void method() {  
    5.         System.out.println("the original method!");  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class Proxy implements Sourceable {  
    2.   
    3.     private Source source;  
    4.     public Proxy(){  
    5.         super();  
    6.         this.source = new Source();  
    7.     }  
    8.     @Override  
    9.     public void method() {  
    10.         before();  
    11.         source.method();  
    12.         atfer();  
    13.     }  
    14.     private void atfer() {  
    15.         System.out.println("after proxy!");  
    16.     }  
    17.     private void before() {  
    18.         System.out.println("before proxy!");  
    19.     }  
    20. }  

    测试类:

    [java]  view plain copy
    1. public class ProxyTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Sourceable source = new Proxy();  
    5.         source.method();  
    6.     }  
    7.   
    8. }  

    输出:

    before proxy!
    the original method!
    after proxy!

    代理模式的应用场景:

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

    1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

    2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

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



    9、外观模式(Facade)

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

    我们先看下实现类:

    [java]  view plain copy
    1. public class CPU {  
    2.       
    3.     public void startup(){  
    4.         System.out.println("cpu startup!");  
    5.     }  
    6.       
    7.     public void shutdown(){  
    8.         System.out.println("cpu shutdown!");  
    9.     }  
    10. }  
    [java]  view plain copy
    1. public class Memory {  
    2.       
    3.     public void startup(){  
    4.         System.out.println("memory startup!");  
    5.     }  
    6.       
    7.     public void shutdown(){  
    8.         System.out.println("memory shutdown!");  
    9.     }  
    10. }  
    [java]  view plain copy
    1. public class Disk {  
    2.       
    3.     public void startup(){  
    4.         System.out.println("disk startup!");  
    5.     }  
    6.       
    7.     public void shutdown(){  
    8.         System.out.println("disk shutdown!");  
    9.     }  
    10. }  
    [java]  view plain copy
    1. public class Computer {  
    2.     private CPU cpu;  
    3.     private Memory memory;  
    4.     private Disk disk;  
    5.       
    6.     public Computer(){  
    7.         cpu = new CPU();  
    8.         memory = new Memory();  
    9.         disk = new Disk();  
    10.     }  
    11.       
    12.     public void startup(){  
    13.         System.out.println("start the computer!");  
    14.         cpu.startup();  
    15.         memory.startup();  
    16.         disk.startup();  
    17.         System.out.println("start computer finished!");  
    18.     }  
    19.       
    20.     public void shutdown(){  
    21.         System.out.println("begin to close the computer!");  
    22.         cpu.shutdown();  
    23.         memory.shutdown();  
    24.         disk.shutdown();  
    25.         System.out.println("computer closed!");  
    26.     }  
    27. }  

    User类如下:

    [java]  view plain copy
    1. public class User {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Computer computer = new Computer();  
    5.         computer.startup();  
    6.         computer.shutdown();  
    7.     }  
    8. }  

    输出:

    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类里,这样就起到了解耦的作用,这,就是外观模式!


    10、桥接模式(Bridge)

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

    实现代码:

    先定义接口:

    [java]  view plain copy
    1. public interface Sourceable {  
    2.     public void method();  
    3. }  

    分别定义两个实现类:

    [java]  view plain copy
    1. public class SourceSub1 implements Sourceable {  
    2.   
    3.     @Override  
    4.     public void method() {  
    5.         System.out.println("this is the first sub!");  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class SourceSub2 implements Sourceable {  
    2.   
    3.     @Override  
    4.     public void method() {  
    5.         System.out.println("this is the second sub!");  
    6.     }  
    7. }  

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

    [java]  view plain copy
    1. public abstract class Bridge {  
    2.     private Sourceable source;  
    3.   
    4.     public void method(){  
    5.         source.method();  
    6.     }  
    7.       
    8.     public Sourceable getSource() {  
    9.         return source;  
    10.     }  
    11.   
    12.     public void setSource(Sourceable source) {  
    13.         this.source = source;  
    14.     }  
    15. }  
    [java]  view plain copy
    1. public class MyBridge extends Bridge {  
    2.     public void method(){  
    3.         getSource().method();  
    4.     }  
    5. }  

    测试类:

    [java]  view plain copy
    1. public class BridgeTest {  
    2.       
    3.     public static void main(String[] args) {  
    4.           
    5.         Bridge bridge = new MyBridge();  
    6.           
    7.         /*调用第一个对象*/  
    8.         Sourceable source1 = new SourceSub1();  
    9.         bridge.setSource(source1);  
    10.         bridge.method();  
    11.           
    12.         /*调用第二个对象*/  
    13.         Sourceable source2 = new SourceSub2();  
    14.         bridge.setSource(source2);  
    15.         bridge.method();  
    16.     }  
    17. }  

    output:

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

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



    11、组合模式(Composite)

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

    直接来看代码:

    [java]  view plain copy
    1. public class TreeNode {  
    2.       
    3.     private String name;  
    4.     private TreeNode parent;  
    5.     private Vector<TreeNode> children = new Vector<TreeNode>();  
    6.       
    7.     public TreeNode(String name){  
    8.         this.name = name;  
    9.     }  
    10.   
    11.     public String getName() {  
    12.         return name;  
    13.     }  
    14.   
    15.     public void setName(String name) {  
    16.         this.name = name;  
    17.     }  
    18.   
    19.     public TreeNode getParent() {  
    20.         return parent;  
    21.     }  
    22.   
    23.     public void setParent(TreeNode parent) {  
    24.         this.parent = parent;  
    25.     }  
    26.       
    27.     //添加孩子节点  
    28.     public void add(TreeNode node){  
    29.         children.add(node);  
    30.     }  
    31.       
    32.     //删除孩子节点  
    33.     public void remove(TreeNode node){  
    34.         children.remove(node);  
    35.     }  
    36.       
    37.     //取得孩子节点  
    38.     public Enumeration<TreeNode> getChildren(){  
    39.         return children.elements();  
    40.     }  
    41. }  
    [java]  view plain copy
    1. public class Tree {  
    2.   
    3.     TreeNode root = null;  
    4.   
    5.     public Tree(String name) {  
    6.         root = new TreeNode(name);  
    7.     }  
    8.   
    9.     public static void main(String[] args) {  
    10.         Tree tree = new Tree("A");  
    11.         TreeNode nodeB = new TreeNode("B");  
    12.         TreeNode nodeC = new TreeNode("C");  
    13.           
    14.         nodeB.add(nodeC);  
    15.         tree.root.add(nodeB);  
    16.         System.out.println("build the tree finished!");  
    17.     }  
    18. }  

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



    12、享元模式(Flyweight)

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

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

    看个例子:

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

    [java]  view plain copy
    1. public class ConnectionPool {  
    2.       
    3.     private Vector<Connection> pool;  
    4.       
    5.     /*公有属性*/  
    6.     private String url = "jdbc:mysql://localhost:3306/test";  
    7.     private String username = "root";  
    8.     private String password = "root";  
    9.     private String driverClassName = "com.mysql.jdbc.Driver";  
    10.   
    11.     private int poolSize = 100;  
    12.     private static ConnectionPool instance = null;  
    13.     Connection conn = null;  
    14.   
    15.     /*构造方法,做一些初始化工作*/  
    16.     private ConnectionPool() {  
    17.         pool = new Vector<Connection>(poolSize);  
    18.   
    19.         for (int i = 0; i < poolSize; i++) {  
    20.             try {  
    21.                 Class.forName(driverClassName);  
    22.                 conn = DriverManager.getConnection(url, username, password);  
    23.                 pool.add(conn);  
    24.             } catch (ClassNotFoundException e) {  
    25.                 e.printStackTrace();  
    26.             } catch (SQLException e) {  
    27.                 e.printStackTrace();  
    28.             }  
    29.         }  
    30.     }  
    31.   
    32.     /* 返回连接到连接池 */  
    33.     public synchronized void release() {  
    34.         pool.add(conn);  
    35.     }  
    36.   
    37.     /* 返回连接池中的一个数据库连接 */  
    38.     public synchronized Connection getConnection() {  
    39.         if (pool.size() > 0) {  
    40.             Connection conn = pool.get(0);  
    41.             pool.remove(conn);  
    42.             return conn;  
    43.         } else {  
    44.             return null;  
    45.         }  
    46.     }  
    47. }  
     
    通过连接池的管理,实现了数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的性能!

     

     C、关系模式(11种)

    先来张图,看看这11中模式的关系:

    第一类:通过父类与子类的关系进行实现。

    第二类:两个类之间。

    第三类:类的状态。

    第四类:通过中间类


    父类与子类关系

    13、策略模式(strategy)

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

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

    首先统一接口:

    [java]  view plain copy
    1. public interface ICalculator {  
    2.     public int calculate(String exp);  
    3. }  

    辅助类:

    [java]  view plain copy
    1. public abstract class AbstractCalculator {  
    2.       
    3.     public int[] split(String exp,String opt){  
    4.         String array[] = exp.split(opt);  
    5.         int arrayInt[] = new int[2];  
    6.         arrayInt[0] = Integer.parseInt(array[0]);  
    7.         arrayInt[1] = Integer.parseInt(array[1]);  
    8.         return arrayInt;  
    9.     }  
    10. }  

    三个实现类:

    [java]  view plain copy
    1. public class Plus extends AbstractCalculator implements ICalculator {  
    2.   
    3.     @Override  
    4.     public int calculate(String exp) {  
    5.         int arrayInt[] = split(exp,"\\+");  
    6.         return arrayInt[0]+arrayInt[1];  
    7.     }  
    8. }  
    [java]  view plain copy
    1. public class Minus extends AbstractCalculator implements ICalculator {  
    2.   
    3.     @Override  
    4.     public int calculate(String exp) {  
    5.         int arrayInt[] = split(exp,"-");  
    6.         return arrayInt[0]-arrayInt[1];  
    7.     }  
    8.   
    9. }  
    [java]  view plain copy
    1. public class Multiply extends AbstractCalculator implements ICalculator {  
    2.   
    3.     @Override  
    4.     public int calculate(String exp) {  
    5.         int arrayInt[] = split(exp,"\\*");  
    6.         return arrayInt[0]*arrayInt[1];  
    7.     }  
    8. }  

    简单的测试类:

    [java]  view plain copy
    1. public class StrategyTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         String exp = "2+8";  
    5.         ICalculator cal = new Plus();  
    6.         int result = cal.calculate(exp);  
    7.         System.out.println(result);  
    8.     }  
    9. }  

    输出:10

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


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

    解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用,先看个关系图:

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

    [java]  view plain copy
    1. public abstract class AbstractCalculator {  
    2.       
    3.     /*主方法,实现对本类其它方法的调用*/  
    4.     public final int calculate(String exp,String opt){  
    5.         int array[] = split(exp,opt);  
    6.         return calculate(array[0],array[1]);  
    7.     }  
    8.       
    9.     /*被子类重写的方法*/  
    10.     abstract public int calculate(int num1,int num2);  
    11.       
    12.     public int[] split(String exp,String opt){  
    13.         String array[] = exp.split(opt);  
    14.         int arrayInt[] = new int[2];  
    15.         arrayInt[0] = Integer.parseInt(array[0]);  
    16.         arrayInt[1] = Integer.parseInt(array[1]);  
    17.         return arrayInt;  
    18.     }  
    19. }  
    [java]  view plain copy
    1. public class Plus extends AbstractCalculator {  
    2.   
    3.     @Override  
    4.     public int calculate(int num1,int num2) {  
    5.         return num1 + num2;  
    6.     }  
    7. }  

    测试类:

    [java]  view plain copy
    1. public class StrategyTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         String exp = "8+8";  
    5.         AbstractCalculator cal = new Plus();  
    6.         int result = cal.calculate(exp, "\\+");  
    7.         System.out.println(result);  
    8.     }  
    9. }  

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


    类之间的关系

    15、观察者模式(Observer)

    包括这个模式在内的接下来的四个模式,都是类和类之间的关系,不涉及到继承,学的时候应该 记得归纳,记得本文最开始的那个图。观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。先来看看关系图:

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

    一个Observer接口:

    [java]  view plain copy
    1. public interface Observer {  
    2.     public void update();  
    3. }  

    两个实现类:

    [java]  view plain copy
    1. public class Observer1 implements Observer {  
    2.   
    3.     @Override  
    4.     public void update() {  
    5.         System.out.println("observer1 has received!");  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class Observer2 implements Observer {  
    2.   
    3.     @Override  
    4.     public void update() {  
    5.         System.out.println("observer2 has received!");  
    6.     }  
    7.   
    8. }  

    Subject接口及实现类:

    [java]  view plain copy
    1. public interface Subject {  
    2.       
    3.     /*增加观察者*/  
    4.     public void add(Observer observer);  
    5.       
    6.     /*删除观察者*/  
    7.     public void del(Observer observer);  
    8.       
    9.     /*通知所有的观察者*/  
    10.     public void notifyObservers();  
    11.       
    12.     /*自身的操作*/  
    13.     public void operation();  
    14. }  
    [java]  view plain copy
    1. public abstract class AbstractSubject implements Subject {  
    2.   
    3.     private Vector<Observer> vector = new Vector<Observer>();  
    4.     @Override  
    5.     public void add(Observer observer) {  
    6.         vector.add(observer);  
    7.     }  
    8.   
    9.     @Override  
    10.     public void del(Observer observer) {  
    11.         vector.remove(observer);  
    12.     }  
    13.   
    14.     @Override  
    15.     public void notifyObservers() {  
    16.         Enumeration<Observer> enumo = vector.elements();  
    17.         while(enumo.hasMoreElements()){  
    18.             enumo.nextElement().update();  
    19.         }  
    20.     }  
    21. }  
    [java]  view plain copy
    1. public class MySubject extends AbstractSubject {  
    2.   
    3.     @Override  
    4.     public void operation() {  
    5.         System.out.println("update self!");  
    6.         notifyObservers();  
    7.     }  
    8.   
    9. }  


    测试类:

    [java]  view plain copy
    1. public class ObserverTest {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Subject sub = new MySubject();  
    5.         sub.add(new Observer1());  
    6.         sub.add(new Observer2());  
    7.           
    8.         sub.operation();  
    9.     }  
    10.   
    11. }  

    输出:

    update self!
    observer1 has received!
    observer2 has received!

     这些东西,其实不难,只是有些抽象,不太容易整体理解,建议读者:根据关系图,新建项目,自己写代码(或者参考我的代码),按照总体思路走一遍,这样才能体会它的思想,理解起来容易!



    16、迭代子模式(Iterator)

    顾名思义,迭代器模式就是顺序访问聚集中的对象,一般来说,集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。我们看下关系图:

     

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

    两个接口:

    [java]  view plain copy
    1. public interface Collection {  
    2.       
    3.     public Iterator iterator();  
    4.       
    5.     /*取得集合元素*/  
    6.     public Object get(int i);  
    7.       
    8.     /*取得集合大小*/  
    9.     public int size();  
    10. }  
    [java]  view plain copy
    1. public interface Iterator {  
    2.     //前移  
    3.     public Object previous();  
    4.       
    5.     //后移  
    6.     public Object next();  
    7.     public boolean hasNext();  
    8.       
    9.     //取得第一个元素  
    10.     public Object first();  
    11. }  

    两个实现:

    [java]  view plain copy
    1. public class MyCollection implements Collection {  
    2.   
    3.     public String string[] = {"A","B","C","D","E"};  
    4.     @Override  
    5.     public Iterator iterator() {  
    6.         return new MyIterator(this);  
    7.     }  
    8.   
    9.     @Override  
    10.     public Object get(int i) {  
    11.         return string[i];  
    12.     }  
    13.   
    14.     @Override  
    15.     public int size() {  
    16.         return string.length;  
    17.     }  
    18. }  
    [java]  view plain copy
    1. public class MyIterator implements Iterator {  
    2.   
    3.     private Collection collection;  
    4.     private int pos = -1;  
    5.       
    6.     public MyIterator(Collection collection){  
    7.         this.collection = collection;  
    8.     }  
    9.       
    10.     @Override  
    11.     public Object previous() {  
    12.         if(pos > 0){  
    13.             pos--;  
    14.         }  
    15.         return collection.get(pos);  
    16.     }  
    17.   
    18.     @Override  
    19.     public Object next() {  
    20.         if(pos<collection.size()-1){  
    21.             pos++;  
    22.         }  
    23.         return collection.get(pos);  
    24.     }  
    25.   
    26.     @Override  
    27.     public boolean hasNext() {  
    28.         if(pos<collection.size()-1){  
    29.             return true;  
    30.         }else{  
    31.             return false;  
    32.         }  
    33.     }  
    34.   
    35.     @Override  
    36.     public Object first() {  
    37.         pos = 0;  
    38.         return collection.get(pos);  
    39.     }  
    40.   
    41. }  

    测试类:

    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Collection collection = new MyCollection();  
    5.         Iterator it = collection.iterator();  
    6.           
    7.         while(it.hasNext()){  
    8.             System.out.println(it.next());  
    9.         }  
    10.     }  
    11. }  

    输出:A B C D E

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



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

    接下来我们将要谈谈责任链模式,有多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。先看看关系图:

     

     

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

    [java]  view plain copy
    1. public interface Handler {  
    2.     public void operator();  
    3. }  
    [java]  view plain copy
    1. public abstract class AbstractHandler {  
    2.       
    3.     private Handler handler;  
    4.   
    5.     public Handler getHandler() {  
    6.         return handler;  
    7.     }  
    8.   
    9.     public void setHandler(Handler handler) {  
    10.         this.handler = handler;  
    11.     }  
    12.       
    13. }  
    [java]  view plain copy
    1. public class MyHandler extends AbstractHandler implements Handler {  
    2.   
    3.     private String name;  
    4.   
    5.     public MyHandler(String name) {  
    6.         this.name = name;  
    7.     }  
    8.   
    9.     @Override  
    10.     public void operator() {  
    11.         System.out.println(name+"deal!");  
    12.         if(getHandler()!=null){  
    13.             getHandler().operator();  
    14.         }  
    15.     }  
    16. }  
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.         MyHandler h1 = new MyHandler("h1");  
    5.         MyHandler h2 = new MyHandler("h2");  
    6.         MyHandler h3 = new MyHandler("h3");  
    7.   
    8.         h1.setHandler(h2);  
    9.         h2.setHandler(h3);  
    10.   
    11.         h1.operator();  
    12.     }  
    13. }  

    输出:

    h1deal!
    h2deal!
    h3deal!

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


     18、命令模式(Command)

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

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

    [java]  view plain copy
    1. public interface Command {  
    2.     public void exe();  
    3. }  
    [java]  view plain copy
    1. public class MyCommand implements Command {  
    2.   
    3.     private Receiver receiver;  
    4.       
    5.     public MyCommand(Receiver receiver) {  
    6.         this.receiver = receiver;  
    7.     }  
    8.   
    9.     @Override  
    10.     public void exe() {  
    11.         receiver.action();  
    12.     }  
    13. }  
    [java]  view plain copy
    1. public class Receiver {  
    2.     public void action(){  
    3.         System.out.println("command received!");  
    4.     }  
    5. }  
    [java]  view plain copy
    1. public class Invoker {  
    2.       
    3.     private Command command;  
    4.       
    5.     public Invoker(Command command) {  
    6.         this.command = command;  
    7.     }  
    8.   
    9.     public void action(){  
    10.         command.exe();  
    11.     }  
    12. }  
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Receiver receiver = new Receiver();  
    5.         Command cmd = new MyCommand(receiver);  
    6.         Invoker invoker = new Invoker(cmd);  
    7.         invoker.action();  
    8.     }  
    9. }  

    输出:command received!

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

    其实每个设计模式都是很重要的一种思想,看上去很熟,其实是因为我们在学到的东西中都有涉及,尽管有时我们并不知道,其实在Java本身的设计之中处处都有体现,像AWT、JDBC、集合类、IO管道或者是Web框架,里面设计模式无处不在。因为我们篇幅有限,很难讲每一个设计模式都讲的很详细,不过我会尽我所能,尽量在有限的空间和篇幅内,把意思写清楚了,更好让大家明白。本章不出意外的话,应该是设计模式最后一讲了,首先还是上一下上篇开头的那个图:

    本章讲讲第三类和第四类。

    类的状态

    19、备忘录模式(Memento)

    主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。做个图来分析一下:

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

    [java]  view plain copy
    1. public class Original {  
    2.       
    3.     private String value;  
    4.       
    5.     public String getValue() {  
    6.         return value;  
    7.     }  
    8.   
    9.     public void setValue(String value) {  
    10.         this.value = value;  
    11.     }  
    12.   
    13.     public Original(String value) {  
    14.         this.value = value;  
    15.     }  
    16.   
    17.     public Memento createMemento(){  
    18.         return new Memento(value);  
    19.     }  
    20.       
    21.     public void restoreMemento(Memento memento){  
    22.         this.value = memento.getValue();  
    23.     }  
    24. }  
    [java]  view plain copy
    1. public class Memento {  
    2.       
    3.     private String value;  
    4.   
    5.     public Memento(String value) {  
    6.         this.value = value;  
    7.     }  
    8.   
    9.     public String getValue() {  
    10.         return value;  
    11.     }  
    12.   
    13.     public void setValue(String value) {  
    14.         this.value = value;  
    15.     }  
    16. }  
    [java]  view plain copy
    1. public class Storage {  
    2.       
    3.     private Memento memento;  
    4.       
    5.     public Storage(Memento memento) {  
    6.         this.memento = memento;  
    7.     }  
    8.   
    9.     public Memento getMemento() {  
    10.         return memento;  
    11.     }  
    12.   
    13.     public void setMemento(Memento memento) {  
    14.         this.memento = memento;  
    15.     }  
    16. }  

    测试类:

    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.           
    5.         // 创建原始类  
    6.         Original origi = new Original("egg");  
    7.   
    8.         // 创建备忘录  
    9.         Storage storage = new Storage(origi.createMemento());  
    10.   
    11.         // 修改原始类的状态  
    12.         System.out.println("初始化状态为:" + origi.getValue());  
    13.         origi.setValue("niu");  
    14.         System.out.println("修改后的状态为:" + origi.getValue());  
    15.   
    16.         // 回复原始类的状态  
    17.         origi.restoreMemento(storage.getMemento());  
    18.         System.out.println("恢复后的状态为:" + origi.getValue());  
    19.     }  
    20. }  

    输出:

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

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


    20、状态模式(State)

    核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。看图:

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

     

    [java]  view plain copy
    1. package com.xtfggef.dp.state;  
    2.   
    3. /** 
    4.  * 状态类的核心类 
    5.  * 2012-12-1 
    6.  * @author erqing 
    7.  * 
    8.  */  
    9. public class State {  
    10.       
    11.     private String value;  
    12.       
    13.     public String getValue() {  
    14.         return value;  
    15.     }  
    16.   
    17.     public void setValue(String value) {  
    18.         this.value = value;  
    19.     }  
    20.   
    21.     public void method1(){  
    22.         System.out.println("execute the first opt!");  
    23.     }  
    24.       
    25.     public void method2(){  
    26.         System.out.println("execute the second opt!");  
    27.     }  
    28. }  
    [java]  view plain copy
    1. package com.xtfggef.dp.state;  
    2.   
    3. /** 
    4.  * 状态模式的切换类   2012-12-1 
    5.  * @author erqing 
    6.  *  
    7.  */  
    8. public class Context {  
    9.   
    10.     private State state;  
    11.   
    12.     public Context(State state) {  
    13.         this.state = state;  
    14.     }  
    15.   
    16.     public State getState() {  
    17.         return state;  
    18.     }  
    19.   
    20.     public void setState(State state) {  
    21.         this.state = state;  
    22.     }  
    23.   
    24.     public void method() {  
    25.         if (state.getValue().equals("state1")) {  
    26.             state.method1();  
    27.         } else if (state.getValue().equals("state2")) {  
    28.             state.method2();  
    29.         }  
    30.     }  
    31. }  
    测试类:
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.           
    5.         State state = new State();  
    6.         Context context = new Context(state);  
    7.           
    8.         //设置第一种状态  
    9.         state.setValue("state1");  
    10.         context.method();  
    11.           
    12.         //设置第二种状态  
    13.         state.setValue("state2");  
    14.         context.method();  
    15.     }  
    16. }  
    输出:

     

    execute the first opt!
    execute the second opt!

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


    通过中间类

    21、访问者模式(Visitor)

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

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

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

     

    [java]  view plain copy
    1. public interface Visitor {  
    2.     public void visit(Subject sub);  
    3. }  
    [java]  view plain copy
    1. public class MyVisitor implements Visitor {  
    2.   
    3.     @Override  
    4.     public void visit(Subject sub) {  
    5.         System.out.println("visit the subject:"+sub.getSubject());  
    6.     }  
    7. }  
    Subject类,accept方法,接受将要访问它的对象,getSubject()获取将要被访问的属性,
    [java]  view plain copy
    1. public interface Subject {  
    2.     public void accept(Visitor visitor);  
    3.     public String getSubject();  
    4. }  
    [java]  view plain copy
    1. public class MySubject implements Subject {  
    2.   
    3.     @Override  
    4.     public void accept(Visitor visitor) {  
    5.         visitor.visit(this);  
    6.     }  
    7.   
    8.     @Override  
    9.     public String getSubject() {  
    10.         return "love";  
    11.     }  
    12. }  
    测试:
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.           
    5.         Visitor visitor = new MyVisitor();  
    6.         Subject sub = new MySubject();  
    7.         sub.accept(visitor);      
    8.     }  
    9. }  
    输出:visit the subject:love


    该模式适用场景:如果我们想为一个现有的类增加新功能,不得不考虑几个事情:1、新功能会不会与现有功能出现兼容性问题?2、以后会不会再需要添加?3、如果类不允许修改代码怎么办?面对这些问题,最好的解决方法就是使用访问者模式,访问者模式适用于数据结构相对稳定的系统,把数据结构和算法解耦,


    22、中介者模式(Mediator)

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

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

     

    [java]  view plain copy
    1. public interface Mediator {  
    2.     public void createMediator();  
    3.     public void workAll();  
    4. }  
    [java]  view plain copy
    1. public class MyMediator implements Mediator {  
    2.   
    3.     private User user1;  
    4.     private User user2;  
    5.       
    6.     public User getUser1() {  
    7.         return user1;  
    8.     }  
    9.   
    10.     public User getUser2() {  
    11.         return user2;  
    12.     }  
    13.   
    14.     @Override  
    15.     public void createMediator() {  
    16.         user1 = new User1(this);  
    17.         user2 = new User2(this);  
    18.     }  
    19.   
    20.     @Override  
    21.     public void workAll() {  
    22.         user1.work();  
    23.         user2.work();  
    24.     }  
    25. }  
    [java]  view plain copy
    1. public abstract class User {  
    2.       
    3.     private Mediator mediator;  
    4.       
    5.     public Mediator getMediator(){  
    6.         return mediator;  
    7.     }  
    8.       
    9.     public User(Mediator mediator) {  
    10.         this.mediator = mediator;  
    11.     }  
    12.   
    13.     public abstract void work();  
    14. }  
    [java]  view plain copy
    1. public class User1 extends User {  
    2.   
    3.     public User1(Mediator mediator){  
    4.         super(mediator);  
    5.     }  
    6.       
    7.     @Override  
    8.     public void work() {  
    9.         System.out.println("user1 exe!");  
    10.     }  
    11. }  
    [java]  view plain copy
    1. public class User2 extends User {  
    2.   
    3.     public User2(Mediator mediator){  
    4.         super(mediator);  
    5.     }  
    6.       
    7.     @Override  
    8.     public void work() {  
    9.         System.out.println("user2 exe!");  
    10.     }  
    11. }  
    测试类:
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.         Mediator mediator = new MyMediator();  
    5.         mediator.createMediator();  
    6.         mediator.workAll();  
    7.     }  
    8. }  
    输出:

    user1 exe!
    user2 exe!


    23、解释器模式(Interpreter)

    解释器模式是我们暂时的最后一讲,一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄。

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

     

    [java]  view plain copy
    1. public interface Expression {  
    2.     public int interpret(Context context);  
    3. }  
    [java]  view plain copy
    1. public class Plus implements Expression {  
    2.   
    3.     @Override  
    4.     public int interpret(Context context) {  
    5.         return context.getNum1()+context.getNum2();  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class Minus implements Expression {  
    2.   
    3.     @Override  
    4.     public int interpret(Context context) {  
    5.         return context.getNum1()-context.getNum2();  
    6.     }  
    7. }  
    [java]  view plain copy
    1. public class Context {  
    2.       
    3.     private int num1;  
    4.     private int num2;  
    5.       
    6.     public Context(int num1, int num2) {  
    7.         this.num1 = num1;  
    8.         this.num2 = num2;  
    9.     }  
    10.       
    11.     public int getNum1() {  
    12.         return num1;  
    13.     }  
    14.     public void setNum1(int num1) {  
    15.         this.num1 = num1;  
    16.     }  
    17.     public int getNum2() {  
    18.         return num2;  
    19.     }  
    20.     public void setNum2(int num2) {  
    21.         this.num2 = num2;  
    22.     }  
    23.       
    24.       
    25. }  
    [java]  view plain copy
    1. public class Test {  
    2.   
    3.     public static void main(String[] args) {  
    4.   
    5.         // 计算9+2-8的值  
    6.         int result = new Minus().interpret((new Context(new Plus()  
    7.                 .interpret(new Context(92)), 8)));  
    8.         System.out.println(result);  
    9.     }  
    10. }  
    最后输出正确的结果:3。

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

    展开全文
  • 二十三种设计模式【PDF版】

    热门讨论 2011-05-30 14:13:49
    23种设计模式汇集 如果你还不了解设计模式是什么的话? 那就先看设计模式引言 ! 学习 GoF 设计模式的重要性 建筑和软件中模式之异同 A. 创建模式 设计模式之 Singleton(单态/单件) 阎宏博士讲解:单例...
  • 23 种设计模式详解(全23

    万次阅读 多人点赞 2019-06-09 00:21:59
    总体来说设计模式分为大类: 创建型模式,共五:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元...

    设计模式的分类

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

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

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

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

     

    A、创建模式(5种)

    工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

    1 工厂模式

    1.1 简单工厂模式

    定义:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

    举例:(我们举一个pizza工厂的例子)

    pizza工厂一共生产三种类型的pizza:chesse,pepper,greak。通过工厂类(SimplePizzaFactory)实例化这三种类型的对象。类图如下:

     

    工厂类的代码:

    public class SimplePizzaFactory {
           public Pizza CreatePizza(String ordertype) {
                  Pizza pizza = null;
                  if (ordertype.equals("cheese")) {
                         pizza = new CheesePizza();
                  } else if (ordertype.equals("greek")) {
                         pizza = new GreekPizza();
                  } else if (ordertype.equals("pepper")) {
                         pizza = new PepperPizza();
                  }
                  return pizza;
           }
    }
    

    简单工厂存在的问题与解决方法: 简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以,从设计角度考虑,有一定的问题,如何解决?我们可以定义一个创建对象的抽象方法并创建多个不同的工厂类实现该抽象方法,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。这种方法也就是我们接下来要说的工厂方法模式。

    1.2 工厂方法模式

    定义:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。

    举例:(我们依然举pizza工厂的例子,不过这个例子中,pizza产地有两个:伦敦和纽约)。添加了一个新的产地,如果用简单工厂模式的的话,我们要去修改工厂代码,并且会增加一堆的if else语句。而工厂方法模式克服了简单工厂要修改代码的缺点,它会直接创建两个工厂,纽约工厂和伦敦工厂。类图如下:

     

    OrderPizza中有个抽象的方法:

    abstract Pizza createPizza();

    两个工厂类继承OrderPizza并实现抽象方法:

    public class LDOrderPizza extends OrderPizza {
           Pizza createPizza(String ordertype) {
                  Pizza pizza = null;
                  if (ordertype.equals("cheese")) {
                         pizza = new LDCheesePizza();
                  } else if (ordertype.equals("pepper")) {
                         pizza = new LDPepperPizza();
                  }
                  return pizza;
           }
    }
    public class NYOrderPizza extends OrderPizza {
    
    	Pizza createPizza(String ordertype) {
    		Pizza pizza = null;
    
    		if (ordertype.equals("cheese")) {
    			pizza = new NYCheesePizza();
    		} else if (ordertype.equals("pepper")) {
    			pizza = new NYPepperPizza();
    		}
    		return pizza;
    
    	}
    
    }

    、通过不同的工厂会得到不同的实例化的对象,PizzaStroe的代码如下:

    public class PizzaStroe {
           public static void main(String[] args) {
                  OrderPizza mOrderPizza;
                  mOrderPizza = new NYOrderPizza();
           }
    }

    解决了简单工厂模式的问题:增加一个新的pizza产地(北京),只要增加一个BJOrderPizza类:

    public class BJOrderPizza extends OrderPizza {
           Pizza createPizza(String ordertype) {
                  Pizza pizza = null;
                  if (ordertype.equals("cheese")) {
                         pizza = new LDCheesePizza();
                  } else if (ordertype.equals("pepper")) {
                         pizza = new LDPepperPizza();
                  }
                  return pizza;
           }
    }

    其实这个模式的好处就是,如果你现在想增加一个功能,只需做一个实现类就OK了,无需去改动现成的代码。这样做,拓展性较好!

    工厂方法存在的问题与解决方法:客户端需要创建类的具体的实例。简单来说就是用户要订纽约工厂的披萨,他必须去纽约工厂,想订伦敦工厂的披萨,必须去伦敦工厂。 当伦敦工厂和纽约工厂发生变化了,用户也要跟着变化,这无疑就增加了用户的操作复杂性。为了解决这一问题,我们可以把工厂类抽象为接口,用户只需要去找默认的工厂提出自己的需求(传入参数),便能得到自己想要产品,而不用根据产品去寻找不同的工厂,方便用户操作。这也就是我们接下来要说的抽象工厂模式。

    1.3 抽象工厂模式

     定义:定义了一个接口用于创建相关或有依赖关系的对象族,而无需明确指定具体类。

    举例:(我们依然举pizza工厂的例子,pizza工厂有两个:纽约工厂和伦敦工厂)。类图如下:

    工厂的接口:

    public interface AbsFactory {
           Pizza CreatePizza(String ordertype) ;
    }

    工厂的实现:

    public class LDFactory implements AbsFactory {
           @Override
           public Pizza CreatePizza(String ordertype) {
                  Pizza pizza = null;
                  if ("cheese".equals(ordertype)) {
                         pizza = new LDCheesePizza();
                  } else if ("pepper".equals(ordertype)) {
                         pizza = new LDPepperPizza();
                  }
                  return pizza;
           }
    }

    PizzaStroe的代码如下:

    public class PizzaStroe {
           public static void main(String[] args) {
                  OrderPizza mOrderPizza;
                  mOrderPizza = new OrderPizza("London");
           }
    }

    解决了工厂方法模式的问题:在抽象工厂中PizzaStroe中只需要传入参数就可以实例化对象。

    1.4 工厂模式适用的场合

    大量的产品需要创建,并且这些产品具有共同的接口 。

    1.5  三种工厂模式的使用选择

    简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品)

    工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)   

    抽象工厂 :用来生产不同产品族的全部产品。(支持拓展增加产品;支持增加产品族)  

    简单工厂的适用场合:只有伦敦工厂(只有这一个等级),并且这个工厂只生产三种类型的pizza:chesse,pepper,greak(固定产品)。

    工厂方法的适用场合:现在不光有伦敦工厂,还增设了纽约工厂(仍然是同一等级结构,但是支持了产品的拓展),这两个工厂依然只生产三种类型的pizza:chesse,pepper,greak(固定产品)。

    抽象工厂的适用场合:不光增设了纽约工厂(仍然是同一等级结构,但是支持了产品的拓展),这两个工厂还增加了一种新的类型的pizza:chinese pizza(增加产品族)。

    所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线。因此,我们可以用抽象工厂模式创建工厂,而用工厂方法模式创建生产线。比如,我们可以使用抽象工厂模式创建伦敦工厂和纽约工厂,使用工厂方法实现cheese pizza和greak pizza的生产。类图如下:     

    总结一下三种模式:

    简单工厂模式就是建立一个实例化对象的类,在该类中对多个对象实例化。工厂方法模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类。这样做的好处是再有新的类型的对象需要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建对象族,而无需明确指定具体类。抽象工厂也是把对象的实例化交给了子类,即支持拓展。同时提供给客户端接口,避免了用户直接操作子类工厂。

     

    2 单例模式

    定义:确保一个类最多只有一个实例,并提供一个全局访问点

    单例模式可以分为两种:预加载和懒加载

    2.1 预加载

    顾名思义,就是预先加载。再进一步解释就是还没有使用该单例对象,但是,该单例对象就已经被加载到内存了。

    public class PreloadSingleton {
           
           public static PreloadSingleton instance = new PreloadSingleton();
       
           //其他的类无法实例化单例类的对象
           private PreloadSingleton() {
           };
           
           public static PreloadSingleton getInstance() {
                  return instance;
           }
    }

    很明显,没有使用该单例对象,该对象就被加载到了内存,会造成内存的浪费。

    2.2 懒加载

    为了避免内存的浪费,我们可以采用懒加载,即用到该单例对象的时候再创建。

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

    2.3 单例模式和线程安全

    (1)预加载只有一条语句return instance,这显然可以保证线程安全。但是,我们知道预加载会造成内存的浪费。

    (2)懒加载不浪费内存,但是无法保证线程的安全。首先,if判断以及其内存执行代码是非原子性的。其次,new Singleton()无法保证执行的顺序性。

    不满足原子性或者顺序性,线程肯定是不安全的,这是基本的常识,不再赘述。我主要讲一下为什么new Singleton()无法保证顺序性。我们知道创建一个对象分三步:

    memory=allocate();//1:初始化内存空间
    
    ctorInstance(memory);//2:初始化对象
    
    instance=memory();//3:设置instance指向刚分配的内存地址

    jvm为了提高程序执行性能,会对没有依赖关系的代码进行重排序,上面2和3行代码可能被重新排序。我们用两个线程来说明线程是不安全的。线程A和线程B都创建对象。其中,A2和A3的重排序,将导致线程B在B1处判断出instance不为空,线程B接下来将访问instance引用的对象。此时,线程B将会访问到一个还未初始化的对象(线程不安全)。

    2.4 保证懒加载的线程安全

    我们首先想到的就是使用synchronized关键字。synchronized加载getInstace()函数上确实保证了线程的安全。但是,如果要经常的调用getInstance()方法,不管有没有初始化实例,都会唤醒和阻塞线程。为了避免线程的上下文切换消耗大量时间,如果对象已经实例化了,我们没有必要再使用synchronized加锁,直接返回对象。

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

    我们把sychronized加在if(instance==null)判断语句里面,保证instance未实例化的时候才加锁

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

    我们经过2.3的讨论知道new一个对象的代码是无法保证顺序性的,因此,我们需要使用另一个关键字volatile保证对象实例化过程的顺序性。

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

    到此,我们就保证了懒加载的线程安全。

     

    3 生成器模式

    定义:封装一个复杂对象构造过程,并允许按步骤构造。

    定义解释: 我们可以将生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。

    3.1 模式的结构和代码示例

    生成器模式结构中包括四种角色:

    (1)产品(Product):具体生产器要构造的复杂对象;

    (2)抽象生成器(Bulider):抽象生成器是一个接口,该接口除了为创建一个Product对象的各个组件定义了若干个方法之外,还要定义返回Product对象的方法(定义构造步骤);

    (3)具体生产器(ConcreteBuilder):实现Builder接口的类,具体生成器将实现Builder接口所定义的方法(生产各个组件);

    (4)指挥者(Director):指挥者是一个类,该类需要含有Builder接口声明的变量。指挥者的职责是负责向用户提供具体生成器,即指挥者将请求具体生成器类来构造用户所需要的Product对象,如果所请求的具体生成器成功地构造出Product对象,指挥者就可以让该具体生产器返回所构造的Product对象。(按照步骤组装部件,并返回Product

     

    举例(我们如果构建生成一台电脑,那么我们可能需要这么几个步骤(1)需要一个主机(2)需要一个显示器(3)需要一个键盘(4)需要一个鼠标)

    虽然我们具体在构建一台主机的时候,每个对象的实际步骤是不一样的,比如,有的对象构建了i7cpu的主机,有的对象构建了i5cpu的主机,有的对象构建了普通键盘,有的对象构建了机械键盘等。但不管怎样,你总是需要经过一个步骤就是构建一台主机,一台键盘。对于这个例子,我们就可以使用生成器模式来生成一台电脑,他需要通过多个步骤来生成。类图如下:

    ComputerBuilder类定义构造步骤:

    public abstract class ComputerBuilder {
       
        protected Computer computer;
       
        public Computer getComputer() {
            return computer;
        }
       
        public void buildComputer() {
            computer = new Computer();
            System.out.println("生成了一台电脑!!!");
        }
        public abstract void buildMaster();
        public abstract void buildScreen();
        public abstract void buildKeyboard();
        public abstract void buildMouse();
        public abstract void buildAudio();
    }

    HPComputerBuilder定义各个组件:

    public class HPComputerBuilder extends ComputerBuilder {
        @Override
        public void buildMaster() {
            // TODO Auto-generated method stub
            computer.setMaster("i7,16g,512SSD,1060");
            System.out.println("(i7,16g,512SSD,1060)的惠普主机");
        }
        @Override
        public void buildScreen() {
            // TODO Auto-generated method stub
            computer.setScreen("1080p");
            System.out.println("(1080p)的惠普显示屏");
        }
        @Override
        public void buildKeyboard() {
            // TODO Auto-generated method stub
            computer.setKeyboard("cherry 青轴机械键盘");
            System.out.println("(cherry 青轴机械键盘)的键盘");
        }
        @Override
        public void buildMouse() {
            // TODO Auto-generated method stub
            computer.setMouse("MI 鼠标");
            System.out.println("(MI 鼠标)的鼠标");
        }
        @Override
        public void buildAudio() {
            // TODO Auto-generated method stub
            computer.setAudio("飞利浦 音响");
            System.out.println("(飞利浦 音响)的音响");
        }
    }

    Director类对组件进行组装并生成产品

    public class Director {
       
        private ComputerBuilder computerBuilder;
        public void setComputerBuilder(ComputerBuilder computerBuilder) {
            this.computerBuilder = computerBuilder;
        }
       
        public Computer getComputer() {
            return computerBuilder.getComputer();
        }
       
        public void constructComputer() {
            computerBuilder.buildComputer();
            computerBuilder.buildMaster();
            computerBuilder.buildScreen();
            computerBuilder.buildKeyboard();
            computerBuilder.buildMouse();
            computerBuilder.buildAudio();
        }
    }

    3.2 生成器模式的优缺点

    优点

    • 将一个对象分解为各个组件

    • 将对象组件的构造封装起来

    • 可以控制整个对象的生成过程

    缺点

    • 对不同类型的对象需要实现不同的具体构造器的类,这可能回答大大增加类的数量

    3.3 生成器模式与工厂模式的不同

    生成器模式构建对象的时候,对象通常构建的过程中需要多个步骤,就像我们例子中的先有主机,再有显示屏,再有鼠标等等,生成器模式的作用就是将这些复杂的构建过程封装起来。工厂模式构建对象的时候通常就只有一个步骤,调用一个工厂方法就可以生成一个对象。

     

    4 原型模式

    定义:通过复制现有实例来创建新的实例,无需知道相应类的信息。

    简单地理解,其实就是当需要创建一个指定的对象时,我们刚好有一个这样的对象,但是又不能直接使用,我会clone一个一毛一样的新对象来使用;基本上这就是原型模式。关键字:Clone

    4.1 深拷贝和浅拷贝

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

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

    4.2 原型模式的结构和代码示例

    Client:使用者

    Prototype:接口(抽象类),声明具备clone能力,例如java中得Cloneable接口

    ConcretePrototype:具体的原型类

    可以看出设计模式还是比较简单的,重点在于Prototype接口和Prototype接口的实现类ConcretePrototype。原型模式的具体实现:一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法。

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

    举例(银行发送大量邮件,使用clone和不使用clone的时间对比):我们模拟创建一个对象需要耗费比较长的时间,因此,在构造函数中我们让当前线程sleep一会

    public Mail(EventTemplate et) {
                  this.tail = et.geteventContent();
                  this.subject = et.geteventSubject();
                  try {
                         Thread.sleep(1000);
                  } catch (InterruptedException e) {
                         // TODO Auto-generated catch block
                         e.printStackTrace();
                  }
           }

    不使用clone,发送十个邮件

    public static void main(String[] args) {
                  int i = 0;
                  int MAX_COUNT = 10;
                  EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
                  long start = System.currentTimeMillis();
                  while (i < MAX_COUNT) {
                         // 以下是每封邮件不同的地方
                         Mail mail = new Mail(et);
                         mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..." + mail.getTail());
                         mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                         // 然后发送邮件
                         sendMail(mail);
                         i++;
                  }
                  long end = System.currentTimeMillis();
                  System.out.println("用时:" + (end - start));
           }

     

    用时:10001

    使用clone,发送十个邮件

        public static void main(String[] args) {
                  int i = 0;
                  int MAX_COUNT = 10;
                  EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动...");
                  long start=System.currentTimeMillis();
                  Mail mail = new Mail(et);         
                  while (i < MAX_COUNT) {
                         Mail cloneMail = mail.clone();
                         mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..."
                                      + mail.getTail());
                         mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com");
                         sendMail(cloneMail);
                         i++;
                  }
                  long end=System.currentTimeMillis();
                  System.out.println("用时:"+(end-start));
           }

    用时:1001

    4.3 总结

    原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

     

    B、结构模式(7种)

    适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

     

    5 适配器模式

    定义: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

    主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

    5.1 类适配器模式

    通过多重继承目标接口和被适配者类方式来实现适配

    举例(将USB接口转为VGA接口),类图如下:

     

    USBImpl的代码:

    public class USBImpl implements USB{
           @Override
           public void showPPT() {
                  // TODO Auto-generated method stub
                  System.out.println("PPT内容演示");
           }
    }

    AdatperUSB2VGA 首先继承USBImpl获取USB的功能,其次,实现VGA接口,表示该类的类型为VGA。

    public class AdapterUSB2VGA extends USBImpl implements VGA {
           @Override
           public void projection() {
                  super.showPPT();
           }
    }

    Projector将USB映射为VGA,只有VGA接口才可以连接上投影仪进行投影

    public class Projector<T> {
           public void projection(T t) {
                  if (t instanceof VGA) {
                         System.out.println("开始投影");
                         VGA v = new VGAImpl();
                         v = (VGA) t;
                         v.projection();
                  } else {
                         System.out.println("接口不匹配,无法投影");
                  }
           }
    }

    test代码

           @Test
           public void test2(){
                  //通过适配器创建一个VGA对象,这个适配器实际是使用的是USB的showPPT()方法
                  VGA a=new AdapterUSB2VGA();
                  //进行投影
                  Projector p1=new Projector();
                  p1.projection(a);
           } 

    5.2 对象适配器模式

    对象适配器和类适配器使用了不同的方法实现适配,对象适配器使用组合,类适配器使用继承。

    举例(将USB接口转为VGA接口),类图如下:

     

    public class AdapterUSB2VGA implements VGA {
           USB u = new USBImpl();
           @Override
           public void projection() {
                  u.showPPT();
           }
    }

    实现VGA接口,表示适配器类是VGA类型的,适配器方法中直接使用USB对象。

    5.3 接口适配器模式

    当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。

    举例(将USB接口转为VGA接口,VGA中的b()和c()不会被实现),类图如下:

    AdapterUSB2VGA抽象类

    public abstract class AdapterUSB2VGA implements VGA {
           USB u = new USBImpl();
           @Override
           public void projection() {
                  u.showPPT();
           }
           @Override
           public void b() {
           };
           @Override
           public void c() {
           };
    }

    AdapterUSB2VGA实现,不用去实现b()和c()方法。

    public class AdapterUSB2VGAImpl extends AdapterUSB2VGA {
           public void projection() {
                  super.projection();
           }
    }

    5.4 总结

    总结一下三种适配器模式的应用场景:

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

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

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

    命名规则:

    我个人理解,三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。 

    类适配器,以类给到,在Adapter里,就是将src当做类,继承, 

    对象适配器,以对象给到,在Adapter里,将src作为一个对象,持有。 

    接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现。

    使用选择:

    根据合成复用原则,组合大于继承。因此,类的适配器模式应该少用。

     

    6 装饰者模式

    定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。

    6.1 装饰者模式结构图与代码示例

    1.Component(被装饰对象的基类)

       定义一个对象接口,可以给这些对象动态地添加职责。

    2.ConcreteComponent(具体被装饰对象)

       定义一个对象,可以给这个对象添加一些职责。

    3.Decorator(装饰者抽象类)

       维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。

    4.ConcreteDecorator(具体装饰者)

       具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。

    被装饰对象和修饰者继承自同一个超类

    举例(咖啡馆订单项目:1)、咖啡种类:Espresso、ShortBlack、LongBlack、Decaf2)、调料(装饰者):Milk、Soy、Chocolate),类图如下:

     

    被装饰的对象和装饰者都继承自同一个超类

    public abstract class Drink {
           public String description="";
           private float price=0f;;
           
           
           public void setDescription(String description)
           {
                  this.description=description;
           }
           
           public String getDescription()
           {
                  return description+"-"+this.getPrice();
           }
           public float getPrice()
           {
                  return price;
           }
           public void setPrice(float price)
           {
                  this.price=price;
           }
           public abstract float cost();
           
    }

    被装饰的对象,不用去改造。原来怎么样写,现在还是怎么写。

    public  class Coffee extends Drink {
           @Override
           public float cost() {
                  // TODO Auto-generated method stub
                  return super.getPrice();
           }
           
    }

    coffee类的实现

    public class Decaf extends Coffee {
           public Decaf()
           {
                  super.setDescription("Decaf");
                  super.setPrice(3.0f);
           }
    }

    装饰者

    装饰者不仅要考虑自身,还要考虑被它修饰的对象,它是在被修饰的对象上继续添加修饰。例如,咖啡里面加牛奶,再加巧克力。加糖后价格为coffee+milk。再加牛奶价格为coffee+milk+chocolate。

    public class Decorator extends Drink {
           private Drink Obj;
           public Decorator(Drink Obj) {
                  this.Obj = Obj;
           };
           @Override
           public float cost() {
                  // TODO Auto-generated method stub
                  return super.getPrice() + Obj.cost();
           }
           @Override
           public String getDescription() {
                  return super.description + "-" + super.getPrice() + "&&" + Obj.getDescription();
           }
    }

    装饰者实例化(加牛奶)。这里面要对被修饰的对象进行实例化。

    public class Milk extends Decorator {
           public Milk(Drink Obj) {          
                  super(Obj);
                  // TODO Auto-generated constructor stub
                  super.setDescription("Milk");
                  super.setPrice(2.0f);
           }
    }

    coffee店:初始化一个被修饰对象,修饰者实例需要对被修改者实例化,才能对具体的被修饰者进行修饰。

    public class CoffeeBar {
           public static void main(String[] args) {
                  Drink order;
                  order = new Decaf();
                  System.out.println("order1 price:" + order.cost());
                  System.out.println("order1 desc:" + order.getDescription());
                  System.out.println("****************");
                  order = new LongBlack();
                  order = new Milk(order);
                  order = new Chocolate(order);
                  order = new Chocolate(order);
                  System.out.println("order2 price:" + order.cost());
                  System.out.println("order2 desc:" + order.getDescription());
           }
    }

    6.2 总结

    装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

     

     

    7 代理模式

    定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

    举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:

    7.1 为什么要用代理模式?

    中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

    开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。

    代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理

    7.2 静态代理

    举例(买房),类图如下:

    第一步:创建服务类接口

    public interface BuyHouse {
        void buyHosue();
    }

    第二步:实现服务接口

    public class BuyHouseImpl implements BuyHouse {
           @Override
           public void buyHosue() {
                  System.out.println("我要买房");
           }
    }

    第三步:创建代理类

    public class BuyHouseProxy implements BuyHouse {
           private BuyHouse buyHouse;
           public BuyHouseProxy(final BuyHouse buyHouse) {
                  this.buyHouse = buyHouse;
           }
           @Override
           public void buyHosue() {
                  System.out.println("买房前准备");
                  buyHouse.buyHosue();
                  System.out.println("买房后装修");
           }
    }

    总结:

    优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

    缺点: 代理对象与目标对象要实现相同的接口,我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。 

    7.3 动态代理

    动态代理有以下特点:

    1.代理对象,不需要实现接口

    2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

    代理类不用再实现接口了。但是,要求被代理对象必须有接口。

    动态代理实现:

    Java.lang.reflect.Proxy类可以直接生成一个代理对象

    • Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象

      • 参数1:ClassLoader loader 代理对象的类加载器 一般使用被代理对象的类加载器

      • 参数2:Class<?>[] interfaces 代理对象的要实现的接口 一般使用的被代理对象实现的接口

      • 参数3:InvocationHandler h (接口)执行处理类

    • InvocationHandler中的invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行

      • 参数3.1:代理对象(慎用)

      • 参数3.2:当前执行的方法

      • 参数3.3:当前执行的方法运行时传递过来的参数

    第一步:编写动态处理器

    public class DynamicProxyHandler implements InvocationHandler {
           private Object object;
           public DynamicProxyHandler(final Object object) {
                  this.object = object;
           }
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                  System.out.println("买房前准备");
                  Object result = method.invoke(object, args);
                  System.out.println("买房后装修");
                  return result;
           }
    }

    第二步:编写测试类

    public class DynamicProxyTest {
        public static void main(String[] args) {
            BuyHouse buyHouse = new BuyHouseImpl();
            BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
                    Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
            proxyBuyHouse.buyHosue();
        }
    }

    动态代理总结:虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理的桎梏(我们要使用被代理的对象的接口),因为它的设计注定了这个遗憾。

    7.4 CGLIB代理

    CGLIB 原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

    CGLIB 底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

    CGLIB缺点:对于final方法,无法进行代理。

    CGLIB的实现步骤:

    第一步:建立拦截器

    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("买房前准备");
    
            Object result = methodProxy.invoke(object, args);
    
            System.out.println("买房后装修");
    
            return result;
    
        }

    参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

    返回:从代理实例的方法调用返回的值。

    其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

    第二步: 生成动态代理类

    public class CglibProxy implements MethodInterceptor {
        private Object target;
        public Object getInstance(final Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
        public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("买房前准备");
            Object result = methodProxy.invoke(object, args);
            System.out.println("买房后装修");
            return result;
        }
    }

    这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

    首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

    第三步:测试

    public class CglibProxyTest {
        public static void main(String[] args){
            BuyHouse buyHouse = new BuyHouseImpl();
            CglibProxy cglibProxy = new CglibProxy();
            BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
            buyHouseCglibProxy.buyHosue();
        }
    }

    CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

     

    8 外观模式

    定义: 隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。

    8.1 模式结构和代码示例

    简单来说,该模式就是把一些复杂的流程封装成一个接口供给外部用户更简单的使用。这个模式中,设计到3个角色。

      1).门面角色:外观模式的核心。它被客户角色调用,它熟悉子系统的功能。内部根据客户角色的需求预定了几种功能的组合。(客户调用,同时自身调用子系统功能

      2).子系统角色:实现了子系统的功能。它对客户角色和Facade时未知的。它内部可以有系统内的相互交互,也可以由供外界调用的接口。(实现具体功能)

      3).客户角色:通过调用Facede来完成要实现的功能(调用门面角色)。

    举例(每个Computer都有CPU、Memory、Disk。在Computer开启和关闭的时候,相应的部件也会开启和关闭),类图如下:

     

    首先是子系统类:

    public class CPU {
    
    	public void start() {
    		System.out.println("cpu is start...");
    	}
    
    	public void shutDown() {
    		System.out.println("CPU is shutDown...");
    	}
    }
    
    public class Disk {
    	public void start() {
    		System.out.println("Disk is start...");
    	}
    
    	public void shutDown() {
    		System.out.println("Disk is shutDown...");
    	}
    }
    
    public class Memory {
    	public void start() {
    		System.out.println("Memory is start...");
    	}
    
    	public void shutDown() {
    		System.out.println("Memory is shutDown...");
    	}
    }

    然后是,门面类Facade

    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 start() {
    		System.out.println("Computer start begin");
    		cpu.start();
    		disk.start();
    		memory.start();
    		System.out.println("Computer start end");
    	}
    
    	public void shutDown() {
    		System.out.println("Computer shutDown begin");
    		cpu.shutDown();
    		disk.shutDown();
    		memory.shutDown();
    		System.out.println("Computer shutDown end...");
    	}
    }

    最后为,客户角色

    public class Client {
    
    	public static void main(String[] args) {
    		Computer computer = new Computer();
    		computer.start();
    		System.out.println("=================");
    		computer.shutDown();
    	}
    
    }

    8.2 优点

      - 松散耦合

      使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;

      - 简单易用

      客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。

      - 更好的划分访问层次

        有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。

     

    9 桥接模式

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

    9.1 案例

    看下图手机与手机软件的类图

    增加一款新的手机软件,需要在所有手机品牌类下添加对应的手机软件类,当手机软件种类较多时,将导致类的个数急剧膨胀,难以维护

    手机和手机中的软件是什么关系?

    手机中的软件从本质上来说并不是一种手机,手机软件运行在手机中,是一种包含与被包含关系,而不是一种父与子或者说一般与特殊的关系,通过继承手机类实现手机软件类的设计是违反一般规律的。

    如果Oppo手机实现了wifi功能,继承它的Oppo应用商城也会继承wifi功能,并且Oppo手机类的任何变动,都会影响其子类

    换一种解决思路

    从类图上看起来更像是手机软件类图,涉及到手机本身相关的功能,比如说:wifi功能,放到哪个类中实现呢?放到OppoAppStore中实现显然是不合适的

    引起整个结构变化的元素有两个,一个是手机品牌,一个是手机软件,所以我们将这两个点抽出来,分别进行封装

    9.2 桥接模式结构和代码示例

    类图:

    实现:

    public interface Software {
    	public void run();
    
    }
    public class AppStore implements Software {
    	 
        @Override
        public void run() {
            System.out.println("run app store");
        }
    }
    public class Camera implements Software {
    	 
        @Override
        public void run() {
            System.out.println("run camera");
        }
    }

    抽象:

    public abstract class Phone {
    
    	protected Software software;
    
    	public void setSoftware(Software software) {
    		this.software = software;
    	}
    
    	public abstract void run();
    
    }
    public class Oppo extends Phone {
    	 
        @Override
        public void run() {
            software.run();
        }
    }
    public class Vivo extends Phone {
    	 
        @Override
        public void run() {
            software.run();
        }
    }

    对比最初的设计,将抽象部分(手机)与它的实现部分(手机软件类)分离,将实现部分抽象成单独的类,使它们都可以独立地变化。整个类图看起来像一座桥,所以称为桥接模式

    继承是一种强耦合关系,子类的实现与它的父类有非常紧密的依赖关系,父类的任何变化 都会导致子类发生变化,因此继承或者说强耦合关系严重影响了类的灵活性,并最终限制了可复用性

    从桥接模式的设计上我们可以看出聚合是一种比继承要弱的关联关系,手机类和软件类都可独立的进行变化,不会互相影响

    9.3 适用场景

    桥接模式通常适用于以下场景。

    1. 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。

    2. 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。

    3. 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。

    9.4 优缺点

    优点:

    (1)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。

    (2)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。

    缺点:

    桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。

     

    10 组合模式

    定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。

    意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    主要解决:它在我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

    何时使用: 1、您想表示对象的部分-整体层次结构(树形结构)。 2、您希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

    如何解决:树枝和叶子实现统一接口,树枝内部组合该接口。

    关键代码:树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

    组合模式的主要优点有:

    1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;

    2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;

    其主要缺点是:

    1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系;

    2. 不容易限制容器中的构件;

    3. 不容易用继承的方法来增加构件的新功能;

    10.1 模式结构和代码示例

    • 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。

    • 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。

    • 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法

    举例(访问一颗树),类图如下:

    1 组件

    public interface Component {
        public void add(Component c);
        public void remove(Component c);
        public Component getChild(int i);
        public void operation();
    
    }
    

    2 叶子

    public class Leaf implements Component{
        
    	private String name;
    	
    	
    	public Leaf(String name) {
    		this.name = name;
    	}
    
    	@Override
    	public void add(Component c) {}
    
    	@Override
    	public void remove(Component c) {}
    
    	@Override
    	public Component getChild(int i) {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    	@Override
    	public void operation() {
    		// TODO Auto-generated method stub
    		 System.out.println("树叶"+name+":被访问!"); 
    	}
    
    }
    

    3 树枝

    public class Composite implements Component {
    
    	private ArrayList<Component> children = new ArrayList<Component>();
    
    	public void add(Component c) {
    		children.add(c);
    	}
    
    	public void remove(Component c) {
    		children.remove(c);
    	}
    
    	public Component getChild(int i) {
    		return children.get(i);
    	}
    
    	public void operation() {
    		for (Object obj : children) {
    			((Component) obj).operation();
    		}
    	}
    }

     

    11 享元模式

    定义:通过共享的方式高效的支持大量细粒度的对象。

    主要解决:在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。

    何时使用: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。

    如何解决:用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。

    关键代码:用 HashMap 存储这些对象。

    应用实例: 1、JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面。

    优点:大大减少对象的创建,降低系统的内存,使效率提高。

    缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

    简单来说,我们抽取出一个对象的外部状态(不能共享)和内部状态(可以共享)。然后根据外部状态的决定是否创建内部状态对象。内部状态对象是通过哈希表保存的,当外部状态相同的时候,不再重复的创建内部状态对象,从而减少要创建对象的数量。

    11.1 享元模式的结构图和代码示例

    1、Flyweight (享元抽象类):一般是接口或者抽象类,定义了享元类的公共方法。这些方法可以分享内部状态的数据,也可以调用这些方法修改外部状态。

    2、ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类的方法,为享元对象开辟了内存空间来保存享元对象的内部数据,同时可以通过和单例模式结合只创建一个享元对象。

    3、FlyweightFactory(享元工厂类):享元工厂类创建并且管理享元类,享元工厂类针对享元类来进行编程,通过提供一个享元池来进行享元对象的管理。一般享元池设计成键值对,或者其他的存储结构来存储。当客户端进行享元对象的请求时,如果享元池中有对应的享元对象则直接返回对应的对象,否则工厂类创建对应的享元对象并保存到享元池。

    举例(JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面)。类图如下:

    (1)创建享元对象接口

    public interface IFlyweight {
        void print();
    }

    (2)创建具体享元对象

    public class Flyweight implements IFlyweight {
        private String id;
        public Flyweight(String id){
            this.id = id;
        }
        @Override
        public void print() {
            System.out.println("Flyweight.id = " + getId() + " ...");
        }
        public String getId() {
            return id;
        }
    }

    (3)创建工厂,这里要特别注意,为了避免享元对象被重复创建,我们使用HashMap中的key值保证其唯一。

    public class FlyweightFactory {
        private Map<String, IFlyweight> flyweightMap = new HashMap();
        public IFlyweight getFlyweight(String str){
            IFlyweight flyweight = flyweightMap.get(str);
            if(flyweight == null){
                flyweight = new Flyweight(str);
                flyweightMap.put(str, flyweight);
            }
            return  flyweight;
        }
        public int getFlyweightMapSize(){
            return flyweightMap.size();
        }
    }

    (4)测试,我们创建三个字符串,但是只会产生两个享元对象

    public class MainTest {
    	public static void main(String[] args) {
            FlyweightFactory flyweightFactory = new FlyweightFactory();
            IFlyweight flyweight1 = flyweightFactory.getFlyweight("A");
            IFlyweight flyweight2 = flyweightFactory.getFlyweight("B");
            IFlyweight flyweight3 = flyweightFactory.getFlyweight("A");
            flyweight1.print();
            flyweight2.print();
            flyweight3.print();
            System.out.println(flyweightFactory.getFlyweightMapSize());
        }
    
    }
    

     

    C、关系模式(11种)

    先来张图,看看这11中模式的关系:

    第一类:通过父类与子类的关系进行实现。

    第二类:两个类之间。

    第三类:类的状态。

    第四类:通过中间类

     

    12 策略模式

    定义: 策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。

    意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

    主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。

    何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。

    如何解决:将这些算法封装成一个一个的类,任意地替换。

    关键代码:实现同一个接口。

    优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

    缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

    12.1 策略模式结构和示例代码

     

    抽象策略角色: 这个是一个抽象的角色,通常情况下使用接口或者抽象类去实现。对比来说,就是我们的Comparator接口。

    具体策略角色: 包装了具体的算法和行为。对比来说,就是实现了Comparator接口的实现一组实现类。 

    环境角色: 内部会持有一个抽象角色的引用,给客户端调用。

    举例如下( 实现一个加减的功能),类图如下:

    1、定义抽象策略角色

    public interface Strategy {
    
    	public int calc(int num1,int num2);
    }
    

    2、定义具体策略角色

    public class AddStrategy implements Strategy {
    
    	@Override
    	public int calc(int num1, int num2) {
    		// TODO Auto-generated method stub
    		return num1 + num2;
    	}
    
    }
    public class SubstractStrategy implements Strategy {
    
    	@Override
    	public int calc(int num1, int num2) {
    		// TODO Auto-generated method stub
    		return num1 - num2;
    	}
    
    }

    3、环境角色

    public class Environment {
    	private Strategy strategy;
    
    	public Environment(Strategy strategy) {
    		this.strategy = strategy;
    	}
    
    	public int calculate(int a, int b) {
    		return strategy.calc(a, b);
    	}
    
    }

    4、测试

    public class MainTest {
    	public static void main(String[] args) {
    		
    		Environment environment=new Environment(new AddStrategy());
    		int result=environment.calculate(20, 5);
    		System.out.println(result);
    		
    		Environment environment1=new Environment(new SubstractStrategy());
    		int result1=environment1.calculate(20, 5);
    		System.out.println(result1);
    	}
    
    }
    

     

    13 模板模式

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

    通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

    13.1 模式结构和代码示例

     

    抽象父类(AbstractClass):实现了模板方法,定义了算法的骨架。

    具体类(ConcreteClass):实现抽象类中的抽象方法,即不同的对象的具体实现细节。

    举例( 我们做菜可以分为三个步骤 (1)备料 (2)具体做菜 (3)盛菜端给客人享用,这三部就是算法的骨架 ;然而做不同菜需要的料,做的方法,以及如何盛装给客人享用都是不同的这个就是不同的实现细节。)。类图如下:

    a. 先来写一个抽象的做菜父类: 

    public abstract class Dish {    
        /**
         * 具体的整个过程
         */
        protected void dodish(){
            this.preparation();
            this.doing();
            this.carriedDishes();
        }
        /**
         * 备料
         */
        public abstract void preparation();
        /**
         * 做菜
         */
        public abstract void doing();
        /**
         * 上菜
         */
        public abstract void carriedDishes ();
    }

    b. 下来做两个番茄炒蛋(EggsWithTomato)和红烧肉(Bouilli)实现父类中的抽象方法

    public class EggsWithTomato extends Dish {
    
    	@Override
    	public void preparation() {
    		System.out.println("洗并切西红柿,打鸡蛋。");
    	}
    
    	@Override
    	public void doing() {
    		System.out.println("鸡蛋倒入锅里,然后倒入西红柿一起炒。");
    	}
    
    	@Override
    	public void carriedDishes() {
    		System.out.println("将炒好的西红寺鸡蛋装入碟子里,端给客人吃。");
    	}
    
    }
    public class Bouilli extends Dish{
    
        @Override
        public void preparation() {
            System.out.println("切猪肉和土豆。");
        }
    
        @Override
        public void doing() {
            System.out.println("将切好的猪肉倒入锅中炒一会然后倒入土豆连炒带炖。");
        }
    
        @Override
        public void carriedDishes() {
            System.out.println("将做好的红烧肉盛进碗里端给客人吃。");
        }
    
    }

    c. 在测试类中我们来做菜:

    public class MainTest {
    	public static void main(String[] args) {
    		Dish eggsWithTomato = new EggsWithTomato();
    		eggsWithTomato.dodish();
    
    		System.out.println("-----------------------------");
    
    		Dish bouilli = new Bouilli();
    		bouilli.dodish();
    	}
    
    }

    13.2  模板模式的优点和缺点

    优点:

     (1)具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。

     (2)代码复用的基本技术,在数据库设计中尤为重要。

     (3)存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。

    缺点:

        每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大。

     

    14 观察者模式

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

    主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

    何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

    如何解决:使用面向对象技术,可以将这种依赖关系弱化。

    关键代码:在抽象类里有一个 ArrayList 存放观察者们。

    优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。

    缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

    14.1 模式结构图和代码示例

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

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

    • 具体被观察者角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。

    • 具体观察者角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

    举例(有一个微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。)类图如下:

     

    1、定义一个抽象被观察者接口

    public interface Subject {
    	
    	  public void registerObserver(Observer o);
    	  public void removeObserver(Observer o);
    	  public void notifyObserver();
    
    }
    

    2、定义一个抽象观察者接口

    public interface Observer {
    	
    	public void update(String message);
    
    }
    

    3、定义被观察者,实现了Observerable接口,对Observerable接口的三个方法进行了具体实现,同时有一个List集合,用以保存注册的观察者,等需要通知观察者时,遍历该集合即可。

    public class WechatServer implements Subject {
    
    	private List<Observer> list;
    	private String message;
    
    	public WechatServer() {
    		list = new ArrayList<Observer>();
    	}
    
    	@Override
    	public void registerObserver(Observer o) {
    		// TODO Auto-generated method stub
    		list.add(o);
    	}
    
    	@Override
    	public void removeObserver(Observer o) {
    		// TODO Auto-generated method stub
    		if (!list.isEmpty()) {
    			list.remove(o);
    		}
    	}
    
    	@Override
    	public void notifyObserver() {
    		// TODO Auto-generated method stub
    		for (Observer o : list) {
    			o.update(message);
    		}
    	}
    
    	public void setInfomation(String s) {
    		this.message = s;
    		System.out.println("微信服务更新消息: " + s);
    		// 消息更新,通知所有观察者
    		notifyObserver();
    	}
    
    }

    4、定义具体观察者,微信公众号的具体观察者为用户User

    public class User implements Observer {
    
    	private String name;
    	private String message;
    
    	public User(String name) {
    		this.name = name;
    	}
    
    	@Override
    	public void update(String message) {
    		this.message = message;
    		read();
    	}
    
    	public void read() {
    		System.out.println(name + " 收到推送消息: " + message);
    	}
    
    }

    5、编写一个测试类

    public class MainTest {
    	
    	 public static void main(String[] args) {
    		 
    	        WechatServer server = new WechatServer();
    	        
    	        Observer userZhang = new User("ZhangSan");
    	        Observer userLi = new User("LiSi");
    	        Observer userWang = new User("WangWu");
    	        
    	        server.registerObserver(userZhang);
    	        server.registerObserver(userLi);
    	        server.registerObserver(userWang);
    	        server.setInfomation("PHP是世界上最好用的语言!");
    	        
    	        System.out.println("----------------------------------------------");
    	        server.removeObserver(userZhang);
    	        server.setInfomation("JAVA是世界上最好用的语言!");
    	        
    	    }
    
    }
    

     

    15 迭代器模式

    定义:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。

    简单来说,不同种类的对象可能需要不同的遍历方式,我们对每一种类型的对象配一个迭代器,最后多个迭代器合成一个。

    主要解决:不同的方式来遍历整个整合对象。

    何时使用:遍历一个聚合对象。

    如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。

    关键代码:定义接口:hasNext, next。

    应用实例:JAVA 中的 iterator。

    优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。

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

    15.1 模式结构和代码示例

    (1)迭代器角色(Iterator):定义遍历元素所需要的方法,一般来说会有这么三个方法:取得下一个元素的方法next(),判断是否遍历结束的方法hasNext()),移出当前对象的方法remove(),

    (2)具体迭代器角色(Concrete Iterator):实现迭代器接口中定义的方法,完成集合的迭代。

    (3)容器角色(Aggregate):  一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等

    (4)具体容器角色(ConcreteAggregate):就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。

    举例(咖啡厅和中餐厅合并,他们两个餐厅的菜单一个是数组保存的,一个是ArrayList保存的。遍历方式不一样,使用迭代器聚合访问,只需要一种方式)

    1 迭代器接口

    public interface Iterator {
    	
    	public boolean hasNext();
    	public Object next();
    	
    }
    

    2 咖啡店菜单和咖啡店菜单遍历器

    public class CakeHouseMenu {
    	private ArrayList<MenuItem> menuItems;
    	
    	
    	public CakeHouseMenu() {
    		menuItems = new ArrayList<MenuItem>();
    		
    		addItem("KFC Cake Breakfast","boiled eggs&toast&cabbage",true,3.99f);
    		addItem("MDL Cake Breakfast","fried eggs&toast",false,3.59f);
    		addItem("Stawberry Cake","fresh stawberry",true,3.29f);
    		addItem("Regular Cake Breakfast","toast&sausage",true,2.59f);
    	}
    
    	private void addItem(String name, String description, boolean vegetable,
    			float price) {
    		MenuItem menuItem = new MenuItem(name, description, vegetable, price);
    		menuItems.add(menuItem);
    	}
    	
    
    	
    	public Iterator getIterator()
    	{
    		return new CakeHouseIterator() ;
    	}
    	
    	class CakeHouseIterator implements  Iterator
    	 {		
    		private int position=0;
    		public CakeHouseIterator()
    		{
    			  position=0;
    		}
    		
    		 	@Override
    			public boolean hasNext() {
    			// TODO Auto-generated method stub
    			if(position<menuItems.size())
    			{
    				return true;
    			}
    			
    			return false;
    		}
    
    		@Override
    		public Object next() {
    			// TODO Auto-generated method stub
    			MenuItem menuItem =menuItems.get(position);
    			position++;
    			return menuItem;
    		}};
    	//鍏朵粬鍔熻兘浠g爜
    	
    }

    3 中餐厅菜单和中餐厅菜单遍历器

    public class DinerMenu {
    	private final static int Max_Items = 5;
    	private int numberOfItems = 0;
    	private MenuItem[] menuItems;
    
    	public DinerMenu() {
    		menuItems = new MenuItem[Max_Items];
    		addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f);
    		addItem("Blt", "bacon&lettuce&tomato", false, 3.00f);
    		addItem("bean soup", "bean&potato salad", true, 3.28f);
    		addItem("hotdog", "onions&cheese&bread", false, 3.05f);
    
    	}
    
    	private void addItem(String name, String description, boolean vegetable,
    			float price) {
    		MenuItem menuItem = new MenuItem(name, description, vegetable, price);
    		if (numberOfItems >= Max_Items) {
    			System.err.println("sorry,menu is full!can not add another item");
    		} else {
    			menuItems[numberOfItems] = menuItem;
    			numberOfItems++;
    		}
    
    	}
    
    	public Iterator getIterator() {
    		return new DinerIterator();
    	}
    
    	class DinerIterator implements Iterator {
    		private int position;
    
    		public DinerIterator() {
    			position = 0;
    		}
    
    		@Override
    		public boolean hasNext() {
    			// TODO Auto-generated method stub
    			if (position < numberOfItems) {
    				return true;
    			}
    			
    			return false;
    		}
    
    		@Override
    		public Object next() {
    			// TODO Auto-generated method stub
    			MenuItem menuItem = menuItems[position];
    			position++;
    			return menuItem;
    		}
    	};
    }
    

    4 女服务员

    public class Waitress {
    	private ArrayList<Iterator> iterators = new ArrayList<Iterator>();
    
    	public Waitress() {
    
    	}
    
    	public void addIterator(Iterator iterator) {
    		iterators.add(iterator);
    
    	}
    
    	public void printMenu() {
    		Iterator iterator;
    		MenuItem menuItem;
    		for (int i = 0, len = iterators.size(); i < len; i++) {
    			iterator = iterators.get(i);
    
    			while (iterator.hasNext()) {
    				menuItem = (MenuItem) iterator.next();
    				System.out
    						.println(menuItem.getName() + "***" + menuItem.getPrice() + "***" + menuItem.getDescription());
    
    			}
    
    		}
    
    	}
    
    	public void printBreakfastMenu() {
    
    	}
    
    	public void printLunchMenu() {
    
    	}
    
    	public void printVegetableMenu() {
    
    	}
    }
    

    16 责任链模式

    定义:如果有多个对象有机会处理请求,责任链可使请求的发送者和接受者解耦,请求沿着责任链传递,直到有一个对象处理了它为止。

    主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。

    何时使用:在处理消息的时候以过滤很多道。

    如何解决:拦截的类都实现统一接口。

    关键代码:Handler 里面聚合它自己,在 HandlerRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前 set 进去。

    16.1 模式的结构和代码示例

    1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。

    2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。

    3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

    举例(购买请求决策,价格不同要由不同的级别决定:组长、部长、副部、总裁)。类图如下:

    1 决策者抽象类,包含对请求处理的函数,同时还包含指定下一个决策者的函数

    public abstract class Approver {
    	 Approver successor;
    	 String Name;
    	public Approver(String Name)
    	{
    		this.Name=Name;
    	}
    	public abstract void ProcessRequest( PurchaseRequest request);
    	public void SetSuccessor(Approver successor) {
    		// TODO Auto-generated method stub
    		this.successor=successor;
    	}
    }
    

    2 客户端以及请求

    public class PurchaseRequest {
    	private int Type = 0;
    	private int Number = 0;
    	private float Price = 0;
    	private int ID = 0;
    
    	public PurchaseRequest(int Type, int Number, float Price) {
    		this.Type = Type;
    		this.Number = Number;
    		this.Price = Price;
    	}
    
    	public int GetType() {
    		return Type;
    	}
    
    	public float GetSum() {
    		return Number * Price;
    	}
    
    	public int GetID() {
    		return (int) (Math.random() * 1000);
    	}
    }
    public class Client {
    
    	public Client() {
    
    	}
    
    	public PurchaseRequest sendRequst(int Type, int Number, float Price) {
    		return new PurchaseRequest(Type, Number, Price);
    	}
    
    }
    

    3 组长、部长。。。继承决策者抽象类

    public class GroupApprover extends Approver {
    
    	public GroupApprover(String Name) {
    		super(Name + " GroupLeader");
    		// TODO Auto-generated constructor stub
    
    	}
    
    	@Override
    	public void ProcessRequest(PurchaseRequest request) {
    		// TODO Auto-generated method stub
    
    		if (request.GetSum() < 5000) {
    			System.out.println("**This request " + request.GetID() + " will be handled by " + this.Name + " **");
    		} else {
    			successor.ProcessRequest(request);
    		}
    	}
    
    }
    public class DepartmentApprover extends Approver {
    
    	public DepartmentApprover(String Name) {
    		super(Name + " DepartmentLeader");
    
    	}
    
    	@Override
    	public void ProcessRequest(PurchaseRequest request) {
    		// TODO Auto-generated method stub
    
    		if ((5000 <= request.GetSum()) && (request.GetSum() < 10000)) {
    			System.out.println("**This request " + request.GetID()
    					+ " will be handled by " + this.Name + " **");
    		} else {
    			successor.ProcessRequest(request);
    		}
    
    	}
    
    }

    4测试

    public class MainTest {
    
    	public static void main(String[] args) {
    
    		Client mClient = new Client();
    		Approver GroupLeader = new GroupApprover("Tom");
    		Approver DepartmentLeader = new DepartmentApprover("Jerry");
    		Approver VicePresident = new VicePresidentApprover("Kate");
    		Approver President = new PresidentApprover("Bush");
    
    		GroupLeader.SetSuccessor(VicePresident);
    		DepartmentLeader.SetSuccessor(President);
    		VicePresident.SetSuccessor(DepartmentLeader);
    		President.SetSuccessor(GroupLeader);
    
    		GroupLeader.ProcessRequest(mClient.sendRequst(1, 10000, 40));
    
    	}
    
    }

     

    17 命令模式

    定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

    意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。

    主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

    何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

    如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。

    17.1模式结构和代码示例

    1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
    2. 具体命令角色(Concrete    Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
    3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
    4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

    代码举例(开灯和关灯),类图如下:

    1 命令抽象类

    public interface Command {
    	
    	public void excute();
    	public void undo();
    
    }
    

    2 具体命令对象

    public class TurnOffLight implements Command {
    
    	private Light light;
    
    	public TurnOffLight(Light light) {
    		this.light = light;
    	}
    
    	@Override
    	public void excute() {
    		// TODO Auto-generated method stub
    		light.Off();
    	}
    
    	@Override
    	public void undo() {
    		// TODO Auto-generated method stub
    		light.On();
    	}
    
    }

    3 实现者

    public class Light {
    
    	String loc = "";
    
    	public Light(String loc) {
    		this.loc = loc;
    	}
    
    	public void On() {
    
    		System.out.println(loc + " On");
    	}
    
    	public void Off() {
    
    		System.out.println(loc + " Off");
    	}
    
    }

    4 请求者

    public class Contral{
    
    	public void CommandExcute(Command command) {
    		// TODO Auto-generated method stub
    		command.excute();
    	}
    
    	public void CommandUndo(Command command) {
    		// TODO Auto-generated method stub
    		command.undo();
    	}
    
    }
    

     

    18 状态模式

    定义: 在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

    简单理解,一个拥有状态的context对象,在不同的状态下,其行为会发生改变。

    意图:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

    主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

    何时使用:代码中包含大量与对象状态有关的条件语句。

    如何解决:将各种具体的状态类抽象出来。

    关键代码:通常命令模式的接口中只有一个方法。而状态模式的接口中有一个或者多个方法。而且,状态模式的实现类的方法,一般返回值,或者是改变实例变量的值。也就是说,状态模式一般和对象的状态有关。实现类的方法有不同的功能,覆盖接口中的方法。状态模式和命令模式一样,也可以用于消除 if...else 等条件选择语句。

    优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

    缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。

    18.1 模式结构和代码示例

     

    • State抽象状态角色

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

    • ConcreteState具体状态角色

      具体状态主要有两个职责:一是处理本状态下的事情,二是从本状态如何过渡到其他状态。

    • Context环境角色

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

    举例(人物在地点A向地点B移动,在地点B向地点A移动)。类图如下:

    1 state接口

    public interface State {
    	public void stop();
    	public void move();
    
    }
    

    2 状态实例

    public class PlaceA implements State {
    
    	private Player context;
    
    	public PlaceA(Player context) {
    		this.context = context;
    	}
    
    	@Override
    	public void move() {
    		System.out.println("处于地点A,开始向B移动");
    		System.out.println("--------");
    		context.setDirection("AB");
    		context.setState(context.onMove);
    
    	}
    
    	@Override
    	public void stop() {
    		// TODO Auto-generated method stub
    		System.out.println("正处在地点A,不用停止移动");
    		System.out.println("--------");
    	}
    
    }
    

    3 context(player)拥有状态的对象

    public class Player {
    
    	State placeA;
    	State placeB;
    	State onMove;
    	private State state;
    	private String direction;
    
    	public Player() {
    		direction = "AB";
    		placeA = new PlaceA(this);
    		placeB = new PlaceB(this);
    		onMove = new OnMove(this);
    		this.state = placeA;
    	}
    
    	public void move() {
    		System.out.println("指令:开始移动");
    		state.move();
    	}
    
    	public void stop() {
    		System.out.println("指令:停止移动");
    		state.stop();
    	}
    
    	public State getState() {
    		return state;
    	}
    
    	public void setState(State state) {
    		this.state = state;
    	}
    
    	public void setDirection(String direction) {
    		this.direction = direction;
    	}
    
    	public String getDirection() {
    		return direction;
    	}
    
    }

     

    19 备忘录模式

    定义: 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

    备忘录模式是一种对象行为型模式,其主要优点如下。

    • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。

    • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。

    • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

    其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

    19.1 模式结构图和代码示例

    1. 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。

    2. 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。

    3. 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

     

    举例(发起者通过备忘录存储信息和获取信息),类图如下:

     

    1 备忘录接口

    public interface MementoIF {
    
    }
    

    2 备忘录

    public class Memento implements MementoIF{
    	
    	private String state;
    
    	public Memento(String state) {
    		this.state = state;
    	}
    	
    	public String getState(){
    		return state;
    	}
    	
    
    }
    

    3 发起者

    public class Originator {
    
    	private String state;
    
    	public String getState() {
    		return state;
    	}
    
    	public void setState(String state) {
    		this.state = state;
    	}
    
    	public Memento saveToMemento() {
    		return new Memento(state);
    	}
    
    	public String getStateFromMemento(MementoIF memento) {
    		return ((Memento) memento).getState();
    	}
    
    }

    4 管理者

    public class CareTaker {
    	
    	private List<MementoIF> mementoList = new ArrayList<MementoIF>();
    
    	public void add(MementoIF memento) {
    		mementoList.add(memento);
    	}
    
    	public MementoIF get(int index) {
    		return mementoList.get(index);
    	}
    
    }
    

     

    20 访问者模式

    定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离。

    访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。

    1. 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

    2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。

    3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。

    4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

    访问者(Visitor)模式的主要缺点如下。

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

    2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。

    3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

    20.1 模式结构和代码示例

    访问者模式包含以下主要角色。

    1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。

    2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。

    3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。

    4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。

    5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

    1 抽象访问者

    public interface Visitor {
    
    	abstract public void Visit(Element element);
    }
    

    2 具体访问者

    public class CompensationVisitor implements Visitor {
    
    	@Override
    	public void Visit(Element element) {
    		// TODO Auto-generated method stub
    		Employee employee = ((Employee) element);
    
    		System.out.println(
    				employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 10));
    	}
    
    }
    

    3 抽象元素

    public interface Element {
    	abstract public void Accept(Visitor visitor);
    
    }
    

    4 具体元素

    public class CompensationVisitor implements Visitor {
    
    	@Override
    	public void Visit(Element element) {
    		// TODO Auto-generated method stub
    		Employee employee = ((Employee) element);
    
    		System.out.println(
    				employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 10));
    	}
    
    }
    

    5 对象结构

    public class ObjectStructure {
    	private HashMap<String, Employee> employees;
    
    	public ObjectStructure() {
    		employees = new HashMap();
    	}
    
    	public void Attach(Employee employee) {
    		employees.put(employee.getName(), employee);
    	}
    
    	public void Detach(Employee employee) {
    		employees.remove(employee);
    	}
    
    	public Employee getEmployee(String name) {
    		return employees.get(name);
    	}
    
    	public void Accept(Visitor visitor) {
    		for (Employee e : employees.values()) {
    			e.Accept(visitor);
    		}
    	}
    
    }

     

    21 中介者模式

    定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

    中介者模式是一种对象行为型模式,其主要优点如下。

    1. 降低了对象之间的耦合性,使得对象易于独立地被复用。

    2. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

    其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。

    21.1 模式结构和代码示例

    1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。

    2. 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。

    3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。

    4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。

    举例(通过中介卖方),类图如下:

    1 抽象中介者

    public interface Mediator {
    
    	void register(Colleague colleague); // 客户注册
    
    	void relay(String from, String to,String ad); // 转发
    
    }
    

    2 具体中介者

    public class ConcreteMediator implements Mediator {
    
    	private List<Colleague> colleagues = new ArrayList<Colleague>();
    
    	@Override
    	public void register(Colleague colleague) {
    		// TODO Auto-generated method stub
    		if (!colleagues.contains(colleague)) {
    			colleagues.add(colleague);
    			colleague.setMedium(this);
    		}
    	}
    
    	@Override
    	public void relay(String from, String to, String ad) {
    		// TODO Auto-generated method stub
    		for (Colleague cl : colleagues) {
    
    			String name = cl.getName();
    			if (name.equals(to)) {
    				cl.receive(from, ad);
    			}
    
    		}
    
    	}
    
    }

    3 抽象同事类

    public abstract class Colleague {
    
    	protected Mediator mediator;
    	protected String name;
    
    	public Colleague(String name) {
    		this.name = name;
    	}
    
    	public void setMedium(Mediator mediator) {
    
    		this.mediator = mediator;
    
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public abstract void Send(String to, String ad);
    
    	public abstract void receive(String from, String ad);
    
    }

    4 具体同事类

    public class Buyer extends Colleague {
    
    	public Buyer(String name) {
    
    		super(name);
    
    	}
    
    	@Override
    	public void Send(String to, String ad) {
    		// TODO Auto-generated method stub
    		mediator.relay(name, to, ad);
    	}
    
    	@Override
    	public void receive(String from, String ad) {
    		// TODO Auto-generated method stub
    		System.out.println(name + "接收到来自" + from + "的消息:" + ad);
    	}
    
    }

     

    展开全文
  • 二十三种设计模式彩图XXOO

    千次阅读 2020-11-03 11:03:45
    总体来说设计模式分为大类: 创建型模式,共五:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元...
  • 二十三种设计模式介绍

    万次阅读 2017-12-08 16:26:19
    设计模式主要分个类型:创建型、结构型和行为型。 其中创建型有: 一、Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点 、Abstract Factory,抽象工厂:提供一个创建一系列相关或...
  • 一张图快速了解23种设计模式

    万次阅读 多人点赞 2021-09-10 16:07:41
    设计模式 设计模式是对被用来在特定场景下解决一般设计问题的类和相互通信的对象的描述。 这是《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented)中对设计模式的...
  • 23种设计模式概述

    万次阅读 2022-03-31 09:58:49
    23种设计模式 概念 结构图
  • 二十三种设计模式及其python实现

    万次阅读 2018-01-19 16:27:14
    本文为博客转载:... 设计模式是什么? 创建型 1.Factory Method(工厂方法) 2. Abstract Factory(抽象工厂) 3. Builder(建造者) 4. Prototype(原型) 5. Singleton(单例) 结构型 6. Adapte...
  • 工厂方法模式: 一个抽象产品类,可以派生出多个具体产品类。 一个抽象工厂类,可以派生出多个具体工厂类。 每个具体工厂类只能创建一个具体产品类的实例。 抽象工厂模式: 多个抽象产品类,每个抽象产品类可以...
  • 关于Java中的23种设计模式简介(超详细)

    千次阅读 多人点赞 2022-01-04 09:22:23
    23种设计模式 文章目录 系列文章目录 前言 一、pandas是什么? 、使用步骤 1.引入库 2.读入数据 总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习...
  • public class Program { private static Water _water = new Water(); public static void Main(string[] args) { try { _water.Increase(68) .Drink() .Increase(82) .Drink() .Reduce(90) ...}
  • public class Roster { public List<Student> Students { get; private set; } public Roster() { Students = new List<Student> { new Cherry("Cherry"), new Kris("...}

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 302,754
精华内容 121,101
关键字:

二十三种设计模式

友情链接: js.zip