2.5 万字详解:23 种设计模式

Posted Java后端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2.5 万字详解:23 种设计模式相关的知识,希望对你有一定的参考价值。

Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂,没有实际意义。2 不同的产品需要不同额外参数的时候 不支持。

6.再升级(重要)

(1)工厂类:

public class FactoryPlusPlus 

    /**<bean id="applePhone" />
     * 熟悉吧!!!spring ioc 就是通过将下面的这句话配置在配置文件中,再利用反射创建对象,
     * 这就是spring ioc的原理:工厂+配置文件+反射!!以达到彻底解耦的目的**/

    private static String className="com.wander.design.simplefactory.product.ApplePhoneImpl";

    public static Phone getPhone() throws Exception
        return (Phone) Class.forName(className).newInstance();
    


(2) 客户端:

@Test
    public void test3() throws Exception
        Phone phone = FactoryPlusPlus.getPhone();
        phone.produce();
    

(3)说明:spring ioc容器的原理就是这种方式:工厂+配置文件+反射,spring通过读取配置文件(<bean id="applePhone" />),获取到className再利用反射机制Class.forName(className).newInstance()得到对象赋值给配置文件里bean标签的id属性的值,就是工厂生成的对象名。

优点:就是满足OCP原则,在不修改源代码的前提下切换底层的实现,达到解耦的目的!

7.开发常用版本:多方法工厂

使用以上两种方法的工厂,都有两个缺点:一是不同的产品需要不同额外参数的时候不支持。二是如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。

而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。

(1)工厂类代码如下:

public class FactoryMoreMethod 

    public static Phone getApple()
        return new ApplePhoneImpl();
    

    public static Phone getRedmi()
        return new RedmiPhoneImpl();
    

    /**新增华为手机产品,只需要在工厂中增加一个静态方法即可,不需要修改原有的方法**/
    public static Phone getHonor()
        return new HonorPhoneImpl();
    

(2)客户端代码:

@Test
    public void test3() throws Exception
        Phone apple = FactoryMoreMethod.getApple();
        apple.produce();

        Phone redmi = FactoryMoreMethod.getRedmi();
        redmi.produce();

        Phone honor = FactoryMoreMethod.getHonor();
        honor.produce();
    

(3)应用场景:查看java源码:java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式:例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写。

public static ExecutorService newFixedThreadPool(int nThreads) 
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    

又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:

public static ExecutorService newWorkStealingPool(int parallelism) 
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    

(4)总结:多方法工厂的优势,方便创建同种类型的复杂参数对象。

8.应用场景

(1)在任何需要生成复杂对象的地方,都可以使用工厂方法模式。直接用new可以完成的不需要用工厂模式个人理解,重点就是这个复杂 (构造函数有很多参数)和 是否可以 直接用new。

(2)客户端只知道传入工厂类的参数,对于如何创建对象并不关心。(对于升级后的简单工厂模式只知道类名即可) 

(3)工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。(对于升级后的简单工厂模式已解决这个问题,符合开闭原则) 

(4)简单工厂在源码中的使用--Calendar:

Calendar cal = Calendar.getInstance(zone.toTimeZone(), locale);
public static Calendar getInstance(TimeZone zone, Locale aLocale)
      return createCalendar(zone, aLocale);
  

  private static Calendar createCalendar(TimeZone zone, Locale aLocale)
      \'部分删减\'
      Calendar cal = null;

      if (aLocale.hasExtensions())
          String caltype = aLocale.getUnicodeLocaleType("ca");
          if (caltype != null)
              switch (caltype)
              case "buddhist":
              cal = new BuddhistCalendar(zone, aLocale);
                  break;
              case "japanese":
                  cal = new JapaneseImperialCalendar(zone, aLocale);
                  break;
              case "Gregory":
                  cal = new GregorianCalendar(zone, aLocale);
                  break;
              
          
      
      if (cal == null)
          if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH")
              cal = new BuddhistCalendar(zone, aLocale);
           else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                     && aLocale.getCountry() == "JP")
              cal = new JapaneseImperialCalendar(zone, aLocale);
           else 
              cal = new GregorianCalendar(zone, aLocale);
          
      
      return cal;
  
(2)工厂方法模式

1.认识

①一句话来说就是,定义一个创建对象的接口,让子类决定实例化那个类。因为当需要增加一个新的产品时,我们需要增加一个具体的产品类和与之对应的具体子工厂,然后在具体子工厂方法中进行对象实例化,所以称为工厂方法模式。

②具体来说就是定义一个用于创建对象的工厂接口,但让实现这个工厂接口的子类来决定实例化哪个具体产品类,工厂方法让类的实例化推迟到子类中进行。

③工厂方法模式非常符合“开闭原则”,当需要增加一个新的产品时,我们只需要增加一个具体的产品类和与之对应的具体工厂即可,无须修改原有系统。同时在工厂方法模式中用户只需要知道生产产品的具体工厂即可,无须关系产品的创建过程,甚至连具体的产品类名称都不需要知道。

④虽然他很好的符合了“开闭原则”,但是由于每新增一个新产品时就需要增加两个类,这样势必会导致系统的复杂度增加。

2.UML类图

UML说明:苹果手机和红米手机实现了手机这个抽象类,苹果工厂和红米工厂实现了抽象工厂,苹果工厂当然要生产(依赖)苹果手机,红米工厂当然要生产(依赖)红米。客户要买苹果手机要去问苹果工厂要苹果手机,客户要买红米手机当然要去问红米工厂要红米手机。

工厂方法有四个对象:抽象产品类:提供抽象方法供具体产品类实现 具体产品类:提供具体的产品 抽象工厂:提供抽象方法供具体工厂实现 具体工厂:提供具体的工厂

3.代码实现

(1)抽象产品类和简单工厂的抽象产品类一样

(2)具体产品类和简单工厂的具体产品类一样 

(3)抽象工厂

public interface Factory 
    Phone getPhone();

(4)具体工厂

public class AppleFactoryImpl implements Factory

    @Override
    public Phone getPhone() 
        return new ApplePhoneImpl();
    


public class RedmiFactoryImpl implements Factory
    @Override
    public Phone getPhone() 
        return new RedmiPhoneImpl();
    

(5)客户端

@Test
public void test1()


        Factory applePhoneFactory = new AppleFactoryImpl();
        Factory redmiPhoneFactory = new RedmiFactoryImpl();

        Phone applePhone = applePhoneFactory.getPhone();
        Phone redmiPhone = redmiPhoneFactory.getPhone();

        System.out.println(applePhone);
        System.out.println(redmiPhone);

        applePhone.produce();
        redmiPhone.produce();
    

执行结果如下:

4.总结

优点:①用户只需要关心所需产品的对应工厂,无需关心细节 

②完全支持开闭原则,提高可扩展性。所谓的开闭原则就是对扩展开放,对修改关闭,再说白点就是实现工厂方法以后要进行扩展时不需要修改原有代码,只需要增加一个工厂实现类和产品实现类就可以。这样的好处可以降低因为修改代码引进错误的风险。

缺点:①每加入一种产品,会创建一个具体工厂类和具体产品类,因此,类的个数容易过多,增加复杂度。

②抽象工厂和抽象产品增加了系统的抽象性和理解难度

5.工厂方法与简单工厂的区别

①可以看出,工厂方法模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。

②工厂方法使一个产品类的实例化延迟到其具体工厂子类. 

③工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类。

④而简单工厂需要修改工厂类的方法,多方法静态工厂模式需要增加一个静态方法。

缺点:引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐多方法静态工厂,每次新增一个具体产品类,工厂只需要新增一个静态方法

6.应用场景

(1)客户端不知道它所需要的对象的类。(需要知道所需的对象的类使用升级版简单工厂模式,需要知道所需的参数的类使用简单工厂模式)。

(2)抽象工厂类通过其子类来指定创建哪个对象。

(3)简单工厂在源码中的使用--Collection:Collection(抽象工厂):

public interface Collection<E> extends Iterable<E> 
 Iterator<E> iterator();

ArrayList(具体工厂):

public class ArrayList<E>
    public Iterator<E> iterator()
        return new Itr();
    

Iterator(抽象产品):

public interface Iterator<E> 
 boolean hasNext();

Itr(具体产品):

private class Itr implements Iterator<E> 
 int cursor; // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() 
            return cursor != size;
    
    \'省略代码...\'
(3)抽象工厂模式

1.认识

①一句话来说就是,创建相关或依赖对象的家族,而无需明确指定具体类。因为我们可以定义具体产品类实现不止一个抽象工厂接口,一个工厂也可以生成不止一个产品类,是三个模式中较为抽象,并具一般性的模式。我们在使用中要注意使用抽象工厂模式的条件。

②所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更改接口及其下所有子类。

2.UML类图

UML说明:具体的苹果手机产品和具体的红米手机产品实现了手机产品抽象类,具体的苹果充电器产品和具体的红米充电器产品实现了充电器产品抽象类。具体的苹果工厂和具体的红米工厂实现了手机抽象工厂,然后苹果工厂生产苹果手机和苹果充电器,红米工厂生成红米手机和红米充电器。客户想要苹果手机和苹果充电器就要向苹果工厂要产品(对象),客户想要红米手机和红米充电器就要向红米工厂要产品(对象)。

工厂方法有四个对象:

抽象产品类:为每种具体产品声明接口,如图中Phone手机抽象类和Charger充电器抽象类 

具体产品类:定义了工厂生产的具体产品对象,实现抽象产品接口声明的业务方法,如图中ApplePhoneImpl、RedmiPhoneImpl,AppleChargerImpl,RedmiChargerImpl

抽象工厂:它声明了一组用于创建一种产品的方法,每一个方法对应一种产品,如上述类图中的Factory就定义了两个方法,分别创建Phone和Charger

具体工厂:它实现了在抽象工厂中定义的创建产品的方法,生产一组具体产品,这组产品构件成了一个产品种类,每一个产品都位于某个产品等级结构中,如上述类图中的AppleFactoryImpl和RedmiFactoryImpl

3.代码实现

(1)抽象的产品

public interface Phone 
    void produce();


public interface Charger 
    void produce();

(2)具体的产品 

① 苹果具体的产品

public class AppleChargerImpl implements Charger

    @Override
    public void produce() 
        System.out.println("生产苹果充电器");
    


public class ApplePhoneImpl implements Phone 
    @Override
    public void produce() 
        System.out.println("生产苹果手机");
    

② 红米具体的产品

public class RedmiChargerImpl implements Charger
    @Override
    public void produce() 
        System.out.println("生产红米充电器");
    



public class RedmiPhoneImpl implements Phone 
    @Override
    public void produce() 
        System.out.println("生产了红米手机");
    

(3)抽象工厂

public interface Factory 
    Phone getPhone();

    Charger getCharger();

(4)具体的工厂

public class AppleFactoryImpl implements Factory 

    @Override
    public Phone getPhone() 
        return new ApplePhoneImpl();
    

    @Override
    public Charger getCharger() 
        return new AppleChargerImpl();
    


public class RedmiFactoryImpl implements Factory 
    
    @Override
    public Phone getPhone() 
        return new RedmiPhoneImpl();
    

    @Override
    public Charger getCharger() 
        return new RedmiChargerImpl();
    


(5)客户端

@Test
    public void test1()

        Factory appleFactory = new AppleFactoryImpl();
        Phone applePhone = appleFactory.getPhone();
        Charger appleCharger = appleFactory.getCharger();
        System.out.println(appleFactory);
        applePhone.produce();
        appleCharger.produce();

        Factory redmiFactory = new RedmiFactoryImpl();
        Phone redmiPhone = redmiFactory.getPhone();
        Charger redmiCharger = redmiFactory.getCharger();
        System.out.println(redmiFactory);
        redmiPhone.produce();
        redmiCharger.produce();

    


(5)执行结果

4.总结

优点:

①具体产品在应用层代码隔离,无须关系创建细节 

②将一个系列的产品统一到一起创建

③对于增加新的产品族(一个具体工厂就是一个产品族),抽象工厂模式很好地支持了“开闭原则”,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。

缺点:①规定了所有可能被创建的产品集合,产品族扩展新的产品(工厂中添加新的方法)困难。如果产品族扩展新的产品,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。

②增加了系统的抽象性和理解难度

5.应用场景

抽象工厂在实际的开发中运用并不多,主要是在开发工程中很少会出现多个产品种类的情况,大部分情况使用以上两种工厂模式即可解决

6.个人总结

一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用建造者Builder模式)

(4)单例模式

1.认识

①一句话来说就是,某个类只能有一个实例,提供一个全局的访问点。

②单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。

③使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection),而且确保所有对象都访问唯一实例。但是不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。用单例模式,就是在适用其优点的状态下使用

2.UML类图

UML说明:1.构造方法私有化:可以使得该类不被实例化即不能被new 2.在类本身里创建自己的对象 3.提供一个公共的方法供其他对象访问

3.代码实现

(1)饿汉式

①第一种:

public class Singleton 

    /**
     * static:
     * ①表示共享变量,语意符合
     * ②使得该变量能在getInstance()静态方法中使用
     * final:
     * ①final修饰的变量值不会改变即常量,语意也符合,当然不加final也是可以的
     * ②保证修饰的变量必须在类加载完成时就已经进行赋值。
     * final修饰的变量,前面一般加static
     */

    private static final Singleton singleton = new Singleton();

    /**
     * 私有化构造方法,使外部无法通过构造方法构造除singleton外的类实例
     * 从而达到单例模式控制类实例数目的目的
     */

    private Singleton()

    /**
     * 类实例的全局访问方法
     * 因为构造方法以及被私有化,外部不可能通过new对象来调用其中的方法
     * 加上static关键词使得外部可以通过类名直接调用该方法获取类实例
     * @return
     */

    public static Singleton getSingleton()
        return singleton;
    

②第二种

public class SingletonStatic 

    private static final SingletonStatic singletonStatic;

    /**
     * 和第一种没有什么区别,这种看起来高大上面试装逼使用
     */

    static 
        singletonStatic = new SingletonStatic();
    

    private SingletonStatic() 

    public static SingletonStatic getSingletonStatic()
        return singletonStatic;
    
说明:①优点:一般使用static和final修饰变量(具体作用已经在代码里描述了),只在类加载时才会初始化,以后都不会,线程绝对安全,无锁,效率高。
②缺点:类加载的时候就初始化,不管用不用,都占用空间,会消耗一定的性能(当然很小很小,几乎可以忽略不计,所以这种模式在很多场合十分常用而且十分简单)

:这里有两个小知识点:

a.如果是final非static成员,必须在构造器、代码块、或者直接定义赋值

b.如果是final static 成员变量,必须直接赋值 或者在静态代码块中赋值

(2)懒汉式
public class Singleton 

    private static Singleton singleton = null;

    private Singleton()

    public static Singleton getSingleton()
        if(singleton == null)
            singleton = new Singleton();
        
        return singleton;
    


说明:

①优点:在外部需要使用的时候才进行实例化,不使用的时候不会占用空间。

②缺点:线程不安全。看上去,这段代码没什么明显问题,但它不是线程安全的。假设当前有N个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入if语句new Singleton(),那么就会由多个线程创建多个多个user对象。

(3)线程安全的懒汉式
public class Singleton 

    private static Singleton singleton;

    private Singleton();

    private static synchronized Singleton getSingleton()
        if(singleton == null)
            singleton = new Singleton();
        
        return singleton;
    

说明:①优点:解决了懒汉式线程不安全的问题 ②缺点:线程阻塞,影响性能。

(4)DCL单例 - 高性能的懒汉式
public class Singleton 
 /*volatile在这里发挥的作用是:禁止指令重排序(编译器和处理器为了优化程序性能
    * 而对指令序列进行排序的一种手段。)
    * singleton = new Singleton();这句代码是非原子性操作可分为三行伪代码
    * a:memory = allocate() //分配内存,在jvm堆中分配一段区域
    * b:ctorInstanc(memory) //初始化对象,在jvm堆中的内存中实例化对象
    * c:instance = memory //赋值,设置instance指向刚分配的内存地址
    * 上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。
    * 重排序是为了优化性能,但是不管怎么重排序,在单线程下程序的执行结果不能被改变
    * 保证最终一致性。而在多线程环境下,可能发生重排序,会影响结果。
    * ①若A线程执行到代码singleton = new Singleton()时;
    * ②同时若B线程进来执行到代码到第一层检查if (singleton == null)
    * ③当cpu切换到A线程执行代码singleton = new Singleton();时发生了指令重排序,
    * 执行了a-b,没有执行c,此时的singleton对象只有地址,没有内容。然后cpu又切换到了B线程,
    * 这时singleton == null为false(==比较的是内存地址),
    * 则代码会直接执行到了return,返回一个未初始化的对象(只有地址,没有内容)。
    * */

    private volatile static Singleton singleton;

    private Singleton() 
    

    public static Singleton getSingleton() 
        /*第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入
        * ①当多个线程第一次进入,所有线程都进入if语句
        * ②当多个线程第二次进入,因为singleton已经不为null,因此所有线程都不会进入if语句,
        * 即不会执行锁,从而也就不会因为锁而阻塞,避免锁竞争*/

        if (singleton == null)
            /*第一层锁,保证只有一个线程进入,
            * ①多个线程第一次进入的时候,只有一个线程会进入,其他线程处于阻塞状态
            * 当进入的线程创建完对象出去之后,其他线程又会进入创建对象,所以有了第二次if检查
            * ②多个线程第二次是进入不到这里的,因为已被第一次if检查拦截*/

            synchronized (Singleton.class)
                /*第二层检查,防止除了进入的第一个线程的其他线程重复创建对象*/
                if (singleton == null)
                    singleton = new Singleton();
                
            
        
        return singleton;
    

说明:代码注释已详细讲解volatile在该单例模式的作用,已经双重锁的作用。①优点:解决了线程阻塞的问题 ②缺点:多个线程第一次进入的时候会造成大量的线程阻塞,代码不够优雅。

(5)静态内部类的方式
public class Singleton 

    private Singleton()

    private static class LayzInner
        private static Singleton singleton = new Singleton();
    
    
    public static Singleton getSingleton()
        return LayzInner.singleton;
    

说明:①优点:第一次类创建的时候加载,避免了内存浪费,不存在阻塞问题,线程安全,唯一性 ②缺点:序列化-漏洞:反射,会破坏内部类单例模式

(6)枚举单例模式
public enum EnumSingleton 
    INSTANCE;
    private Singleton singleton;
    EnumSingleton()
        singleton = new Singleton();
    
    public Singleton getSingleton()
        return singleton;
    

说明:单元素的枚举类型已经成为实现Singleton的最佳方法,无法反射创建对象,但是特殊的饿汉式。

(7)静态内部类升级版

借鉴枚举单例的内部实现的方式

public class Singleton 
    private Singleton()
        if(LayzInner.singleton != null)
            throw new RuntimeException("不能够进行反射!");
        
    

    private static class LayzInner
        private static Singleton singleton = new Singleton();
    

    public static Singleton getSingleton ()
        return LayzInner.singleton;
    

说明:①优点:第一次类创建的时候加载,避免了内存浪费,不存在阻塞问题,线程安全,唯一性,解决了反射会破坏内部类单例模式的问题

②缺点:不是官方的

(8)容器式单例
public class Singleton 
    private Singleton()
    

    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className)
        synchronized (ioc)
            if (ioc.containsKey(className))
                Object o = null;
                try 
                    o = Class.forName(className).newInstance();
                 catch (Exception e)
                    e.printStackTrace();
                
                return o;
             else 
                return ioc.get(className);
            
        
    


说明:Spring ioc 单例 是懒汉式 枚举上的升级

(9)ThreadLocal单例

说明:局部单例模式:某一个线程里唯一 

①ThreadLocal的作用呢,是提供线程内的局部变量,在多线程环境访问时,能保证各个线程内的ThreadLocal变量各自独立。也就是说每个线程的ThreadLocal变量是自己专用的,其他线程是访问不到的。

②ThreadLocal最常用于在多线程环境下存在对非线程安全对象的并发访问,而且该对象不需要在线程内共享,如果对该对象加锁,会造成大量线程阻塞影响程序性能,这时候就可以使用ThreadLocal来使每个线程都持有该对象的副本,这是典型的空间换取时间从而提高执行效率的方式。例如项目里经常使用的SimpleDateFormat日期格式化对象,该对象是线程不安全的,而且不需要在线程内共享,因此可以使用ThreadLocal保证其线程安全。

(5)原型模式

1.认识

①一句话来说就是,通过复制现有的实例来创建新的实例。因为是同过原有的对象创建新的对象,所以称为原型模式。

②原型模式是用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。原型模式允许一个对象再创建另外一个可定制的对象,无须知道任何创建的细节。

③用于创建重复的对象,同时又能保证性能。

(1)浅拷贝:我们只拷贝对象中的基本数据类型(8种),对于数组、容器、引用对象等都不会拷贝,只会拷贝对这些对象的引用。

(2)深拷贝:不仅能拷贝基本数据类型,还能拷贝那些数组、容器、引用对象(不仅拷贝对这些对象的引用,而且拷贝对象本身)。

2.UML类图

UML说明:实体类实现Cloneable接口,重写clone方法

3.代码实现

(1)Prototype类:

public class Prototype implements Cloneable 

    private Integer id;
    private String name;
    private Map<String, Double> map;

    @Override
    protected Prototype clone() throws CloneNotSupportedException

        //浅拷贝方式
        Prototype prototype = (Prototype) super.clone();
        //深拷贝方式:对每一个复杂类型分别进行克隆
        //测试浅拷贝的时候注释下面代码
        prototype.map = (Map<String, Double>) ((HashMap)this.map).clone();
        return prototype;
    

    public Prototype(Integer id, String name, Map<String, Double> map) 
        this.id = id;
        this.name = name;
        this.map = map;
    
/**省略get、set方法和toString方法*/


(2)客户端:

public class Client 
    @Test
    public void test() throws CloneNotSupportedException

        Map<String, Double> map = new HashMap<>();
        map.put("数学",100D);
   &nb

Java设计模式之十一种行为型模式(附实例和详解)

Java经典设计模式共有21中,分为三大类:创建型模式(5种)、结构型模式(7种)和行为型模式(11种)。

本文主要讲行为型模式,创建型模式和结构型模式可以看博主的另外两篇文章:J设计模式之五大创建型模式(附实例和详解)、 设计模式之七大结构型模式(附实例和详解)。

行为型模式细分为如下11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
技术分享图片
接下来对11种行为型模式逐个进行介绍。

一、策略模式

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

首先统一接口:

package com.model.behaviour;

public interface ICalculator {
    public int calculate(String exp);
}
  • 1
  • 2
  • 3
  • 4
  • 5

辅助类:

package com.model.behaviour;

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

三个实现类:

package com.model.behaviour;

public class Plus extends AbstractCalculator implements ICalculator {

    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\+");
        return arrayInt[0] + arrayInt[1];
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
package com.model.behaviour;

public class Minus extends AbstractCalculator implements ICalculator {

    @Override
    public int calculate(String exp) {
        int arrayInt[] = split(exp, "\\-");
        return arrayInt[0] - arrayInt[1];
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
package com.model.behaviour;

public class Multiply extends AbstractCalculator implements ICalculator {  

    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\*");  
        return arrayInt[0]*arrayInt[1];  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

测试类:

package com.model.behaviour;

public class StrategyTest {

    public static void main(String[] args) {
        String exp = "8-2";
        ICalculator cal = new Minus();
        int result = cal.calculate(exp);
        System.out.println(exp + "=" + result);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

二、模板方法模式

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

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

package com.model.behaviour;

public abstract class AbstractCalculator {  

    /*主方法,实现对本类其它方法的调用*/  
    public final int calculate(String exp,String opt){  
        int array[] = split(exp,opt);  
        return calculate(array[0],array[1]);  
    }  

    /*被子类重写的方法*/  
    abstract public int calculate(int num1,int num2);  

    public int[] split(String exp,String opt){  
        String array[] = exp.split(opt);  
        int arrayInt[] = new int[2];  
        arrayInt[0] = Integer.parseInt(array[0]);  
        arrayInt[1] = Integer.parseInt(array[1]);  
        return arrayInt;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
package com.model.behaviour;

public class Plus extends AbstractCalculator {  

    @Override  
    public int calculate(int num1,int num2) {  
        return num1 + num2;  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
package com.model.behaviour;

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);  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

三、观察者模式

包括这个模式在内的接下来的四个模式,都是类和类之间的关系,不涉及到继承。

观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

package com.model.behaviour;

public interface Observer {  
    public void update();  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
package com.model.behaviour;

public class Observer1 implements Observer {  

    @Override  
    public void update() {  
        System.out.println("observer1 has received!");  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
package com.model.behaviour;

public class Observer2 implements Observer {  

    @Override  
    public void update() {  
        System.out.println("observer2 has received!");  
    }  

}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
package com.model.behaviour;

public interface Subject {  

    /*增加观察者*/  
    public void add(Observer observer);  

    /*删除观察者*/  
    public void del(Observer observer);  

    /*通知所有的观察者*/  
    public void notifyObservers();  

    /*自身的操作*/  
    public void operation();  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
package com.model.behaviour;

import java.util.Enumeration;
import java.util.Vector;

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();  
        }  
    }  
}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
package com.model.behaviour;

public class MySubject extends AbstractSubject {  

    @Override  
    public void operation() {  
        System.out.println("update self!");  
        notifyObservers();  
    }  

}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
package com.model.behaviour;

public class ObserverTest {  

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

        sub.operation();  
    }  

}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

运行结果:

update self!
observer1 has received!
observer2 has received!
  • 1
  • 2
  • 3

也许看完实例之后还是比较抽象,再将文字描述和代码实例看一两遍吧,然后结合工作中看哪些场景可以使用这种模式以加深理解。

四、迭代子模式

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

具体来看看代码实例:

package com.model.behaviour;

public interface Collection {

    public Iterator iterator();

    /* 取得集合元素 */
    public Object get(int i);

    /* 取得集合大小 */
    public int size();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
package com.model.behaviour;

public interface Iterator {
    // 前移
    public Object previous();

    // 后移
    public Object next();

    public boolean hasNext();

    // 取得第一个元素
    public Object first();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
package com.model.behaviour;

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
package com.model.behaviour;

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);  
    }  

}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
package com.model.behaviour;

public class Test {  

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

输出结果:

A
B
C
D
E
  • 1
  • 2
  • 3
  • 4
  • 5

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

五、责任链模式

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

package com.model.behaviour;

public interface Handler {  
    public void operator();  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
package com.model.behaviour;

public abstract class AbstractHandler {

    private Handler handler;

    public Handler getHandler() {
        return handler;
    }

    public void setHandler(Handler handler) {
        this.handler = handler;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
package com.model.behaviour;

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();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
package com.model.behaviour;

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

运行结果:

h1deal!
h2deal!
h3deal!
  • 1
  • 2
  • 3

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

六、命令模式

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

package com.model.behaviour;

public interface Command {  
    public void exe();  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
package com.model.behaviour;

public class MyCommand implements Command {  

    private Receiver receiver;  

    public MyCommand(Receiver receiver) {  
        this.receiver = receiver;  
    }  

    @Override  
    public void exe() {  
        receiver.action();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
package com.model.behaviour;

public class Invoker {

    private Command command;

    public Invoker(Command command) {
        this.command = command;
    }

    public void action() {
        command.exe();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
package com.model.behaviour;

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();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

七、备忘录模式

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

package com.model.behaviour;

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();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
package com.model.behaviour;

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
package com.model.behaviour;

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;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
package com.model.behaviour;

public class Test {

    public static void main(String[] args) {

        // 创建原始类
        Original origi = new Original("egg");

        // 创建备忘录
        Storage storage = new Storage(origi.createMemento());

        // 修改原始类的状态
        System.out.println("初始化状态为:" + origi.getValue());
        origi.setValue("niu");
        System.out.println("修改后的状态为:" + origi.getValue());

        // 回复原始类的状态
        origi.restoreMemento(storage.getMemento());
        System.out.println("恢复后的状态为:" + origi.getValue());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

输出结果:

初始化状态为:egg
修改后的状态为:niu
恢复后的状态为:egg
  • 1
  • 2
  • 3

如果还不能理解,可以给Original类添加一个属性name,然后其他类进行相应的修改试试。

八、状态模式

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

package com.model.behaviour;

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!");  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
package com.model.behaviour;

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() {
        System.out.println("状态为:" + state.getValue());
        if (state.getValue().equals("state1")) {
            state.method1();
        } else if (state.getValue().equals("state2")) {
            state.method2();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
package com.model.behaviour;

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();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行结果:

状态为:state1
execute the first opt!
状态为:state2
execute the second opt!
  • 1
  • 2
  • 3
  • 4

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

九、访问者模式

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

访问者模式算是最复杂也是最难以理解的一种模式了。它表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

涉及角色:

1.Visitor 抽象访问者角色:为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
2.ConcreteVisitor.具体访问者角色:实现Visitor声明的接口。
3.Element 定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
4.ConcreteElement 具体元素:实现了抽象元素(Element)所定义的接受操作接口。
5.ObjectStructure 结构对象角色:这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
abstract class Element
{
    public abstract void accept(IVisitor visitor);
    public abstract void doSomething();
}
  • 1
  • 2
  • 3
  • 4
  • 5
class ConcreteElement1 extends Element{
    public void doSomething(){
        System.out.println("这是元素1");
    }
    public void accept(IVisitor visitor){
        visitor.visit(this);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
class ConcreteElement2 extends Element{
    public void doSomething(){
        System.out.println("这是元素2");
    }
    public void accept(IVisitor visitor){
        visitor.visit(this);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
interface IVisitor{
    public void visit(ConcreteElement1el1);
    public void visit(ConcreteElement2el2);
}
  • 1
  • 2
  • 3
  • 4
class Visitor implements IVisitor{
    public void visit(ConcreteElement1 el1){
        el1.doSomething();
    }
    public void visit(ConcreteElement2 el2){
        el2.doSomething();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
class ObjectStruture{
    public static List<Element> getList(){
        List<Element>list = new ArrayList<Element>();
        Random ran = newRandom();
        for(int i = 0 ; i < 10 ; i ++){
            int a=ran.nextInt(100);
            if(a>50){
                list.add (newConcreteElement1());
            }else{
                list.add (newConcreteElement2());
            }
        }
        return list;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
public class Client{
    public static void main (String[]args){
        List<Element> list = ObjectStruture.getList();
        for(Elemente:list){
            e.accept(newVisitor());
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

十、中介者模式

中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

举例:在一个公司里面,有很多部门、员工(我们统称他们互相为Colleague“同事”),为了完成一定的任务,“同事”之间肯定有许多需要互相配合、交流的过程。如果由各个“同事”频繁地到处去与自己有关的“同事”沟通,这样肯定会形成一个多对多的杂乱的联系网络而造成工作效率低下。

此时就需要一位专门的“中介者”给各个“同事”分配任务,以及统一跟进大家的进度并在“同事”之间实时地进行交互,保证“同事”之间必须的沟通交流。很明显我们知道此时的“中介者”担任了沟通“同事”彼此之间的重要角色了,“中介者”使得每个“同事”都变成一对一的联系方式,减轻了每个“同事”的负担,增强工作效率。

同事类族:

package com.model.behaviour;

public abstract class AbstractColleague {  
    protected AbstractMediator mediator;  

    /**既然有中介者,那么每个具体同事必然要与中介者有联系,  
     * 否则就没必要存在于 这个系统当中,这里的构造函数相当  
     * 于向该系统中注册一个中介者,以取得联系  
     */ 
    public AbstractColleague(AbstractMediator mediator) {  
        this.mediator = mediator;  
    }  

    // 在抽象同事类中添加用于与中介者取得联系(即注册)的方法  
    public void setMediator(AbstractMediator mediator) {  
        this.mediator = mediator;  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
//具体同事A  
package com.model.behaviour;

public class ColleagueA extends AbstractColleague {  

    //每个具体同事都通过父类构造函数与中介者取得联系  
    public ColleagueA(AbstractMediator mediator) {  
        super(mediator);  
    }  

    //每个具体同事必然有自己分内的事,没必要与外界相关联  
    public void self() {  
        System.out.println("同事A --> 做好自己分内的事情 ...");  
    }  

    //每个具体同事总有需要与外界交互的操作,通过中介者来处理这些逻辑并安排工作  
    public void out() {  
        System.out.println("同事A --> 请求同事B做好分内工作 ...");  
        super.mediator.execute("ColleagueB", "self");  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
//具体同事B  
package com.model.behaviour;

public class ColleagueB extends AbstractColleague {  

    public ColleagueB(AbstractMediator mediator) {  
        super(mediator);  
    }  

    public void self() {  
        System.out.println("同事B --> 做好自己分内的事情 ...");  
    }  

    public void out() {  
        System.out.println("同事B --> 请求同事A做好分内工作  ...");  
        super.mediator.execute("ColleagueA", "self");  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

中介者类族:

package com.model.behaviour;

public abstract class AbstractMediator {  

    //中介者肯定需要保持有若干同事的联系方式  
    protected Hashtable<String, AbstractColleague> colleagues = new Hashtable<String, AbstractColleague>();  

    //中介者可以动态地与某个同事建立联系  
    public void addColleague(String name, AbstractColleague c) {  
        this.colleagues.put(name, c);  
    }     

    //中介者也可以动态地撤销与某个同事的联系  
    public void deleteColleague(String name) {  
        this.colleagues.remove(name);  
    }  

    //中介者必须具备在同事之间处理逻辑、分配任务、促进交流的操作  
    public abstract void execute(String name, String method);   
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
//具体中介者  
package com.model.behaviour;

public class Mediator extends AbstractMediator{  

    //中介者最重要的功能,来回奔波与各个同事之间  
    public void execute(String name, String method) {  

        if("self".equals(method)){  //各自做好分内事  
            if("ColleagueA".equals(name)) {  
                ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");  
                colleague.self();  
            }else {  
                ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");  
                colleague.self();  
            }  
        }else { //与其他同事合作  
            if("ColleagueA".equals(name)) {  
                ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");  
                colleague.out();  
            }else {  
                ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");  
                colleague.out();  
            }  
        }  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

测试类:

//测试类  
package com.model.behaviour;

public class Client {  
    public static void main(String[] args) {  

        //创建一个中介者  
        AbstractMediator mediator = new Mediator();  

        //创建两个同事  
        ColleagueA colleagueA = new ColleagueA(mediator);  
        ColleagueB colleagueB = new ColleagueB(mediator);  

        //中介者分别与每个同事建立联系  
        mediator.addColleague("ColleagueA", colleagueA);  
        mediator.addColleague("ColleagueB", colleagueB);  

        //同事们开始工作  
        colleagueA.self();  
        colleagueA.out();  
        System.out.println("======================合作愉快,任务完成!\n");  

        colleagueB.self();  
        colleagueB.out();  
        System.out.println("======================合作愉快,任务完成!");  
    }  
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

运行结果:

同事A --> 做好自己分内的事情 ...  
同事A --> 请求同事B做好分内工作 ...  
同事B --> 做好自己分内的事情 ...  
======================合作愉快,任务完成!  

同事B --> 做好自己分内的事情 ...  
同事B --> 请求同事A做好分内工作  ...  
同事A --> 做好自己分内的事情 ...  
======================合作愉快,任务完成! 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

十一、解释器模式

解释器模式:给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。

解释器模式是一个比较少用的模式。

package com.model.behaviour;

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;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
package com.model.behaviour;

public interface Expression {  
    public int interpret(Context context);  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
package com.model.behaviour;

public class Minus implements Expression {  

    @Override  
    public int interpret(Context context) {  
        return context.getNum1()-context.getNum2();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
package com.model.behaviour;

public class Plus implements Expression {  

    @Override  
    public int interpret(Context context) {  
        return context.getNum1()+context.getNum2();  
    }  
}  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
package com.model.behaviour;

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注,本文参考了另外一位博主的文章,某些地方有结合自己的一些理解加以修改:
http://blog.csdn.net/zhangerqing/article/details/8194653

http://blog.csdn.net/mbshqqb/article/details/78615152

以上是关于2.5 万字详解:23 种设计模式的主要内容,如果未能解决你的问题,请参考以下文章

23种设计模式-抽象工厂模式

设计模式-简单工厂工厂方法模式抽象工厂模式详解

23种设计模式之抽象工厂

23种设计模式——抽象工厂模式对象创建

23中设计模式之抽象工厂模式

JAVA设计模式——抽象工厂模式案例实现