<blockquote id="shwb3"><ruby id="shwb3"></ruby></blockquote>
  • <small id="shwb3"><strong id="shwb3"></strong></small>

      1. <big id="shwb3"></big>
        1. 首页 > 编程语言 > 详细

          Java设计模式学习

          时间:2019-03-03 16:30:55      阅读:2074      评论:0      收藏:0      [点我收藏+]

          标签:observer   else if   情况下   做?#31859;?#24049;   infa   策略   source   variable   变更   

           

          一、设计模式六大原则

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

          开闭原则:对扩展开放,对修改关闭。即为了程序的扩展性好,?#23376;?#32500;护和升级,我们需要使用接口?#32479;?#35937;类。

          1、单一职责原则

          不要存在多于一个导致类变更?#33041;?#22240;,也就是说每个类应该?#36842;?#21333;一的职责,如若不然,就应该把类拆分

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

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

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

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

          这个是开闭原则的基础,具体内容?#22909;?#21521;接口编程。依赖于抽象而不依赖于具体。?#21019;?#30721;时用到具体类时,不于具体类交互,而与具体类的上层接口交互。

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

          这个原则的意思是?#22909;?#20010;接口中不存在子类用不到却必须?#36842;?#30340;方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个方法集?#31995;?#19968;个的接口)要好。

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

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

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

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

          原则是尽量首先使用合成/聚?#31995;?#26041;式,而不是使用继承。

          二、Java?#27169;玻?#31181;设计模式

          A、创建模式(五种)

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

          0、简单工厂模式

          01 普通

          就是创建一个工厂类,对?#36842;?#20102;同一接口的一些类进行?#36947;?#30340;创建。首先看下图:

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          举例如下?#28023;?#21457;送邮件和短信的例子)

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

           public class MailSender implements Sender {
               @Override
               public void Send() {
                   System.out.println("This is mailSender");
              }
           }
           ?
           public class SmsSender implements Sender {
               @Override
               public void Send() {
                   Sysytem.out.println("This is sms sender");
              }
           }

          最后,建工厂类:

           public class SendFactory {
               
               public Sender produce(String type) {
                   if("mail".equals("type")) {
                       return new MailSender();
                  }
                   else if("sms".equals(type)) {
                       return new SmsSender();
                  }
                   else {
                       Sysytem.out.println("请输入正确的类型!");
                       retrurn null;
                  }
              }
           }
          02、多个方法

          对于普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确的创建对象,而多个方法工厂是提供多个工厂方法,分别创建对象。技术分享?#35745;? src=

          技术分享?#35745;? src=

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

           

          测试类如下:

           public class FactoryTest {
               public static void main(String[] args) {
                   SendFactory factory = new SendFactory();
                   Sender sender = factory.produceMail();
                   sender.Send();
              }
           }
          03、多个静态方法

          将上面的多个工厂方法里的方法设置为动态?#27169;?#19981;需要创建?#36947;?#30452;接调用。

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

          总结:工程模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法进行创建。以上三种方式中

          • 1、传入字符串有误,不能正确创建对象

          • 2、第三种相对于第二种,不需要?#36947;?#21270;工厂类,大多数情况下,我们会选择第三种-静态工厂方法模式。

          1、简单工厂模式(Factory Method)

          简单工厂模式有一个问题就是,类的创建依赖工厂 ,也就是说,若想要拓展程序,必须和工厂类进行修?#27169;?#36825;违背了闭包原则,所?#28304;?#35774;计角度考虑,有一定的问题,

          解决:创建一个工厂接口,和多个工厂?#36842;?#31867;,这样一旦需要增加新的功能,直接增加新的工厂类就行,不需要改之前的代码。

          技术分享?#35745;? src=

          技术分享?#35745;? src=

           public interface Sender {
               public void Send();
           }
           ?
           //两个?#36842;?#31867;
           public class MailSender implements Sender {
               @Override
               public void Send() {
                   System.out.println("This is mailsender");
              }
           }
           ?
           public class SmsSender implements Sender {
               @Override
               public void Send() {
                   System.out.println("This is sms sender");
              }
           }
           ?
           //抽象工厂接口
           public interface Provider {
               public Sender produce();
           }
           //工厂?#36842;?#31867;
           public class SendMainFactory implements Provider {
               @Override
               public Sender produce() {
                   return new MailSender();
              }
           }
           ?
           public class SmsMainFactory implements Provider {
               @Override
               public Sender produce() {
                   return new SmsSender();
              }
           }

          工厂模式的好处就是,如果你现在想增加一个功能:发即时消息,即只需一个?#36842;?#31867;,?#36842;諷ender接口,同时做一个工厂类,?#36842;諴rovider接口,就ok了,无需去改现成的代码。这样做,拓展性好。

          2、抽象工厂模式

          工厂模式与抽象工厂的区别如下:

          1. 工厂方法模式

            一个抽象产品类,可以派生出多个具体产品类。

            一个抽象工厂类,可以派生出多个具体工厂类。

            每个具体工厂类只能创建一个具体产品类的?#36947;?/p>

             

          2. 抽象工厂模式

            多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。

            一个抽象工厂类,可以派生出多个具体工厂类。

            每个具体工厂类可?#28304;?#24314;多个具体产品类的?#36947;?#20063;就是创建的是一个产品线下的多个产品。

          区别:

          ?#34180;?#24037;厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。

          2、工厂方法模式的具体工厂类只能创建一个具体产品类的?#36947;?#32780;抽象工厂模式可?#28304;?#24314;多个。

          ?#22330;?#24037;厂方法创建”一种“产品,它的着重点在于”怎?#21019;?#24314;“,也就是说如果你开发,你的大量代码很可能围?#26222;?#31181;产品的构造,初始化这些细节上面。也因为如此,类似的产?#20998;?#38388;有很多可以复用的特征。所以会和模板方法相随。

           

          抽象工厂需要创建一些产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一直,从而可?#28304;?#21516;一个抽象工厂继承。

          ?#28023;海?#25277;象工厂里面是一堆工厂方法,每个工厂方法返回某类型的对象。

          比如?#20498;?#21378;可以生产鼠标和键盘。那么抽象工厂的?#36842;?#31867;(它的某个具体子类)的对象都可以生产鼠标和键盘,但可能工厂A生产的是罗技的键盘和鼠标,工厂B是微软的。

          这样A和B就是工厂,对应于抽象工厂; 每个工厂生产的鼠标和键盘就是产品,对应于工厂方法;

          用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从罗技换到微软。但是用了抽象工厂模式,你只要换家工厂,就可以同时替换鼠标和键盘一套。如果你要的产品有几十个,当然用抽象工厂模式一次替换全部最方便(这个工厂会替你用相应的工厂方法)

          所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线

          ?#22330;?#21333;例模式(Singleton)

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

          1、?#25215;?#31867;创建比较频?#20445;?#23545;于一些大型的对象,这是一笔很大的系统开销。

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

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

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

           public class Singleton {  
             
               /* 持有?#25509;?#38745;态?#36947;?#38450;止被引用,此处赋值为null,目的是?#36842;?#24310;迟加载 */  
               private static Singleton instance = null;  
             
               /* ?#25509;?#26500;造方法,防止被?#36947;?#21270; */  
               private Singleton() {  
              }  
             
               /* 静态工程方法,创建?#36947;?*/  
               public static Singleton getInstance() {  
                   if (instance == null) {  
                       instance = new Singleton();  
                  }  
                   return instance;  
              }  
             
               /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
               public Object readResolve() {  
                   return instance;  
              }  
           }  

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

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

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

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

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

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

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

          c>由于JVM内部的优化机制,JVM?#28982;?#20986;了一些分配给Singleton?#36947;目?#30333;内存,并赋值给instance成员(注意此时JVM没有开始初始化这个?#36947;?#28982;后A离开了synchronized块。

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

          e>此时B线程打算使用Singleton?#36947;?#21364;发现它没有被初始化,于是错误发生了。

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

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

          ?#23548;?#24773;况是,单例模式使用内部类来维护单例的?#36842;鄭琂VM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并?#19968;?#20445;证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也?#25442;?#22312;第一次调用的时候使用互斥机制,这样就解决了低?#38405;?#38382;题。这样我们暂时总结一个完?#36182;?#21333;例模式:

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

          其实说它完美,也不一定,如果在构造函数?#20449;?#20986;异常,?#36947;?#23558;永远得不到创建,?#19981;?#20986;错。所以说,十分完?#36182;?#19996;西是没有?#27169;?#25105;们只能根据?#23548;?#24773;况,选择最适合自己应用场景的?#36842;?#26041;法。也有人这样?#36842;鄭?#22240;为我们只需要在创建类的时候进?#22411;?#27493;,所以只要将创建和getInstance()分开,单独为创建加synchronized关键字,也是可以的.

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

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

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

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

          1、单例模式理解起来简单,但是具体?#36842;制?#26469;还是有一定的难度。

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

          到这儿,单例模式基本已经讲完了,结尾处,?#25910;?#31361;然想到另一个问题,就是采用类的静态方法,?#36842;?#21333;例模式的效果,也是可行?#27169;?#27492;处二者有什么不同?

          首先,静态类不能?#36842;?#25509;口。(从类的角度说是可以?#27169;?#20294;是那样就破坏了静态了。因为接口中不允许有static修饰的方法,所以即使?#36842;?#20102;也是非静态?#27169;?/p>

          其次,单例可以被延迟初始化,静态类一般在第一次加载是初始化。之所以延迟加载,是因为有些类比较庞大,所以延迟加载有助于提升?#38405;堋?/p>

          再次,单例类可以被继承,他的方法可以被覆?#30784;?#20294;是静态类内部方法都是static,无法被覆?#30784;?/p>

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

          4、建造者模式(Builder)

           

          5、原型模式(Prototype)

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

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

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

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

          深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复?#24179;?#34892;了完全彻底的复制,而浅复制不彻?#20303;?/p>

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

           public class Prototype implements Cloneable, Serializable {  
             
               private static final long serialVersionUID = 1L;  
               private String string;  
             
               private SerializableObject obj;  
             
               /* 浅复制 */  
               public Object clone() throws CloneNotSupportedException {  
                   Prototype proto = (Prototype) super.clone();  
                   return proto;  
              }  
             
               /* 深复制 */  
               public Object deepClone() throws IOException, ClassNotFoundException {  
             
                   /* 写入当前对象的二进制流 */  
                   ByteArrayOutputStream bos = new ByteArrayOutputStream();  
                   ObjectOutputStream oos = new ObjectOutputStream(bos);  
                   oos.writeObject(this);  
             
                   /* 读出二进制流产生的新对象 */  
                   ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
                   ObjectInputStream ois = new ObjectInputStream(bis);  
                   return ois.readObject();  
              }  
             
               public String getString() {  
                   return string;  
              }  
             
               public void setString(String string) {  
                   this.string = string;  
              }  
             
               public SerializableObject getObj() {  
                   return obj;  
              }  
             
               public void setObj(SerializableObject obj) {  
                   this.obj = obj;  
              }  
             
           }  
             
           class SerializableObject implements Serializable {  
               private static final long serialVersionUID = 1L;  
           }  

          ?#36842;?#28145;复制,需要采用流的形式读入当前对象的二进制输入,再写出二进制数据对应的对象。

          B、结构模式(七种)

          七种结构模式?#28023;?#36866;配器模式、装?#25991;?#24335;、代理模式、外观模式、桥接模式、组合模式、享元模式)。其中对象的适配器是各种模式的起源。如下图:

          技术分享?#35745;? src= 技术分享?#35745;? src=

          6、适配器模式

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

          1. 类的适配器模式

          2. 对象的适配器模式

          3. 接口的适配器模式

          01、类的适配器模式
          • 技术分享?#35745;? src=技术分享?#35745;? src=

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

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

          Adapter类继承Source类,?#36842;諸argetable接口。

          这样Targetable接口的?#36842;?#31867;就具有了Source类的功能。

          02、对象的适配器模式

          基本思路和类的适配器模式相同,只是将Adapter类作修?#27169;?#36825;次不继承Source类,而是持有Source类的?#36947;源?#21040;解决兼容性的问题。看图:

          技术分享?#35745;? src=

          技术分享?#35745;? src=


          //只需要修改Adapter类的源码。 public class Wrapper implements Targetable { private Source source; public Wrapper(Source source){ super(); this.source = source; } @Override public void method2() { System.out.println("this is the targetable method!"); } @Override public void method1() { source.method1(); } }
          03、接口的适配器模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          这个很好理解,在?#23548;?#24320;发中,我们也常会遇到这种接口中定义了太多的方法,以致于有时我们在一些?#36842;?#31867;中并不是都需要。?#21019;?#30721;:

          //接口
          public interface Sourceable {  
                
              public void method1();  
              public void method2();  
          }  
          //抽象类
          public abstract class Wrapper2 implements Sourceable{  
                
              public void method1(){}  
              public void method2(){}  
          } 
          
          //两个?#36842;?#31867;
          public class SourceSub1 extends Wrapper2 {  
              public void method1(){  
                  System.out.println("the sourceable interface‘s first Sub1!");  
              }  
          }  
          
          public class SourceSub2 extends Wrapper2 {  
              public void method2(){  
                  System.out.println("the sourceable interface‘s second Sub2!");  
              }  
          }  
          //测试类
          public class WrapperTest {  
            
              public static void main(String[] args) {  
                  Sourceable source1 = new SourceSub1();  
                  Sourceable source2 = new SourceSub2();  
                    
                  source1.method1();  
                  source1.method2();  
                  source2.method1();  
                  source2.method2();  
              }  
          }  
          

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

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

          对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可?#28304;?#24314;一个Wrapper类,持有原类的一个?#36947;?#22312;Wrapper类的方法中,调用?#36947;?#30340;方法就行。

          接口的适配器模式:当不希望?#36842;?#19968;个接口中所有的方法时,可?#28304;?#24314;一个抽象类Wrapper,?#36842;?#25152;有方法,我们写别的类的时候,继承抽象类即可。

          7、装?#25991;?#24335;

          顾名思义,装?#25991;?#24335;就是给一个对象增加一些新的功能,而且是动态?#27169;?#35201;求装饰对象和被装饰对象?#36842;?#21516;一个接口,装饰对象持有被装饰对象的?#36947;?#20851;系图如下:

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

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

          装饰器模式的应用场景:

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

          2、动态的为一个对象增加功能,而?#19968;?#33021;动态撤销。(继承不能做到这一点,继承的功能是静态?#27169;?#19981;能动态增?#23613;#?/p>

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

          8、代理模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

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

          代理模式的应用场景:

          如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两?#32844;?#27861;:

          1、修?#33041;?#26377;的方法来适应。这样违反了“对扩展开放,对修改关闭”?#33041;?#21017;。

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

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

          代理模式与装?#25991;?#24335;的区别

          代理模式对被代理的对象有控制权,决定其执行或者不执行。而装饰者模式,装饰类?#28304;?#29702;对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能。

          9、外观模式(Facade)

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          代码如下:

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

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

          10、桥接模式

          桥接模式就是把事物和其具体?#36842;?#20998;开,使他们可以各自独立的变化。桥接的用意是:将抽象化与?#36842;只?#35299;耦,使得二者可以独立变化,像我们常用的JDBC桥DriverManager一样,JDBC进行连接数据库的时候,在各个数据库之间进行切换,基?#38745;?#38656;要动太多的代码,甚?#20102;?#27627;不用动,原因就是JDBC提供统一接口,每个数据库提供各自的?#36842;鄭?#29992;一个叫做数据库驱动的程序来桥接就行了。我们来看看关系图:

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          代码如下:

          public interface Sourceable {  
              public void method();  
          }  
          
          //?#36842;?#31867;
          public class SourceSub1 implements Sourceable {  
            
              @Override  
              public void method() {  
                  System.out.println("this is the first sub!");  
              }  
          }  
          
          public class SourceSub2 implements Sourceable {  
            
              @Override  
              public void method() {  
                  System.out.println("this is the second sub!");  
              }  
          }  
          
          //定义一个桥 持有Sourceable的一个?#36947;?
          public abstract class Bridge {  
              private Sourceable source;  
            
              public void method(){  
                  source.method();  
              }  
                
              public Sourceable getSource() {  
                  return source;  
              }  
            
              public void setSource(Sourceable source) {  
                  this.source = source;  
              }  
          }  
          
          public class MyBridge extends Bridge {  
              public void method(){  
                  getSource().method();  
              }  
          }  
          
          //测试类
          public class BridgeTest {  
                
              public static void main(String[] args) {  
                    
                  Bridge bridge = new MyBridge();  
                    
                  /*调用第一个对象*/  
                  Sourceable source1 = new SourceSub1();  
                  bridge.setSource(source1);  
                  bridge.method();  
                    
                  /*调用第二个对象*/  
                  Sourceable source2 = new SourceSub2();  
                  bridge.setSource(source2);  
                  bridge.method();  
              }  
          }  
          

          这样,就通过对Bridge类的调用,?#36842;?#20102;对接口Sourceable的?#36842;?#31867;SourceSub1和SourceSub2的调用。接下来我再画个图,大家就应该明白了,因为这个图是我们JDBC连接?#33041;?#29702;,有数据库学习基础?#27169;?#19968;结合就?#32423;?#20102;。

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          11、组合模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

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

          12、享元模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

          看个例子:

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

          通过连接池的管理,?#36842;?#20102;数据库连接的共享,不需要每一次都重新创建连接,节省了数据库重新创建的开销,提升了系统的?#38405;埽?/p>

          C、关系模式

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

          第一类:通过父类与子类的关系进行?#36842;幀?/p>

          第二类:两个类之间。

          第三类:类的状态。

          第四类:通过中间类

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          父类与子类关系

          13、策略模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          图中ICalculator提供同意的方法, AbstractCalculator是辅助类,提供辅助方法,接下来,依?#38382;迪?#19979;每个类:

          首先统一接口:

          public interface ICalculator {  
              public int calculate(String exp);  
          }  
          
          //辅助类
          public abstract class AbstractCalculator {  
                
              public int[] split(String exp,String opt){  
                  String array[] = exp.split(opt);  
                  int arrayInt[] = new int[2];  
                  arrayInt[0] = Integer.parseInt(array[0]);  
                  arrayInt[1] = Integer.parseInt(array[1]);  
                  return arrayInt;  
              }  
          }  
          
          //三个?#36842;?#31867;
          public class Plus extends AbstractCalculator implements ICalculator {  
            
              @Override  
              public int calculate(String exp) {  
                  int arrayInt[] = split(exp,"\\+");  
                  return arrayInt[0]+arrayInt[1];  
              }  
          }  
          //2
          public class Minus extends AbstractCalculator implements ICalculator {  
            
              @Override  
              public int calculate(String exp) {  
                  int arrayInt[] = split(exp,"-");  
                  return arrayInt[0]-arrayInt[1];  
              }  
            
          }  
          //3
          public class Multiply extends AbstractCalculator implements ICalculator {  
            
              @Override  
              public int calculate(String exp) {  
                  int arrayInt[] = split(exp,"\\*");  
                  return arrayInt[0]*arrayInt[1];  
              }  
          }  
          //测试
          
          public class StrategyTest {  
            
              public static void main(String[] args) {  
                  String exp = "2+8";  
                  ICalculator cal = new Plus();  
                  int result = cal.calculate(exp);  
                  System.out.println(result);  
              }  
          }  
          

          策略模式的决定权在用户,系统本身提供不同算法的?#36842;鄭?#26032;增或者?#22659;?#31639;法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

          14、模板方法模式

          解释一下模板方法模式,就是指:一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象?#27169;?#20063;可以是?#23548;?#30340;方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,?#36842;?#23545;子类的调用,先看个关系图:

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

          //抽象类
          public abstract class AbstractCalculator {  
                
              /*主方法,?#36842;?#23545;本类其它方法的调用*/  
              public final int calculate(String exp,String opt){  
                  int array[] = split(exp,opt);  
                  return calculate(array[0],array[1]);  
              }  
                
              /*被子类重写的方法*/  
              abstract public int calculate(int num1,int num2);  
                
              public int[] split(String exp,String opt){  
                  String array[] = exp.split(opt);  
                  int arrayInt[] = new int[2];  
                  arrayInt[0] = Integer.parseInt(array[0]);  
                  arrayInt[1] = Integer.parseInt(array[1]);  
                  return arrayInt;  
              }  
          }  
          
          public class Plus extends AbstractCalculator {  
            
              @Override  
              public int calculate(int num1,int num2) {  
                  return num1 + num2;  
              }  
          }  
          
          //测试类
          public class StrategyTest {  
            
              public static void main(String[] args) {  
                  String exp = "8+8";  
                  AbstractCalculator cal = new Plus();  
                  int result = cal.calculate(exp, "\\+");  
                  System.out.println(result);  
              }  
          }  
          
          

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

          类之间的关系

          15、观察者模式(Observer)

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          我解释下这些类的作用:MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。AbstractSubject类中定义着需要监控的对象列表,可以对其进行修?#27169;?#22686;加或?#22659;?#34987;监控对象,且当MySubject变化时,负责通知在列表内存在的对象。我们看?#36842;?#20195;码:

          //观察者接口
          public interface Observer {  
              public void update();  
          }  
          //两个?#36842;?#31867;
          public class Observer1 implements Observer {  
            
              @Override  
              public void update() {  
                  System.out.println("observer1 has received!");  
              }  
          }  
          //2
          public class Observer2 implements Observer {  
            
              @Override  
              public void update() {  
                  System.out.println("observer2 has received!");  
              }  
            
          }  
          
          //Subject接口及?#36842;?#31867;
          public interface Subject {  
                
              /*增加观察者*/  
              public void add(Observer observer);  
                
              /*?#22659;?#35266;察者*/  
              public void del(Observer observer);  
                
              /*通知所有的观察者*/  
              public void notifyObservers();  
                
              /*自身的操作*/  
              public void operation();  
          }  
          //抽象类
          public abstract class AbstractSubject implements Subject {  
            
              private Vector<Observer> vector = new Vector<Observer>();  
              @Override  
              public void add(Observer observer) {  
                  vector.add(observer);  
              }  
            
              @Override  
              public void del(Observer observer) {  
                  vector.remove(observer);  
              }  
            
              @Override  
              public void notifyObservers() {  
                  Enumeration<Observer> enumo = vector.elements();  
                  while(enumo.hasMoreElements()){  
                      enumo.nextElement().update();  
                  }  
              }  
          }  
          //?#36947;?#31867;
          public class MySubject extends AbstractSubject {  
            
              @Override  
              public void operation() {  
                  System.out.println("update self!");  
                  notifyObservers();  
              }  
            
          }  
          
          //测试
          public class ObserverTest {  
            
              public static void main(String[] args) {  
                  Subject sub = new MySubject();  
                  sub.add(new Observer1());  
                  sub.add(new Observer2());  
                    
                  sub.operation();  
              }  
            
          }  
          

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

           

          16、迭代子模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          这个思路和我们常用的一模一样,MyCollection中定义了集?#31995;?#19968;些操作,MyIterator中定义了一系列迭代操作,?#39029;?#26377;Collection?#36947;?#25105;们来看看?#36842;?#20195;码:

          //集合
          public interface Collection {  
                
              public Iterator iterator();  
                
              /*取得集合元素*/  
              public Object get(int i);  
                
              /*取得集合大小*/  
              public int size();  
          }  
          //迭代器
          public interface Iterator {  
              //前移  
              public Object previous();  
                
              //后移  
              public Object next();  
              public boolean hasNext();  
                
              //取得第一个元素  
              public Object first();  
          }  
          
          //?#36842;?#31867;
          public class MyCollection implements Collection {  
            
              public String string[] = {"A","B","C","D","E"};  
              @Override  
              public Iterator iterator() {  
                  return new MyIterator(this);  
              }  
            
              @Override  
              public Object get(int i) {  
                  return string[i];  
              }  
            
              @Override  
              public int size() {  
                  return string.length;  
              }  
          }  
          
          //2
          public class MyIterator implements Iterator {  
            
              private Collection collection;  
              private int pos = -1;  
                
              public MyIterator(Collection collection){  
                  this.collection = collection;  
              }  
                
              @Override  
              public Object previous() {  
                  if(pos > 0){  
                      pos--;  
                  }  
                  return collection.get(pos);  
              }  
            
              @Override  
              public Object next() {  
                  if(pos<collection.size()-1){  
                      pos++;  
                  }  
                  return collection.get(pos);  
              }  
            
              @Override  
              public boolean hasNext() {  
                  if(pos<collection.size()-1){  
                      return true;  
                  }else{  
                      return false;  
                  }  
              }  
            
              @Override  
              public Object first() {  
                  pos = 0;  
                  return collection.get(pos);  
              }  
            
          }  
          
          //测试类
          public class Test {  
            
              public static void main(String[] args) {  
                  Collection collection = new MyCollection();  
                  Iterator it = collection.iterator();  
                    
                  while(it.hasNext()){  
                      System.out.println(it.next());  
                  }  
              }  
          }  
          
          

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

          17、责任链模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          Abstracthandler类提供了get和set方法,方便MyHandle类设置和修改引用对象,MyHandle类是核?#27169;道?#21270;后生成一系列相互持有的对象,构成一条链。

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

          此处强调一点就是,链接?#31995;?#35831;求可以是一条链,可以是一个树,还可以是一个环,模式本身不?#38469;?#36825;个,需要我们自己去?#36842;鄭?#21516;时,在一个时刻,命令只允许由一个对象传给另一个对象,而不允许传给多个对象。

          18、命令模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          Invoker是调用者(司令员),Receiver是被调用者(士兵),MyCommand是命令,?#36842;?#20102;Command接口,持有接收对象,看?#36842;?#20195;码:

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

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

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

          类的状态

          19、备忘录模式

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

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

          //原始类
          public class Original {  
                
              private String value;  
                
              public String getValue() {  
                  return value;  
              }  
            
              public void setValue(String value) {  
                  this.value = value;  
              }  
            
              public Original(String value) {  
                  this.value = value;  
              }  
            
              public Memento createMemento(){  
                  return new Memento(value);  
              }  
                
              public void restoreMemento(Memento memento){  
                  this.value = memento.getValue();  
              }  
          }  
          //备忘类
          public class Memento {  
                
              private String value;  
            
              public Memento(String value) {  
                  this.value = value;  
              }  
            
              public String getValue() {  
                  return value;  
              }  
            
              public void setValue(String value) {  
                  this.value = value;  
              }  
          }  
          
          //备忘录
          public class Storage {  
                
              private Memento memento;  
                
              public Storage(Memento memento) {  
                  this.memento = memento;  
              }  
            
              public Memento getMemento() {  
                  return memento;  
              }  
            
              public void setMemento(Memento memento) {  
                  this.memento = memento;  
              }  
          } 
          
          //测试类
          public class Test {  
            
              public static void main(String[] args) {  
                    
                  // 创建原始类  
                  Original origi = new Original("egg");  
            
                  // 创建备忘录  
                  Storage storage = new Storage(origi.createMemento());  
            
                  // 修?#33041;?#22987;类的状态  
                  System.out.println("初始化状态为:" + origi.getValue());  
                  origi.setValue("niu");  
                  System.out.println("修改后的状态为:" + origi.getValue());  
            
                  // 恢复原始类的状态  
                  origi.restoreMemento(storage.getMemento());  
                  System.out.println("恢复后的状态为:" + origi.getValue());  
              }  
          }  
          
          

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

          20、状态模式 

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=

          State类是个状态类,Context类可以?#36842;?#20999;换,我们来看?#21019;?#30721;:

          //状态类
          public class State {  
                
              private String value;  
                
              public String getValue() {  
                  return value;  
              }  
            
              public void setValue(String value) {  
                  this.value = value;  
              }  
            
              public void method1(){  
                  System.out.println("execute the first opt!");  
              }  
                
              public void method2(){  
                  System.out.println("execute the second opt!");  
              }  
          }  
          //状态切换类
          public class Context {  
            
              private State state;  
            
              public Context(State state) {  
                  this.state = state;  
              }  
            
              public State getState() {  
                  return state;  
              }  
            
              public void setState(State state) {  
                  this.state = state;  
              }  
            
              public void method() {  
                  if (state.getValue().equals("state1")) {  
                      state.method1();  
                  } else if (state.getValue().equals("state2")) {  
                      state.method2();  
                  }  
              }  
          }  
          
          //测试类
          public class Test {  
            
              public static void main(String[] args) {  
                    
                  State state = new State();  
                  Context context = new Context(state);  
                    
                  //设置第一种状态  
                  state.setValue("state1");  
                  context.method();  
                    
                  //设置第二种状态  
                  state.setValue("state2");  
                  context.method();  
              }  
          }  
          

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

          通过中间类

          21、访?#25910;?#27169;式

          访?#25910;?#27169;式把数据结构和作用于结构?#31995;?#25805;作解耦合,使得操作集合可相对自由地演化。访?#25910;?#27169;式适用于数据结构相对稳定算法又易变化的系统。因为访?#25910;?#27169;式使得算法操作增加变得容易。若系统数据结构对象?#23376;?#21464;化,经常有新的数据对象增加进来,则不适合使用访?#25910;?#27169;式。访?#25910;?#27169;式的优点是增?#30828;?#20316;很容易,因为增?#30828;?#20316;意味着增加新的访?#25910;摺?#35775;?#25910;?#27169;式将有关行为集中到一个访?#25910;?#23545;象中,其改变不影响系统数据结构。其缺点就是增加新的数据结构很困?#36873;?mdash;— From 百科

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

          技术分享?#35745;? src=

          技术分享?#35745;? src=


          public interface Visitor { public void visit(Subject sub); } public class MyVisitor implements Visitor { @Override public void visit(Subject sub) { System.out.println("visit the subject:"+sub.getSubject()); } } // Subject 类 public interface Subject { public void accept(Visitor visitor); public String getSubject(); } public class MySubject implements Subject { @Override public void accept(Visitor visitor) { visitor.visit(this); } @Override public String getSubject() { return "love"; } } //测试 public class Test { public static void main(String[] args) { Visitor visitor = new MyVisitor(); Subject sub = new MySubject(); sub.accept(visitor); } }

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

          22、中介者模式

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

          技术分享?#35745;? src=

           

          技术分享?#35745;? src=

          User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其?#36842;?#31867;,里面持有User1和User2的?#36947;?#29992;来?#36842;?#23545;User1和User2?#30446;?#21046;。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!基本?#36842;鄭?/p>

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

          23、解释器模式

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

          技术分享?#35745;? src=

           

          Context类是一个上下文环境类,Plus和Minus分别是用来计算的?#36842;鄭?#20195;码如下:

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

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

          技术分享?#35745;? src=

          Java设计模式学习

          标签:observer   else if   情况下   做?#31859;?#24049;   infa   策略   source   variable   变更   

          原文:https://www.cnblogs.com/tenglei/p/10466138.html

          (0)
          (0)
             
          举报
          评论 一句话评论(0
          0条  
          登录后才能评论!
          ? 2014 bubuko.com 版权所有 鲁ICP备09046678号-4
          打开技术之扣,分享程序人生!
                       

          ?#24443;?#32593;安备 37021202000002号

          t6娱乐平台官方
          <blockquote id="shwb3"><ruby id="shwb3"></ruby></blockquote>
        2. <small id="shwb3"><strong id="shwb3"></strong></small>

            1. <big id="shwb3"></big>
              1. <blockquote id="shwb3"><ruby id="shwb3"></ruby></blockquote>
              2. <small id="shwb3"><strong id="shwb3"></strong></small>

                  1. <big id="shwb3"></big>