设计模式之java-学习笔记

设计模式简介

本笔记是根据尚硅谷java设计模式视频学习得

设计模式七大原则

1设计模式的目的

编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好

1)代码重用性 (即:相同功能的代码,不用多次编写)
2)可读性 (即:编程规范性,  便于其他程序员的阅读和理解)

3)可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
4)可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
5)使程序呈现高内聚,低耦合的特性分享金句:
6)设计模式包含了面向对象的精髓,“懂了设计模式,你就懂了面向对象分析和设计(OOA/D)的精要”
7)Scott Mayers 在其巨著《Effective C++》就曾经说过:C++老手和 C++新手的区别就是前者手背上有很多伤疤

2设计模式七大原则

设计模式原则,其实就是程序员在编程时,应当遵守的原则,也是各种设计模式的基础(即:设计模式为什么这样设计的依据)

设计模式常用的七大原则有:
1)单一职责原则
2)接口隔离原则
3)依赖倒转(倒置)原则
4)里氏替换原则
5)开闭原则
6)迪米特法则
7)合成复用原则

                                                    设计模式概述
1掌握设计模式的层次
1)第 1 层:刚开始学编程不久,听说过什么是设计模式
2)第 2 层:有很长时间的编程经验,自己写了很多代码,其中用到了设计模式,但是自己却不知道

3)第 3 层:学习过了设计模式,发现自己已经在使用了,并且发现了一些新的模式挺好用的
4)第 4 层:阅读了很多别人写的源码和框架,在其中看到别人设计模式,并且能够领会设计模式的精妙和带来的好处。
5)第 5 层:代码写着写着,自己都没有意识到使用了设计模式,并且熟练的写了出来。

2设计模式介绍
1)设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

2)设计模式的本质提高 软件的维护性,通用性和扩展性,并降低软件的复杂度。
3)<<设计模式>> 是经典的书,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称  “四人组  GOF”)
4)设计模式并不局限于某种语言,java,php,c++ 都有设计模式.

3设计模式类型
设计模式分为三种类型,共 23 种

1)创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。



2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3)行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter 模式)、状态模式、策略模式、职责链模式(责任链模式)。
注意:不同的书籍上对分类和名称略有差别

详细解释代码中也有

一 单例设计模式

1单例设计模式介绍
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)。


比如 Hibernate 的 SessionFactory,它充当数据存储源的代理,并负责创建 Session 对象。SessionFactory 并不是轻量级的,一般情况下,一个项目通常只需要一个 SessionFactory 就够,这是就会使用到单例模式。

2单例设计模式八种方式
单例模式有八种方式:

1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步方法)
5)懒汉式(线程安全,同步代码块)
6)双重检查
7)静态内部类
8)枚举


单例模式在JDK应用的源码分析单例模式在JDK应用的源码分析
1)我们JDK中,java.lang.Runtime就是经典的单例模式2)代码分析+Debug源码+代码说明



单例模式注意事项和细节说明
1)单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需
要频繁创建销毁的对象,使用单例模式可以提高系统性能
2)当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使
用new
3)单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或
耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)

饿汉单列模式

package com.fs.sjms.single;

/**
 * 饿汉单列模式
 */
public class EagerSingleton {
    public static void main(String[] args) {
        //测试 饿汉式(静态常量)
/*        EagerSingletonStaticConstant instance = EagerSingletonStaticConstant.getInstance();
        EagerSingletonStaticConstant instance2 = EagerSingletonStaticConstant.getInstance();
        System.out.println(instance);
        System.out.println(instance2);//输出的地址值一致,说明是同一个对象*/

        //测试 饿汉式(静态代码块)
/*        EagerSingletonStaticCode instance = EagerSingletonStaticCode.getInstance();
        EagerSingletonStaticCode instance2 = EagerSingletonStaticCode.getInstance();
        System.out.println(instance);
        System.out.println(instance2);//输出的地址值一致,说明是同一个对象*/

    }

}


/**
 * 饿汉式(静态常量)
 * 步骤:
 * 1 构造私有化 (防止直接new来创建对象)
 * 2 类的内部创建对象
 * 3 向外暴露一个静态的公共方法
 *
 * 优缺点
 * 优缺点说明:
 * 1)优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
 * 2)缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始
 * 至终从未使用过这个实例,则会造成内存的浪费
 * 3)这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载
 * 时就实例化,在单例模式中大多数都是调用getlnstance方法,但是导致类装载的原因有很多种,
 * 因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
 * 4)结论:这种单例模式可用,可能造成内存浪费
 */
class EagerSingletonStaticConstant {
    // * 2 类的内部创建对象
    private final static EagerSingletonStaticConstant eagerSingletonStaticConstant = new EagerSingletonStaticConstant();
    // * 1 构造私有化 (防止直接new来创建对象)
    private EagerSingletonStaticConstant() {
    }
    // * 3 向外暴露一个静态的公共方法
    public static EagerSingletonStaticConstant getInstance() {
        return eagerSingletonStaticConstant;
    }
}


/**
 * 饿汉式(静态代码块)
 *
 * 优缺点说明:
 * 1)这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块
 * 中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
 * 2)结论:这种单例模式可用,但是可能造成内存浪费
 */
class EagerSingletonStaticCode {

    private static EagerSingletonStaticCode eagerSingletonStaticCode;
    //静态代码块中创建单列对象
    static {
        eagerSingletonStaticCode = new EagerSingletonStaticCode();
    }
    // * 1 构造私有化 (防止直接new来创建对象)
    private EagerSingletonStaticCode() {
    }
    // * 3 向外暴露一个静态的公共方法
    public static EagerSingletonStaticCode getInstance() {
        return eagerSingletonStaticCode;
    }
}



懒汉式单列

package com.fs.sjms.single;

/**
 * 懒汉式单列
 */
public class LazySingleton {
    public static void main(String[] args) {

/*        //测试 懒汉式 (线程不安全)
        LazySingletonInsecurity instance = LazySingletonInsecurity.getInstance();
        LazySingletonInsecurity instance2 = LazySingletonInsecurity.getInstance();
        System.out.println(instance);
        System.out.println(instance2);输出的地址值一致,说明是同一个对象*/
        //但是在多线程环境下,就会导致线程不安全
/*        for (int i = 0; i < 10000; i++) {
            Thread thread  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //测试 懒汉式 (线程不安全)
                    LazySingletonInsecurity instance = LazySingletonInsecurity.getInstance();
                    System.out.println(instance);
                    *//*
                    com.fs.sjms.single.LazySingletonInsecurity@3b7fff9b  这个和后面的不一致
                    com.fs.sjms.single.LazySingletonInsecurity@2a75ae17
                    com.fs.sjms.single.LazySingletonInsecurity@2a75ae17
                     *//*
                }
            });
            thread.start();
        }*/


        //测试同步synchronized 单列模式是否安全
        for (int i = 0; i < 10000; i++) {
            Thread thread  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //测试 懒汉式 (线程不安全)
                    LazySingletonSafety instance = LazySingletonSafety.getInstance();
                    System.out.println(instance);
                    /*
com.fs.sjms.single.LazySingletonSafety@5dde7148  说明是线程安全的
com.fs.sjms.single.LazySingletonSafety@5dde7148
com.fs.sjms.single.LazySingletonSafety@5dde7148
                     */
                }
            });
            thread.start();
        }


    }
}


/**
 * 懒汉式 (线程不安全)
 *
 * 优缺点说明:
 * 1)起到了Lazy Loading的效果,但是只能在单线程下使用。
 * 2)如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及
 * 往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
 * 3)结论:在实际开发中,不要使用这种方式.
 */
class LazySingletonInsecurity{
    private static LazySingletonInsecurity lazySingletonInsecurity;
    //构造方法私有化
    private LazySingletonInsecurity(){}
    //提供一个静态的公有方法,当使用到该方法时候。才去创建
    public static LazySingletonInsecurity getInstance(){
        if (lazySingletonInsecurity == null){
            lazySingletonInsecurity = new LazySingletonInsecurity();
        }
        return lazySingletonInsecurity;
    }
}

/**
 * 懒汉式 (线程安全,同步方法)
 *
 * 优缺点说明:
 * 1)解决了线程不安全问题
 * 2)效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行
 * 同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
 * 3)结论:在实际开发中,不推荐使用这种方式
 */
class LazySingletonSafety{
    private static LazySingletonSafety lazySingletonSafety;
    //构造方法私有化
    private LazySingletonSafety(){}
    //提供一个静态的公有方法,当使用到该方法时候。才去创建
    //synchronized 假如同步关键字,解决线程安全问题
    public static synchronized LazySingletonSafety getInstance(){
        if (lazySingletonSafety == null){
            lazySingletonSafety = new LazySingletonSafety();
        }
        return lazySingletonSafety;
    }
}



/**
 * 懒汉式 (线程不安全,同步代码块方法,注意:不能解决线程安全问题)
 *
 * 优缺点说明:
 * 1)这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低,
 * 改为同步产生实例化的的代码块
 * 2)但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
 * 致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例
 * 3)结论:在实际开发中,不能使用这种方式
 */
class LazySingletonSafetySyncBlock{
    private static LazySingletonSafetySyncBlock lazySingletonSafetySyncBlock;
    //构造方法私有化
    private LazySingletonSafetySyncBlock(){}
    //提供一个静态的公有方法,当使用到该方法时候。才去创建
    public static LazySingletonSafetySyncBlock getInstance(){
        if (lazySingletonSafetySyncBlock == null){
            //添加同步代码块,不能解决线程安全问题
            //注意,这样不能解决线程安全问题,但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一
            //致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,
            // 另一个线程也通过了这个判断语句,这时便会产生多个实例
            synchronized (LazySingletonSafetySyncBlock.class){
                lazySingletonSafetySyncBlock = new LazySingletonSafetySyncBlock();
            }
        }
        return lazySingletonSafetySyncBlock;
    }
}

双重检查

package com.fs.sjms.single;

/**
 * 双重检查
 */
public class DuplicationCheck {
    public static void main(String[] args) {
        //测试同步synchronized 单列模式是否安全
        for (int i = 0; i < 10000; i++) {
            Thread thread  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //测试 懒汉式 双重检查
                    LazySingletonDuplicationCheck instance = LazySingletonDuplicationCheck.getInstance();
                    System.out.println(instance);
                }
            });
            thread.start();
        }
    }
}

/**
 * 双重检查
 *
 * 优缺点说明:
 * 1) Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两
 * 次if (singleton == null)检查,这样就可以保证线程安全了。
 * 2)这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton mm null),
 * 直接return实例化对象,也避免的反复进行方法同步.
 * 3)线程安全;延迟加载;效率较高
 * 4)结论:在实际开发中,推荐使用这种单例设计模式
 */
class LazySingletonDuplicationCheck{
    private static volatile LazySingletonDuplicationCheck lazySingletonDuplicationCheck;
    //构造方法私有化
    private LazySingletonDuplicationCheck(){}
    //提供一个静态的公有方法,当使用到该方法时候。才去创建
    //双重检查
    public static LazySingletonDuplicationCheck getInstance(){
        if (lazySingletonDuplicationCheck == null){
            synchronized (LazySingletonDuplicationCheck.class){
                if (lazySingletonDuplicationCheck == null){
                    lazySingletonDuplicationCheck = new LazySingletonDuplicationCheck();
                }
            }
        }
        return lazySingletonDuplicationCheck;
    }
}


静态内部类的方式

package com.fs.sjms.single;

/**
 * 懒加载单列 静态内部类的方式
 */
public class StaticInnerClass {

    public static void main(String[] args) {
        //测试静态内部类 单列模式是否安全
        for (int i = 0; i < 10000; i++) {
            Thread thread  = new Thread(new Runnable() {
                @Override
                public void run() {
                    //测试 懒汉式 双重检查
                    LazySingletonStaticInnerClass instance = LazySingletonStaticInnerClass.getInstance();
                    System.out.println(instance);
                }
            });
            thread.start();
        }
    }
}


/**
 * 懒加载单列 静态内部类的方式
 *
 * 优缺点说明I
 * 1)这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
 * 2)静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化
 * 时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
 * 3)类的静态属性只会在第一次加载类的时候初始化,所以在这里,JⅣM帮助我们
 * 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
 * 4)优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高5)结论:推荐使用.
 */
class LazySingletonStaticInnerClass{
    //构造方法私有化
    private LazySingletonStaticInnerClass(){}

    //静态内部类方法
    // * 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JⅣM帮助我们
    // * 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
    private static class LazySingletonStaticInnerClassClass{
        private static  LazySingletonStaticInnerClass lazySingletonStaticInnerClass=new LazySingletonStaticInnerClass();
    }

    public static LazySingletonStaticInnerClass getInstance(){
        return LazySingletonStaticInnerClassClass.lazySingletonStaticInnerClass;
    }
}

枚举实现单列模式

package com.fs.sjms.single;

/**
 * 枚举实现单列模式
 */
public class EnumSingleton {
    public static void main(String[] args) {
        SingletonEnumClass instance = SingletonEnumClass.INSTANCE;
        SingletonEnumClass instance2 = SingletonEnumClass.INSTANCE;
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance == instance2);
        System.out.println(instance.hashCode());
        System.out.println(instance2.hashCode());
        /*
        可以发现输出的是一样的,说明也能完成
        INSTANCE
        INSTANCE
        true
        21685669
        21685669
         */
    }

}

/**
 * 枚举实现单列模式
 *
 *优缺点说明:
 * 1)这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
 * 2)这种方式是Effective Java作者Josh Bloch提倡的方式3)结论:推荐使用
 */
enum SingletonEnumClass{
    INSTANCE;
    public void testMethod(){
        System.out.println("我也可以实现单列模式");
    }
}

二 工厂模式

1)工厂模式的意义
将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
2)三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)
3)设计模式的依赖抽象原则

创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。
不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)
不要覆盖基类中已经实现的方法。

简单(静态)工厂模式

package com.fs.sjms.factory;

/**
 * 简单(静态)工厂模式
 *
 * 基本介绍
 * 1)简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一
 * 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
 * 2)简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
 * 3)在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式.
 *
 * 看一个具体的需求
 * 看一个披萨的项目:要便于披萨种类的扩展,要便于维护
 * 1)披萨的种类很多(比如GreekPizza、CheesePizza、 PepperPizza等)
 * 2)披萨的制作有prepare,bake, cut, box
 * 3)完成披萨店订购功能。
 */
public class SimpleFactoryDemo {
    public static void main(String[] args) {
        //测试简单工厂
        SimpleFactory.getPizza("GreekPizza");
        SimpleFactory.getPizza("CheesePizza");
        SimpleFactory.getPizza("PepperPizza");
        SimpleFactory.getPizza("没有的pizza");
        /*
        输入结果
GreekPizza---prepare
GreekPizza---bake
GreekPizza---cut
GreekPizza---box
请尽情享用吧····
CheesePizza---prepare
CheesePizza---bake
CheesePizza---cut
CheesePizza---box
请尽情享用吧····
PepperPizza---prepare
PepperPizza---bake
PepperPizza---cut
PepperPizza---box
请尽情享用吧····
当前pizza还未上线,请尽情期待哟····

Process finished with exit code 0

         */
    }
}


/**
 * 简单工厂类
 */
class SimpleFactory{

    //用户传递想要的pizza就生产他需要的
    public static Pizza getPizza(String pizzaName){
        Pizza pizza = OrderPizza.creatPizza(pizzaName);
        if (pizza!=null){
            //说明用户想要的pizza工厂中有
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            System.out.println("请尽情享用吧····");
        }else {
            //说明没有
            System.out.println("当前pizza还未上线,请尽情期待哟····");
        }
        return pizza;
    }

}

/**
 * 制作pizza的店家
 */
class OrderPizza{

    //根据用户需要什么披萨而产出什么披萨
    public static Pizza creatPizza(String pizzaName){
        switch (pizzaName){
            case "GreekPizza":
                return new GreekPizza();
            case "CheesePizza":
                return new CheesePizza();
            case "PepperPizza":
                return new PepperPizza();
        }
        return null;
    }
}

/**
 * 披萨抽象类
 */
abstract class Pizza{
    //制作pizza的过程
    abstract void prepare();
    abstract void bake();
    abstract void cut();
    abstract void box();
}
/**
 * 三种pizz对象
 */
class GreekPizza extends Pizza{

    @Override
    void prepare() {
        System.out.println("GreekPizza---prepare");
    }

    @Override
    void bake() {
        System.out.println("GreekPizza---bake");
    }

    @Override
    void cut() {
        System.out.println("GreekPizza---cut");
    }

    @Override
    void box() {
        System.out.println("GreekPizza---box");
    }
}
class CheesePizza extends Pizza{

    @Override
    void prepare() {
        System.out.println("CheesePizza---prepare");
    }

    @Override
    void bake() {
        System.out.println("CheesePizza---bake");
    }

    @Override
    void cut() {
        System.out.println("CheesePizza---cut");
    }

    @Override
    void box() {
        System.out.println("CheesePizza---box");
    }
}
class PepperPizza extends Pizza{

    @Override
    void prepare() {
        System.out.println("PepperPizza---prepare");
    }

    @Override
    void bake() {
        System.out.println("PepperPizza---bake");
    }

    @Override
    void cut() {
        System.out.println("PepperPizza---cut");
    }

    @Override
    void box() {
        System.out.println("PepperPizza---box");
    }
}


工厂方法模式

package com.fs.sjms.factory;

/**
 * 工厂方法模式
 *
 * 看一个新的需求
 * 披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
 * 思路1
 * 使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、
 * LDPizzaSimpleFactory 等等.从当前这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
 * 思路2
 * 使用工厂方法模式
 * 工厂方法模式工厂方法模式介绍
 * 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
 * 工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
 *
 */
public class FactoryMethodDemo {
    public static void main(String[] args) {
        //测试 使用BJCheesePizzaMethod
        PizzaMethod bjCheesePizzaMethod = new BJOrderPizzaMethod().getPizza("CheesePizza");
        bjCheesePizzaMethod.prepare();
        bjCheesePizzaMethod.bake();
        bjCheesePizzaMethod.cut();
        bjCheesePizzaMethod.box();
    }
}

/**
 * 制作pizza的店家
 */
abstract class OrderPizzaMethod{
    //制作不同地方的披萨抽象
    public abstract PizzaMethod getPizza(String pizzaName);
}


class BJOrderPizzaMethod extends OrderPizzaMethod{

    //根据用户需要什么披萨而产出什么披萨
    @Override
    public PizzaMethod getPizza(String pizzaName){
        switch (pizzaName){
            case "CheesePizza":
                return new BJCheesePizzaMethod();
            case "PepperPizza":
                return new BJPepperPizzaMethod();
        }
        return null;
    }
}

class LDOrderPizzaMethod extends OrderPizzaMethod{

    //根据用户需要什么披萨而产出什么披萨
    @Override
    public PizzaMethod getPizza(String pizzaName){
        switch (pizzaName){
            case "PepperPizza":
                return new LDPepperPizzaMethod();
            case "CheesePizza":
                return new LDCheesePizzaMethod();
        }
        return null;
    }
}


/**
 * 披萨抽象类
 */
abstract class PizzaMethod{
    //制作pizza的过程
    abstract void prepare();
    abstract void bake();
    abstract void cut();
    abstract void box();
}

/**
 * 比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
 */
class BJCheesePizzaMethod extends PizzaMethod{

    @Override
    void prepare() {
        System.out.println("BJCheesePizzaMethod---prepare");
    }

    @Override
    void bake() {
        System.out.println("BJCheesePizzaMethod---bake");
    }

    @Override
    void cut() {
        System.out.println("BJCheesePizzaMethod---cut");
    }

    @Override
    void box() {
        System.out.println("BJCheesePizzaMethod---box");
    }
}

class LDCheesePizzaMethod extends PizzaMethod{

    @Override
    void prepare() {
        System.out.println("LDCheesePizzaMethod---prepare");
    }

    @Override
    void bake() {
        System.out.println("LDCheesePizzaMethod---bake");
    }

    @Override
    void cut() {
        System.out.println("LDCheesePizzaMethod---cut");
    }

    @Override
    void box() {
        System.out.println("LDCheesePizzaMethod---box");
    }
}

class BJPepperPizzaMethod extends PizzaMethod{

    @Override
    void prepare() {
        System.out.println("BJPepperPizzaMethod---prepare");
    }

    @Override
    void bake() {
        System.out.println("BJPepperPizzaMethod---bake");
    }

    @Override
    void cut() {
        System.out.println("BJPepperPizzaMethod---cut");
    }

    @Override
    void box() {
        System.out.println("BJPepperPizzaMethod---box");
    }
}

class LDPepperPizzaMethod extends PizzaMethod{

    @Override
    void prepare() {
        System.out.println("LDPepperPizzaMethod---prepare");
    }

    @Override
    void bake() {
        System.out.println("LDPepperPizzaMethod---bake");
    }

    @Override
    void cut() {
        System.out.println("LDPepperPizzaMethod---cut");
    }

    @Override
    void box() {
        System.out.println("LDPepperPizzaMethod---box");
    }
}

抽象工厂模式

package com.fs.sjms.factory;

/**
 *
 * 抽象工厂模式基本介绍
 *
 * 1))抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
 * 2)抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
 * 3)从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
 * 4)将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
 *
 */
public class AbstractFactoryDemo {
    public static void main(String[] args) {
        //测试抽象工厂 需要北京的奶酪口味的
        PizzaAbstract cheesePizza = OrderPizzaAbstract.setFactory(new BJAbstractFactory(), "CheesePizza");
        cheesePizza.prepare();
        cheesePizza.bake();
        cheesePizza.box();
        cheesePizza.cut();

        //测试抽象工厂 需要伦敦的奶酪口味的
        PizzaAbstract LDcheesePizza = OrderPizzaAbstract.setFactory(new LDAbstractFactory(), "CheesePizza");
        LDcheesePizza.prepare();
        LDcheesePizza.bake();
        LDcheesePizza.box();
        LDcheesePizza.cut();
    }
}

/**
 * 制作pizza的抽象工厂
 */
 interface AbstractFactory{
    //让工厂子类具体实现
    PizzaAbstract getPizza(String pizzaName);
}


class BJAbstractFactory implements AbstractFactory{

    //根据用户需要什么披萨而产出什么披萨
    @Override
    public PizzaAbstract getPizza(String pizzaName){
        switch (pizzaName){
            case "CheesePizza":
                return new BJCheesePizzaAbstract();
            case "PepperPizza":
                return new BJPepperPizzaAbstract();
        }
        return null;
    }
}

class LDAbstractFactory implements AbstractFactory{

    //根据用户需要什么披萨而产出什么披萨
    @Override
    public PizzaAbstract getPizza(String pizzaName){
        switch (pizzaName){
            case "PepperPizza":
                return new LDPepperPizzaAbstract();
            case "CheesePizza":
                return new LDCheesePizzaAbstract();
        }
        return null;
    }
}

/**
 * 生产pizza的店家
 */
class OrderPizzaAbstract{
     public static PizzaAbstract setFactory(AbstractFactory abstractFactory,String pizzaName){
        return abstractFactory.getPizza(pizzaName);
     }
}


/**
 * 披萨抽象类
 */
abstract class PizzaAbstract{
    //制作pizza的过程
    abstract void prepare();
    abstract void bake();
    abstract void cut();
    abstract void box();
}

/**
 * 比如北京的奶酪pizza、北京的胡椒pizza或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
 */
class BJCheesePizzaAbstract extends PizzaAbstract{

    @Override
    void prepare() {
        System.out.println("BJCheesePizzaAbstract---prepare");
    }

    @Override
    void bake() {
        System.out.println("BJCheesePizzaAbstract---bake");
    }

    @Override
    void cut() {
        System.out.println("BJCheesePizzaAbstract---cut");
    }

    @Override
    void box() {
        System.out.println("BJCheesePizzaAbstract---box");
    }
}

class LDCheesePizzaAbstract extends PizzaAbstract{

    @Override
    void prepare() {
        System.out.println("LDCheesePizzaAbstract---prepare");
    }

    @Override
    void bake() {
        System.out.println("LDCheesePizzaAbstract---bake");
    }

    @Override
    void cut() {
        System.out.println("LDCheesePizzaAbstract---cut");
    }

    @Override
    void box() {
        System.out.println("LDCheesePizzaAbstract---box");
    }
}

class BJPepperPizzaAbstract extends PizzaAbstract{

    @Override
    void prepare() {
        System.out.println("BJPepperPizzaAbstract---prepare");
    }

    @Override
    void bake() {
        System.out.println("BJPepperPizzaAbstract---bake");
    }

    @Override
    void cut() {
        System.out.println("BJPepperPizzaAbstract---cut");
    }

    @Override
    void box() {
        System.out.println("BJPepperPizzaAbstract---box");
    }
}

class LDPepperPizzaAbstract extends PizzaAbstract{

    @Override
    void prepare() {
        System.out.println("LDPepperPizzaAbstract---prepare");
    }

    @Override
    void bake() {
        System.out.println("LDPepperPizzaAbstract---bake");
    }

    @Override
    void cut() {
        System.out.println("LDPepperPizzaAbstract---cut");
    }

    @Override
    void box() {
        System.out.println("LDPepperPizzaAbstract---box");
    }
}

三 原型模式

                        原型模式

克隆羊问题
现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和 tom 羊 属性完全相同的 10
只羊。

传统方式解决克隆羊问题
传统的方式的优缺点
1)优点是比较好理解,简单易操作。
2)在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
3)总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活

4)改进的思路分析
思路:Java中object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且具有复制的能力=>原型模式


●原型模式-基本介绍基本介绍
1)原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷
贝这些原型,创建新的对象
2)原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,
无需知道如何创建的细节
3)工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建
的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
4)形象的理解:孙大圣拔出猴毛,变出其它孙大圣
5)原型模式只是属性的复制,生产出的对象不是原来的对象,是新对象。在spring源码中
若在xml中配置bean的时候socpe=“prototype”的话,从ioc获取的对象就是原型模式,
新建对象复制属性获取的,而不是单列模式


●深入讨论-浅拷贝和深拷贝

                    浅拷贝的介绍
1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类
的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3)前面我们克隆羊就是浅拷贝
4)浅拷贝是使用默认的clone()方法来实现
sheep = (Sheep) super.clone();

                    深拷贝基本介绍
1)复制对象的所有基本数据类型的成员变量值
2)为所有引用数据类型的成员变量中请存储空间,并复制每个引用数据类型成员变
量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
3)深拷贝实现方式1:重写clone方法来实现深拷贝
4)深拷贝实现方式2:通过对象序列化实现深拷贝


                原型模式的注意事项和细节

1)创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2)不用重新初始化对象,而是动态地获得对象运行时的状态
3)如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码
4)在实现深克隆的时候可能需要比较复杂的代码
5)缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有的类进行改造时,需要修改其源代码,违背了 ocp 原则,这点请同学们注意.



原型模式解决克隆羊问题

package com.fs.sjms.prototype;

/**
 * 原型模式解决克隆羊问题
 *
 * 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,
 * 请编写程序创建和 tom 羊 属性完全相同的 5只羊。
 */
public class PrototypeClone{
    public static void main(String[] args) {

        //测试原型模式克隆羊
        Sheep sheep = new Sheep("tom", 1, "白色");
        Sheep clone = (Sheep) sheep.clone();
        Sheep clone2 = (Sheep) sheep.clone();
        Sheep clone3 = (Sheep) sheep.clone();
        Sheep clone4 = (Sheep) sheep.clone();
        Sheep clone5 = (Sheep) sheep.clone();
        Sheep clone6 = (Sheep) sheep.clone();
        System.out.println(clone);
        System.out.println(clone2);
        System.out.println(clone3);
        System.out.println(clone4);
        System.out.println(clone5);
        System.out.println(clone6);
    }

}


/**
 * 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法
 */
class Sheep implements Cloneable{
    private String name;
    private Integer age;
    private String colour;

    public Sheep(String name, Integer age, String colour) {
        this.name = name;
        this.age = age;
        this.colour = colour;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", colour='" + colour + '\'' +
                '}';
    }

    /**
     * 重写clone方法
     */
    @Override
    protected Object clone()  {
        Sheep clone = null;
        try {
            clone = (Sheep)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

浅拷贝

package com.fs.sjms.prototype.shallow;

/**
 * 浅拷贝
 *
 * 浅拷贝的介绍
 *
 * 1)对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
 * 2)对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
 *
 * 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,他的朋友xiaoming羊
 * 请编写程序创建和 tom 羊 属性完全相同的 5只羊。
 */
public class PrototypeShallowClone{
    public static void main(String[] args) {


        //测试原型模式克隆羊
        Sheep sheep = new Sheep("tom", 1, "白色",new Sheep("xiaoming",2,"黑色",null));
        Sheep clone = (Sheep) sheep.clone();
        Sheep clone2 = (Sheep) sheep.clone();
        Sheep clone3 = (Sheep) sheep.clone();
        Sheep clone4 = (Sheep) sheep.clone();
        Sheep clone5 = (Sheep) sheep.clone();
        Sheep clone6 = (Sheep) sheep.clone();
        System.out.println(clone+"clone hash:"+clone.hashCode()+"clone de friend:"+clone.getFriend().hashCode());
        System.out.println(clone2+"clone2 hash:"+clone2.hashCode()+"clone2 de friend:"+clone2.getFriend().hashCode());
        System.out.println(clone3+"clone3 hash:"+clone3.hashCode()+"clone3 de friend:"+clone3.getFriend().hashCode());
        System.out.println(clone4+"clone4 hash:"+clone4.hashCode()+"clone4 de friend:"+clone4.getFriend().hashCode());
        System.out.println(clone5+"clone5 hash:"+clone5.hashCode()+"clone5 de friend:"+clone5.getFriend().hashCode());
        System.out.println(clone6+"clone6 hash:"+clone6.hashCode()+"clone6 de friend:"+clone6.getFriend().hashCode());

        /*
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone hash:21685669clone de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone2 hash:1836019240clone2 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone3 hash:325040804clone3 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone4 hash:1173230247clone4 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone5 hash:856419764clone5 de friend:2133927002
Sheep{name='tom', age=1, colour='白色', friend=Sheep{name='xiaoming', age=2, colour='黑色', friend=null}}clone6 hash:621009875clone6 de friend:2133927002


由输出结果得知,复制的tom羊是新建的对象复制原有对象的属性值,而tom的朋友对象是引用的同一个对象,这样的拷贝叫浅拷贝
         */
    }

}


/**
 * 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法
 */
class Sheep implements Cloneable{
    private String name;
    private Integer age;
    private String colour;

    //若我们的属性有实体类型呢?clone复制的是值?还是地址引用?
    private Sheep friend;

    public Sheep(String name, Integer age, String colour, Sheep friend) {
        this.name = name;
        this.age = age;
        this.colour = colour;
        this.friend = friend;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getColour() {
        return colour;
    }

    public void setColour(String colour) {
        this.colour = colour;
    }

    public Sheep getFriend() {
        return friend;
    }

    public void setFriend(Sheep friend) {
        this.friend = friend;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", colour='" + colour + '\'' +
                ", friend=" + friend +
                '}';
    }

    /**
     * 重写clone方法
     */
    @Override
    protected Object clone()  {
        Sheep clone = null;
        try {
            clone = (Sheep)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }
}

深拷贝

package com.fs.sjms.prototype.deep;

import java.io.*;

/**
 * 深拷贝
 * 

* 深拷贝基本介绍 *

* 1)复制对象的所有基本数据类型的成员变量值 * 2)为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象(包括对象的引用类型)进行拷贝 *

* 3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝 * 4)深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐) *

* 现在有一只羊 tom,姓名为: tom, 年龄为:1,颜色为:白色,他的小伙伴dog * 请编写程序创建和 tom 羊 属性完全相同的 5只羊。 */ public class PrototypeDeepClone { public static void main(String[] args) { //测试 深拷贝实现方式 1:重写 clone 方法来实现深拷贝 /* Sheep sheep = new Sheep("tom", 1, "白色",new Dog("dog")); Sheep clone = (Sheep) sheep.clone(); Sheep clone2 = (Sheep) sheep.clone(); Sheep clone3 = (Sheep) sheep.clone(); Sheep clone4 = (Sheep) sheep.clone(); Sheep clone5 = (Sheep) sheep.clone(); Sheep clone6 = (Sheep) sheep.clone(); System.out.println(clone+"clone hash:"+clone.hashCode()+",clone de friend:"+clone.getFriend().hashCode()); System.out.println(clone2+"clone2 hash:"+clone2.hashCode()+",clone2 de friend:"+clone2.getFriend().hashCode()); System.out.println(clone3+"clone3 hash:"+clone3.hashCode()+",clone3 de friend:"+clone3.getFriend().hashCode()); System.out.println(clone4+"clone4 hash:"+clone4.hashCode()+",clone4 de friend:"+clone4.getFriend().hashCode()); System.out.println(clone5+"clone5 hash:"+clone5.hashCode()+",clone5 de friend:"+clone5.getFriend().hashCode()); System.out.println(clone6+"clone6 hash:"+clone6.hashCode()+",clone6 de friend:"+clone6.getFriend().hashCode());*/ /* Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone hash:21685669,clone de friend:2133927002 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone2 hash:1836019240,clone2 de friend:325040804 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone3 hash:1173230247,clone3 de friend:856419764 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone4 hash:621009875,clone4 de friend:1265094477 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone5 hash:2125039532,clone5 de friend:312714112 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone6 hash:692404036,clone6 de friend:1554874502 由输出得,都是克隆生成的新对象,这就叫深拷贝 */ System.out.println("--------------------------------------------------------------------------------------------"); //测试 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐) Sheep sheep = new Sheep("tom", 1, "白色", new Dog("dog")); Sheep clone = sheep.deepClone(); Sheep clone2 = sheep.deepClone(); Sheep clone3 = sheep.deepClone(); Sheep clone4 = sheep.deepClone(); Sheep clone5 = sheep.deepClone(); Sheep clone6 = sheep.deepClone(); System.out.println(clone + "clone hash:" + clone.hashCode() + ",clone de friend:" + clone.getFriend().hashCode()); System.out.println(clone2 + "clone2 hash:" + clone2.hashCode() + ",clone2 de friend:" + clone2.getFriend().hashCode()); System.out.println(clone3 + "clone3 hash:" + clone3.hashCode() + ",clone3 de friend:" + clone3.getFriend().hashCode()); System.out.println(clone4 + "clone4 hash:" + clone4.hashCode() + ",clone4 de friend:" + clone4.getFriend().hashCode()); System.out.println(clone5 + "clone5 hash:" + clone5.hashCode() + ",clone5 de friend:" + clone5.getFriend().hashCode()); System.out.println(clone6 + "clone6 hash:" + clone6.hashCode() + ",clone6 de friend:" + clone6.getFriend().hashCode()); /* Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone hash:1867750575,clone de friend:2046562095 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone2 hash:1342443276,clone2 de friend:769287236 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone3 hash:1587487668,clone3 de friend:1199823423 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone4 hash:932172204,clone4 de friend:1225358173 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone5 hash:1848402763,clone5 de friend:933699219 Sheep{name='tom', age=1, colour='白色', friend=Dog{name='dog'}}clone6 hash:2121055098,clone6 de friend:2084435065 同样地址值不同,得知为深拷贝 */ } } /** * 羊实体类,要使用到原型模式,需要实现Cloneable接口,从写clone()方法 */ class Sheep implements Serializable,Cloneable { private String name; private Integer age; private String colour; //若我们的属性有实体类型呢?clone复制的是值?还是地址引用? private Dog friend; public Sheep(String name, Integer age, String colour, Dog friend) { this.name = name; this.age = age; this.colour = colour; this.friend = friend; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getColour() { return colour; } public void setColour(String colour) { this.colour = colour; } public Dog getFriend() { return friend; } public void setFriend(Dog friend) { this.friend = friend; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", colour='" + colour + '\'' + ", friend=" + friend + '}'; } /** * 重写clone方法 * 3)深拷贝实现方式 1:重写 clone 方法来实现深拷贝 */ @Override protected Object clone() { Sheep clone = null; try { //先对基本属性进行克隆 clone = (Sheep) super.clone(); //在对实体属性进克隆 clone.setFriend((Dog) clone.getFriend().clone()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } /** * 深拷贝实现方式 2:通过对象序列化实现深拷贝(推荐) */ public Sheep deepClone() { //创建输入输出流 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { //创建输出流 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); //将当期对象写入流 oos.writeObject(this); //创建输入流 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); //将当前对象从流中读出来 Sheep o = (Sheep) ois.readObject(); return o; } catch (Exception e) { e.printStackTrace(); return null; } finally { //关流 try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 羊的小伙伴dog *

* 要实现深拷贝需要序列化与实现Cloneable接口 */ class Dog implements Serializable, Cloneable { private static final long serialVersionUID = 1L; private String name; public Dog(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + '}'; } //重写clone方法 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }

四 建造者模式

建造者模式
            建造者模式基本介绍

            基本介绍

1)建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
2)建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们, 用户不需要知道内部的具体构建细节。

        建造者模式的四个角色

1)Product(产品角色): 一个具体的产品对象。
2)Builder(抽象建造者): 创建一个 Product 对象的各个部件指定的 接口/抽象类。

3)ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
4)Director(指挥者): 构建一个使用 Builder 接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
package com.fs.sjms.builder;

/**
 * 建造者模式解决盖房需求应用实例
 * 

* 1)需要建房子:这一过程为打桩、砌墙、封顶。不管是普通房子也好,别墅也好都需要经历这些过程, * 下面我们使用建造者模式(Builder Pattern)来完成 * 2)思路分析图解(类图) */ public class BuilderDemo { public static void main(String[] args) { //盖普通房子 CommonHouse commonHouse = new CommonHouse(); //准备创建房子的指挥者 HouseDirector houseDirector = new HouseDirector(commonHouse); //完成盖房子,返回产品(普通房子) House house = houseDirector.constructHouse(); //System.out.println(" 输 出 流 程 "); System.out.println("--------------------------"); //盖高楼 HighBuilding highBuilding = new HighBuilding(); //重置建造者 houseDirector.setHouseBuilder(highBuilding); //完成盖房子,返回产品(高楼) houseDirector.constructHouse(); } } // 产 品 ->Product class House { // 产 品 ->Product public class House { private String baise; private String wall; private String roofed; public String getBaise() { return baise; } public void setBaise(String baise) { this.baise = baise; } public String getWall() { return wall; } public void setWall(String wall) { this.wall = wall; } public String getRoofed() { return roofed; } public void setRoofed(String roofed) { this.roofed = roofed; } } // 抽象的建造者 abstract class HouseBuilder { protected House house = new House(); //将建造的流程写好, 抽象的方法 public abstract void buildBasic(); public abstract void buildWalls(); public abstract void roofed(); //建造房子好, 将产品(房子) 返回 public House buildHouse() { return house; } } //普通房子 class CommonHouse extends HouseBuilder { @Override public void buildBasic() { System.out.println(" 普通房子打地基 5 米 "); } @Override public void buildWalls() { System.out.println(" 普通房子砌墙 10cm "); } @Override public void roofed() { System.out.println(" 普通房子屋顶 "); } } //高楼 class HighBuilding extends HouseBuilder { @Override public void buildBasic() { System.out.println(" 高楼的打地基 100 米 "); } @Override public void buildWalls() { System.out.println(" 高楼的砌墙 20cm "); } @Override public void roofed() { System.out.println(" 高楼的透明屋顶 "); } } //指挥者,这里去指定制作流程,返回产品 class HouseDirector { HouseBuilder houseBuilder = null; //构造器传入 houseBuilder public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } //通过 setter 传入 houseBuilder public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } //如何处理建造房子的流程,交给指挥者 public House constructHouse() { houseBuilder.buildBasic(); houseBuilder.buildWalls(); houseBuilder.roofed(); return houseBuilder.buildHouse(); } }

五 适配器模式

适配器设计模式
现实生活中的适配器例子

泰国插座用的是两孔的(欧标),可以买个多功能转换插头 (适配器) ,这样就可以使用了。
基本介绍

1)适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
2)适配器模式属于结构型模式
3)主要分为三类:类适配器模式、对象适配器模式、接口适配器模式

工作原理
1)适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
2)从用户的角度看不到被适配者,是解耦的
3)用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
4)用户收到反馈结果,感觉只是和目标接口交互,如图


类适配器模式注意事项和细节

1)Java 是单继承机制,所以类适配器需要继承 src 类这一点算是一个缺点, 因为这要求 dst 必须是接口,有一定局限性;
2)src 类的方法在 Adapter 中都会暴露出来,也增加了使用的成本。
3)由于其继承了 src 类,所以它可以根据需求重写 src 类的方法,使得 Adapter 的灵活性增强了。

对象适配器模式

对象适配器模式介绍
1)基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而
是持有src类的实例,以解决兼容性的问题。即:持有src类,实现dst类接口,完成src->dst的适配
2)根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。3)对象适配器模式是适配器模式常用的一种


对象适配器模式注意事项和细节

1)对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst
必须是接口。
2)使用成本更低,更灵活。

接口适配器模式
接口适配器模式介绍

1)一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。
2)核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
3)适用于一个接口不想使用其所有的方法的情况。


适配器模式在 SpringMVC 框架应用的源码剖析
1)SpringMvc 中的 HandlerAdapter, 就使用了适配器模式
2)SpringMVC 处理请求的流程回顾
3)使用 HandlerAdapter 的原因分析:
可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断是使用 if else 来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了 OCP 原则。

适配器模式的注意事项和细节
1)三种命名方式,是根据 src 是以怎样的形式给到 Adapter(在 Adapter 里的形式)来命名的。
2)类适配器:以类给到,在 Adapter 里,就是将 src 当做类,继承
对象适配器:以对象给到,在 Adapter 里,将 src 作为一个对象,持有接口适配器:以接口给到,在 Adapter 里,将 src 作为一个接口,实现
3)Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作。
实际开发中,实现起来不拘泥于我们讲解的三种经典形式

类适配器模式

package com.fs.sjms.adapter.classadapter;

/**
 * 类适配器模式
 *
 * 类适配器模式介绍
 * 基本介绍:Adapter 类,通过继承 src 类,实现 dst  类接口,完成 src->dst 的适配。
 *
 * 类适配器模式应用实例
 * 1)应用实例说明
 * 以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们的目 dst(即 目标)是 5V 直流电
 *
 * 类适配器模式
 * 类适配器模式注意事项和细节
 * 1) Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为这要
 * 求dst必须是接口,有一定局限性;
 * 2) src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
 * 3)由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵
 * 活性增强了。
 */
public class ClassAdapterDemo {
    public static void main(String[] args) {
        //测试类适配器模式
        Phone.chongDian(new VoltageAdapter());
        /*
被适配,输出了220V电压
适配将电压降低
我正在使用5伏电压充电
         */
    }
}

//被适配类
class Voltage220V{
    public static int getIntV(){
        int src = 220;
        System.out.println("被适配,输出了220V电压");
        return src;
    }
}

//适配器接口
interface Voltage5V{
    int getOutV();
}

//适配器类
class VoltageAdapter extends Voltage220V implements Voltage5V{
    @Override
    public int getOutV() {
        //获取被适配类 的度数
        int intV = getIntV();
        int outV = 0;
        if (intV == 220){
            outV = intV / 44;
            System.out.println("适配将电压降低");
        }
        return outV;
    }
}

//手机类,来充电
class Phone{
    public static void chongDian(Voltage5V v){
        int outV = v.getOutV();
        if (outV==5){
            System.out.println("我正在使用5伏电压充电");
        }else {
            System.out.println("太大了,受不了了");
        }
    }
}

对象适配器模式

package com.fs.sjms.adapter.entityadapter;

/**
 * 对象适配器
 *
 */
public class EntityAdapterDemo {
    public static void main(String[] args) {
        //测试对象适配器模式
        Phone.chongDian(new VoltageAdapter(new Voltage220V()));
        /*
被适配,输出了220V电压
适配将电压降低
我正在使用5伏电压充电
         */
    }
}

//被适配类
class Voltage220V{
    public  int getIntV(){
        int src = 220;
        System.out.println("被适配,输出了220V电压");
        return src;
    }
}

//适配器接口
interface Voltage5V{
    int getOutV();
}

//适配器类
class VoltageAdapter implements Voltage5V{

    private Voltage220V voltage220V;

    //通过构造器传递Voltage220V
    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public int getOutV() {
        //获取被适配类 的度数

        int outV = 0;
        if (voltage220V !=null && voltage220V.getIntV()==220){
            //获取220电压,使用的对象适配器
            outV = voltage220V.getIntV() / 44;
            System.out.println("适配将电压降低");
        }
        return outV;
    }
}

//手机类,来充电
class Phone{
    public static void chongDian(Voltage5V v){
        int outV = v.getOutV();
        if (outV==5){
            System.out.println("我正在使用5伏电压充电");
        }else {
            System.out.println("太大了,受不了了");
        }
    }
}

接口适配器模式

package com.fs.sjms.adapter.interfaceadapter;

/**
 * 接口适配器模式应用实例
 *
 * 现在的 充电器有5v   的 和 10v 的。
 */
public class InterfaceAdapterDemo {
    public static void main(String[] args) {
        //测试接口适配器 模式 因为Voltage5VOr10VClass抽象类实现了 Voltage5VOr10V重写了方法,所有我们需要什么实习,就从写 抽象了类什么方法
        Phone.chongDian(new Voltage5VOr10VClass() {
            //假如客户需要5负电压
            @Override
            public int getOut5V(Voltage220V voltage220V) {
                int intV = voltage220V.getIntV();
                return intV/44;
            }
        });


        //假如 又想使用10v的了
        Phone.chongDian(new Voltage5VOr10VClass() {
            @Override
            public int getOut10V(Voltage220V voltage220V) {
                int intV = voltage220V.getIntV();
                return intV/22;
            }
        });
    }
}

//被适配类
class Voltage220V{
    public  int getIntV(){
        int src = 220;
        System.out.println("被适配,输出了220V电压");
        return src;
    }
}

//适配器接口
interface Voltage5VOr10V{
    int getOut5V(Voltage220V voltage220V);
    int getOut10V(Voltage220V voltage220V);
}

//适配器类,从写所有方法
abstract class Voltage5VOr10VClass implements Voltage5VOr10V{
    @Override
    public int getOut5V(Voltage220V voltage220V) {
        return 0;
    }

    @Override
    public int getOut10V(Voltage220V voltage220V) {
        return 0;
    }
}




//手机类,来充电
class Phone{
    public static void chongDian(Voltage5VOr10V v){
        Voltage220V voltage220V = new Voltage220V();
        if (v.getOut5V(voltage220V)==5){
            System.out.println("我正在使用5伏电压充电");
        }else if(v.getOut10V(voltage220V)==10){
            System.out.println("我正在使用10伏电压充电");
        }else {
            System.out.println("太大了,受不了了");
        }
    }
}

六 桥接模式

设计模式之java-学习笔记_第1张图片

        桥接模式
手机操作问题
现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等)

传统方案解决手机操作问题分析
1)扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
2)违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
3)解决方案-使用桥接模式

桥接模式(Bridge)-基本介绍
基本介绍
1)桥接模式(Bridge 模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
2)是一种结构型设计模式
3)Bridge 模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

桥接模式(Bridge)  =====这里有个图=====
1)Client 类:桥接模式的调用者
2)抽象类(Abstraction) :维护了 Implementor / 即它的实现类 ConcreteImplementorA.., 二者是聚合关系, Abstraction充当桥接类
3)RefinedAbstraction :  是 Abstraction  抽象类的子类
4)Implementor :  行为实现类的接口
5)ConcreteImplementorA /B :行为的具体实现类
6)从 UML 图:这里的抽象类和接口是聚合的关系,其实调用和被调用关系



桥接模式的注意事项和细节

1)实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
2)对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
3)桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。

4)桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
5)桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。


桥接模式在 JDBC 的源码剖析
1)Jdbc 的 Driver 接口,如果从桥接模式来看,Driver 就是一个接口,下面可以有 MySQL 的 Driver,Oracle 的
Driver,这些就可以当做实现接口类


桥接模式其它应用场景

对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用.
常见的应用场景:
1)-JDBC 驱动程序
2)-银行转账系统
转账分类: 网上转账,柜台转账,AMT 转账
转账用户类型:普通用户,银卡用户,金卡用户..
3)-消息管理
消息类型:即时消息,延时消息
消息分类:手机短信,邮件消息,QQ 消息...

package com.fs.sjms.bridging;

/**
 * 桥接模式
 *
 * 手机案例
 *
 * 桥接模式,相当于现在增加一个样式就新增一个样式类继承phone就可以使用了
 */
public class BridgingDemo {
    public static void main(String[] args) {
        //测试桥接模式
        //获取折叠式的小米手机
        FoldPhone foldPhone = new FoldPhone(new XiaoMi());
        foldPhone.open();
        foldPhone.call();
        foldPhone.close();

        //获取全面屏的小米手机
        BezelLessPhone bezelLessPhone = new BezelLessPhone(new XiaoMi());
        bezelLessPhone.open();
        bezelLessPhone.call();
        bezelLessPhone.close();

        //获取华为折叠手机
        FoldPhone foldPhoneHAWEI = new FoldPhone(new HAWEI());
        foldPhoneHAWEI.open();
        foldPhoneHAWEI.call();
        foldPhoneHAWEI.close();
    }
}

//品牌接口
interface Brand{
    void open();
    void close();
    void call();
}


//小米手机
class XiaoMi implements Brand{
    @Override
    public void open() {
        System.out.println("xiaoMi-开机了");
    }

    @Override
    public void close() {
        System.out.println("xiaoMi-关机了");
    }

    @Override
    public void call() {
        System.out.println("xiaoMi-打电话");
    }
}

//华为手机
class HAWEI implements Brand{
    @Override
    public void open() {
        System.out.println("HAWEI-开机了");
    }

    @Override
    public void close() {
        System.out.println("HAWEI-关机了");
    }

    @Override
    public void call() {
        System.out.println("HAWEI-打电话");
    }
}


//抽象类手机
abstract class Phone{
    //组合品牌
   private Brand brand;

   //构造
    public Phone(Brand brand){
        super();
        this.brand = brand;
    }
    public void open() {
        this.brand.open();
    }

    public void close() {
        this.brand.close();
    }

    public void call() {
        this.brand.call();
    }
}

//折叠手机,继承了phone这个抽象的类
class FoldPhone extends Phone{

    public FoldPhone(Brand brand) {
        super(brand);

    }

    public void open() {
        super.open();
        System.out.println("折叠手机--open");
    }

    public void close() {
        super.close();
        System.out.println("折叠手机--close");
    }

    public void call() {
        super.call();
        System.out.println("折叠手机--call");
    }
}


//全面屏手机,继承了phone这个抽象的类
class BezelLessPhone extends Phone{

    public BezelLessPhone(Brand brand) {
        super(brand);

    }

    public void open() {
        super.open();
        System.out.println("全面屏手机--open");
    }

    public void close() {
        super.close();
        System.out.println("全面屏手机--close");
    }

    public void call() {
        super.call();
        System.out.println("全面屏手机--call");
    }
}

七 装饰者模式

案例

星巴克咖啡订单项目(咖啡馆):

1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
2)调料:Milk、Soy(豆浆)、Chocolate
3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4)使用 OO 的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。

方案 1-解决星巴克咖啡订单问题分析
设计模式之java-学习笔记_第2张图片

1)Drink 是一个抽象类,表示饮料
2)des 就是对咖啡的描述, 比如咖啡的名字
3)cost() 方法就是计算费用,Drink 类中做成一个抽象方法.
4)Decaf 就是单品咖啡, 继承 Drink, 并实现 cost
5)Espress && Milk 就是单品咖啡+调料, 这个组合很多
6)问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案 2-解决星巴克咖啡订单(好点)
设计模式之java-学习笔记_第3张图片

1)前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成类数量过多。从而提高项目的维护性(如图)

方案 2-解决星巴克咖啡订单问题分析

1)方案 2 可以控制类的数量,不至于造成很多的类
2)在增加或者删除调料种类时,代码的维护量很大
3)考虑到用户可以添加多份 调料时,可以将 hasMilk 返回一个对应 int
4)考虑使用 装饰者 模式

使用装饰者模式来解决案例

装饰者模式定义

1)装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
2)这里提到的动态的将新功能附加到对象和 ocp 原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会。

装饰者模式原理

1)装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component) // 被装饰者
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
2)Component 主体:比如类似前面的 Drink
3)ConcreteComponent 和 Decorator
ConcreteComponent:具体的主体, 比如前面的各个单品咖啡
4)Decorator: 装饰者,比如各调料.
在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。
设计模式之java-学习笔记_第4张图片

装饰者模式解决星巴克咖啡订单

设计模式之java-学习笔记_第5张图片
装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
设计模式之java-学习笔记_第6张图片

代码

package com.fs.sjms.decorate;

/**
 * 装饰者模式案例
 *星巴克咖啡订单项目(咖啡馆):
 *
 * 1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
 * 2)调料:Milk、Soy(豆浆)、Chocolate
 * 3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
 * 4)使用 OO 的来计算不同种类咖啡的费用:  客户可以点单品咖啡,也可以单品咖啡+调料组合。
 *
 *
 * 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack 和 一份珍珠奶茶
 */
public class CoffeeDemo {
    public static void main(String[] args) {
        //2 份巧克力+一份牛奶的 LongBlack 和 一份珍珠奶茶
        Drink order = new LongBlack();
        order = new Chocolate(order);
        order = new Chocolate(order);
        order = new Milk(order);
        System.out.println("商品:"+order.getDescription());
        System.out.println("价格:"+order.cost());

        //珍珠奶茶
        Drink order2 = new ZhenZhuNaiCha();
        order2 = new ZhenZhu(order2);
        System.out.println("商品:"+order2.getDescription());
        System.out.println("价格:"+order2.cost());


    }
}

//饮料类  被装饰类
abstract class Drink{
    public String description; //描述信息
    private int cost = 5; //价格 初始价格5元

    //计算费用的抽象方法
    //子类来实现
    public abstract float cost();

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getCost() {
        return cost;
    }

    public void setCost(int cost) {
        this.cost = cost;
    }
}


//装饰类,继承被装饰类,组合被装饰类
class Decorator extends Drink{
    private Drink drink;

    public Decorator(Drink drink){
        this.drink = drink;
    }

    @Override
    public float cost() {
        //装饰价格加上被装饰者的价格
        return super.getCost()+drink.cost();
    }

    @Override
    public String getDescription() {
        //获取描述信息
        return " "+description + " "+drink.getDescription();
    }
}

//抽出咖啡类,来继承被装饰饮料类
class Coffee extends Drink{

    @Override
    public float cost() {
        return super.getCost();
    }
}

//抽出奶茶类,来继承被装饰饮料类
class NaiCha extends Drink{

    @Override
    public float cost() {
        return super.getCost();
    }
}

//调料类 继承装饰者类
class Milk extends Decorator{

    public Milk(Drink drink) {
        super(drink);
        //牛奶的描述与价格
        setDescription("牛奶");
        setCost(3);
    }
}
class Chocolate extends Decorator{

    public Chocolate(Drink drink) {
        super(drink);
        //牛奶的描述与价格
        setDescription("巧克力");
        setCost(2);
    }
}
class Tang extends Decorator{

    public Tang(Drink drink) {
        super(drink);
        //牛奶的描述与价格
        setDescription("糖");
        setCost(1);
    }
}

class ZhenZhu extends Decorator{

    public ZhenZhu(Drink drink) {
        super(drink);
        //牛奶的描述与价格
        setDescription("珍珠");
        setCost(1);
    }
}

//美式咖啡继承咖啡类
class LongBlack extends Coffee{
    public LongBlack(){
        setCost(5);
        setDescription("美式咖啡");
    }
}


//奶茶继承奶茶类
class ZhenZhuNaiCha extends NaiCha{
    public ZhenZhuNaiCha(){
        setCost(3);
        setDescription("奶茶");
    }
}

装饰者模式在 JDK 应用的源码分析

装饰者模式在 JDK 应用的源码分析
Java 的 IO 结构,FilterInputStream 就是一个装饰者
设计模式之java-学习笔记_第7张图片

import java.io.DataInputStream; import java.io.FileInputStream; import java.io.InputStream;

public class Decorator {


public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub

//说明
//1. InputStream  是抽象类,  类似我们前面讲的 Drink
//2. FileInputStream 是	InputStream  子类,类似我们前面的 DeCaf, LongBlack
//3. FilterInputStream	是	InputStream 子类:类似我们前面 的 Decorator 修饰者
//4. DataInputStream  是 FilterInputStream  子类,具体的修饰者,类似前面的 Milk, Soy 等
//5. FilterInputStream 类  有	protected volatile InputStream in;  即含被装饰者
//6. 分析得出在 jdk 的 io 体系中,就是使用装饰者模式


DataInputStream dis = new DataInputStream(new FileInputStream("d:\\abc.txt")); System.out.println(dis.read());
dis.close();
}


}

八 组合模式

组合模式基本介绍

基本介绍

1)组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。

2)组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
3)这种类型的设计模式属于结构型模式。
4)组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象

组合模式原理类图
设计模式之java-学习笔记_第8张图片

1)Component :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理
Component 子部件, Component 可以是抽象类或者接口
2)Leaf : 在组合中表示叶子节点,叶子节点没有子节点
3)Composite :非叶子节点, 用于存储子部件, 在 Component 接口中实现 子部件的相关操作,比如增加(add), 删除。

组合模式解决学校院系展示的 应用实例

应用实例要求
1)编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。
2)思路分析和图解(类图)
设计模式之java-学习笔记_第9张图片

代码实现

package com.fs.sjms.combination;

import java.util.ArrayList;
import java.util.List;

/**
 * 组合模式
 *
 * 应用实例要求
 * 1)编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,
 * 一个学校有多个学院, 一个学院有多个系。
 */
public class CombinationDemo {
    //测试
    public static void main(String[] args) {
        //从大到小创建对象 学校
        OrganizationComponentInterface university = new University("清华大学", " 中国顶级大学 ");

        //创建 学院
        OrganizationComponent computerCollege = new College(" 计 算 机 学 院 ", " 计 算 机 学 院 ");
        OrganizationComponent infoEngineerCollege = new College("信息工程学院", " 信息工程学院 ");

        //创建各个学院下面的系(专业)
        computerCollege.add(new Department("软件工程", " 软件工程不错 ")); computerCollege.add(new Department("网络工程", " 网络工程不错 "));
        computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
        infoEngineerCollege.add(new Department("通信工程", " 通信工程不好学 "));
        infoEngineerCollege.add(new Department("信息工程", " 信息工程好学 "));

        //将学院加入到 学校
        university.add(computerCollege);
        university.add(infoEngineerCollege);

        //打印
        university.print();
    }
}

//学习单位接口
interface OrganizationComponentInterface{
    void add(OrganizationComponentInterface organizationComponent);
    void delete(OrganizationComponentInterface organizationComponent);
    void print();
}
//学习单位抽象类
abstract class OrganizationComponent implements OrganizationComponentInterface{

    private String name;//机构名称
    private String des;//机构描述

    public OrganizationComponent(String name, String des) {
        this.name = name;
        this.des = des;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDes() {
        return des;
    }

    public void setDes(String des) {
        this.des = des;
    }

    @Override
    public void add(OrganizationComponentInterface organizationComponent) {
        throw new RuntimeException("不能添加顶级节点");
    }

    @Override
    public void delete(OrganizationComponentInterface organizationComponent) {
        throw new RuntimeException("不能删除顶级节点");
    }

    @Override
    public void print() {
        throw new RuntimeException("顶级节点无打印信息");
    }
}
//University 就是 Composite ,  可以管理 College
class University extends OrganizationComponent{
    List<OrganizationComponentInterface> organizationComponents = new ArrayList<OrganizationComponentInterface>();
    public University(String name, String des) {
        super(name, des);
    }

    @Override
    public void add(OrganizationComponentInterface organizationComponent) {
        organizationComponents.add(organizationComponent);
    }

    @Override
    public void delete(OrganizationComponentInterface organizationComponent) {
        organizationComponents.remove(organizationComponent);
    }

    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public String getDes() {
        return super.getDes();
    }

    @Override
    public void print() {
        System.out.println("---------------"+getName()+"----------------");
        for (OrganizationComponentInterface organizationComponent : organizationComponents) {
            organizationComponent.print();
        }
    }
}


//University 就是 Composite ,  可以管理 College
class College extends OrganizationComponent{
    List<OrganizationComponentInterface> organizationComponents = new ArrayList<OrganizationComponentInterface>();
    public College (String name, String des) {
        super(name, des);
    }

    @Override
    public void add(OrganizationComponentInterface organizationComponent) {
        //	将来实际业务中,Colleage  的  add  和	University add 不一定完全一样
        organizationComponents.add(organizationComponent);
    }

    @Override
    public void delete(OrganizationComponentInterface organizationComponent) {
        //	将来实际业务中,Colleage  的  add  和	University add 不一定完全一样
        organizationComponents.remove(organizationComponent);
    }

    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public String getDes() {
        return super.getDes();
    }

    @Override
    public void print() {
        System.out.println("---------------"+getName()+"----------------");
        for (OrganizationComponentInterface organizationComponent : organizationComponents) {
            organizationComponent.print();
        }
    }
}

class Department extends OrganizationComponent {

        //没有集合
    public Department(String name, String des) {
        super(name, des);
    }

    //add , remove 就不用写了,因为他是叶子节点


    @Override
    public String getName() {
        return super.getName();
    }

    @Override
    public String getDes() {
        return super.getDes();
    }

    @Override
    public void print() {
        System.out.println(getName());
    }
}

组合模式在 JDK 集合的源码分析

1)Java 的集合类-HashMap 就使用了组合模式
2)代码分析+Debug 源码

设计模式之java-学习笔记_第10张图片

组合模式的注意事项和细节

1)简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题。
2)具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,客户端不用做出任何改动.
3)方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树形结构
4)需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
5)要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性都不一样,不适合使用组合模式

九 外观模式

影院管理项目
组建一个家庭影院:
DVD 播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为: 直接用遥控器:统筹各设备开关
开爆米花机放 下 屏 幕 开 投 影 仪 开音响
开 DVD,选 dvd
去拿爆米花调 暗 灯 光 播放
观影结束后,关闭各种设备
设计模式之java-学习笔记_第11张图片
传统方式解决影院管理问题分析

1)在 ClientTest 的 main 方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
2)不利于在 ClientTest 中,去维护对子系统的操作
3)解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法
ready, play, pause, end ),用来访问子系统中的一群接口
4)也就是说 就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观模式

外观模式基本介绍

基本介绍

1)外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
2)外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节

外观模式原理类图
设计模式之java-学习笔记_第12张图片
对类图说明(分类外观模式的角色)
1)外观类(Facade): 为调用端提供统一的调用接口, 外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
2)调用者(Client): 外观接口的调用者
3)子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,他是功能的实际提供者

外观模式解决影院管理
传统方式解决影院管理说明

1)外观模式可以理解为转换一群接口,客户只要调用一个接口,而不用调用多个接口才能达到目的。比如:在 pc 上安装软件的时候经常有一键安装选项(省去选择安装目录、安装的组件等等),还有就是手机的重启功能(把关机和启动合为一个操作)。
2)外观模式就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用
3)示意图说明
设计模式之java-学习笔记_第13张图片

外观模式应用实例

1)应用实例要求
2)使用外观模式来完成家庭影院项目
3)思路分析和图解(类图)

设计模式之java-学习笔记_第14张图片

代码

package com.fs.sjms.facade;

/**
 * 外观模式
 * 影院管理项目
 * 组建一个家庭影院:
 * 

* DVD 播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为: * 直接用遥控器:统筹各设备开关 * 开爆米花机 * 放 下 屏 幕 * 开 投 影 仪 * 开音响 * 开 DVD,选 dvd * 去拿爆米花调 暗 灯 光 播放 * 观影结束后,关闭各种设备 */ public class FacadeDemo { public static void main(String[] args) { //创建我们的小爱同学 外观类,调用方法 XiaoAiStudent xiaoAiStudent = new XiaoAiStudent(); xiaoAiStudent.command("打开影院系统"); System.out.println("--------------------------------------------------------"); xiaoAiStudent.command("播放影片"); System.out.println("--------------------------------------------------------"); xiaoAiStudent.command("暂停播放"); System.out.println("--------------------------------------------------------"); xiaoAiStudent.command("关闭影院系统"); /* 爆米花 打开了 爆米花 出米花中。。。。 荧幕 下降了 投影仪 打开了 投影仪 完成自动对焦了、。。。。 立体声系统 打开了 立体声系统 已经启动.. dvd 打开了 灯光已经暗下来了.. -------------------------------------------------------- dvd 开始播放片 -------------------------------------------------------- dvd 暂停中 .. -------------------------------------------------------- 爆米花 关闭了 灯光亮咯.. 荧幕 上升了 投影仪 关闭了 立体声系统 关闭了 dvd 关闭了 */ } } /** * 小爱同学,这个类就相当于外观类,直接操作这个类 */ class XiaoAiStudent { //定义各个子系统对象 private TheaterLight theaterLight; private Popcorn popcorn; private Stereo stereo; private Projector projector; private Screen screen; private DVDPlayer dVDPlayer; //构造器 public XiaoAiStudent() { super(); this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance(); this.projector = Projector.getInstance(); this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getInstance(); } public void command(String command){ switch (command){ case ("打开影院系统"):ready(); break; case ("播放影片"):play(); break; case ("暂停播放"):pause(); break; case ("关闭影院系统"):end(); break; } } //操作分成 4 步 public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim(); } public void play() { dVDPlayer.play(); } public void pause() { dVDPlayer.pause(); } public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } } class DVDPlayer { //使用单例模式, 使用饿汉式 private static DVDPlayer instance = new DVDPlayer(); public static DVDPlayer getInstance() { return instance; } private DVDPlayer() { } public void on() { System.out.println(" dvd 打开了 "); } public void off() { System.out.println(" dvd 关闭了 "); } public void play() { System.out.println(" dvd 开始播放片 "); } public void pause() { System.out.println(" dvd 暂停中 .."); } } //爆米花 class Popcorn { private static Popcorn instance = new Popcorn(); public static Popcorn getInstance() { return instance; } private Popcorn() { } public void on() { System.out.println(" 爆米花 打开了 "); } public void off() { System.out.println(" 爆米花 关闭了 "); } public void pop() { System.out.println(" 爆米花 出米花中。。。。 "); } } //投影仪 class Projector { private static Projector instance = new Projector(); public static Projector getInstance() { return instance; } private Projector() { } public void on() { System.out.println(" 投影仪 打开了 "); focus(); } public void off() { System.out.println(" 投影仪 关闭了 "); } public void focus() { System.out.println(" 投影仪 完成自动对焦了、。。。。 "); } //... } //荧幕 class Screen { private static Screen instance = new Screen(); public static Screen getInstance() { return instance; } private Screen() { } public void up() { System.out.println(" 荧幕 上升了 "); } public void down() { System.out.println(" 荧幕 下降了 "); } } //立体声系统 class Stereo { private static Stereo instance = new Stereo(); public static Stereo getInstance() { return instance; } private Stereo() { } public void on() { System.out.println(" 立体声系统 打开了 "); up(); } public void off() { System.out.println(" 立体声系统 关闭了 "); } public void up() { System.out.println(" 立体声系统 已经启动.. "); } //... } //灯光 class TheaterLight { private static TheaterLight instance = new TheaterLight(); public static TheaterLight getInstance() { return instance; } private TheaterLight() { } public void on() { System.out.println(" 灯光系统开启 "); } public void off() { System.out.println(" 灯光系统关闭 "); } public void dim() { System.out.println(" 灯光已经暗下来了.. "); } public void bright() { System.out.println(" 灯光亮咯.. "); } }

外观模式在 MyBatis 框架应用的源码分析

1)MyBatis 中的 Configuration 去创建 MetaObject 对象使用到外观模式
设计模式之java-学习笔记_第15张图片

设计模式之java-学习笔记_第16张图片

外观模式的注意事项和细节

1)外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
2)外观模式对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展
3)通过合理的使用外观模式,可以帮我们更好的划分访问的层次
4)当系统需要进行分层设计时,可以考虑使用 Facade 模式
5)在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个
Facade 类,来提供遗留系统的比较清晰简单的接口,让新系统与 Facade 类交互,提高复用性
6)不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。

十 享元模式

展示网站项目需求
小型的外包项目,给客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
1)有客户要求以新闻的形式发布
2)有客户人要求以博客的形式发布
3)有客户希望以微信公众号的形式发布

传统方案解决网站展现项目

1)直接复制粘贴一份,然后根据客户不同要求,进行定制修改
2)给每个网站租用一个空间
3)方案设计示意图

设计模式之java-学习笔记_第17张图片
传统方案解决网站展现项目-问题分析

1)需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
2)解决思路:整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源
3)对于代码来说,由于是一份实例,维护和扩展都更加容易
4)上面的解决思路就可以使用 享元模式 来解决

享元模式基本介绍

基本介绍
1)享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
2)常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个
3)享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
4)享元模式经典的应用场景就是池技术了,String 常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

设计模式之java-学习笔记_第18张图片
享元模式的原理类图

设计模式之java-学习笔记_第19张图片
对类图的说明
对原理图的说明-即(模式的角色及职责)
1)FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态(后面介绍) 的接口或实现
2)ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
3)UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
4)FlyWeightFactory 享元工厂类,用于构建一个池容器(集合), 同时提供从池中获取对象方法

内部状态和外部状态
比如围棋、五子棋、跳棋,它们都有大量的棋子对象,围棋和五子棋只有黑白两色,跳棋颜色多一点,所以棋子颜色就是棋子的内部状态;而各个棋子之间的差别就是位置的不同,当我们落子后,落子颜色是定的,但位置是变化的,所以棋子坐标就是棋子的外部状态

1)享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
2)内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
3)外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。

4)举个例子:围棋理论上有 361 个空位可以放棋子,每盘棋都有可能有两三百个棋子对象产生,因为内存空间有限,一台服务器很难支持更多的玩家玩围棋游戏,如果用享元模式来处理棋子,那么棋子对象就可以减少到只有两个实例,这样就很好的解决了对象的开销问题

享元模式解决网站展现项目

1)应用实例要求
使用享元模式完成,前面提出的网站外包问题

1)应用实例要求
使用享元模式完成,前面提出的网站外包问题

2)思路分析和图解(类图)
设计模式之java-学习笔记_第20张图片

代码实现

package com.fs.sjms.flyweightPattern;

import java.util.HashMap;
import java.util.Map;

/**
 * 享元模式Demo
 *
 * 展示网站项目需求
 * 小型的外包项目,给客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同:
 * 1)有客户要求以新闻的形式发布
 * 2)有客户人要求以博客的形式发布
 * 3)有客户希望以微信公众号的形式发布
 */
public class FlyweightPatternDemo {
    public static void main(String[] args) {
        //创建一个工厂类
        WebFactory webFactory = new WebFactory();
        WebProject xw1 = webFactory.getWebProject("新闻");
        WebProject xw2 = webFactory.getWebProject("新闻");
        WebProject xw3 = webFactory.getWebProject("新闻");
        WebProject bk1 = webFactory.getWebProject("博客");
        WebProject bk2 = webFactory.getWebProject("博客");
        WebProject wx = webFactory.getWebProject("微信公众号");

        xw1.webUse(new User("小米"));
        xw2.webUse(new User("小画"));
        xw3.webUse(new User("小力"));
        bk1.webUse(new User("强强"));
        bk2.webUse(new User("马马"));
        wx.webUse(new User("蕾蕾"));

        //输出下总的网站类型书
        System.out.println("总数:"+webFactory.WebSum());
        /*
网站类型:新闻 使用方:小米
网站类型:新闻 使用方:小画
网站类型:新闻 使用方:小力
网站类型:博客 使用方:强强
网站类型:博客 使用方:马马
网站类型:微信公众号 使用方:蕾蕾
总数:3
         */
        //由输出得到web被共享复用了,这就是享元模式

    }
}


//网站项目接口
interface WebProject{
    void webUse(User user);
}


//用户  是外部状态  外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
class User{
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//具体的网站 实现我们的接口
class WebEntity implements WebProject{

    //type   就是内部状态
    // 内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
    private String type = "";

    public WebEntity(String type) {
        this.type = type;
    }

    @Override
    public void webUse(User user) {
        System.out.println("网站类型:"+type+" 使用方:"+user.getName());
    }
}

//网站工厂类
class WebFactory{
    //创建一个map来存储我们共享的web项目,当有相同的内部状态的时候就直接从map中取,没有就创建一个放在map中,
    // 后面有用户(外部状态)来使用web项目的时候就在我们的map中取
    private Map<String, Object> map = new HashMap<>();

    public WebProject getWebProject(String type){
        if (!map.containsKey(type)){
            //没有就创建,存放在map中作为共享资源
            map.put(type,new WebEntity(type));
        }
        return (WebProject)map.get(type);
    }

    //返回当前map中有多少对象(元)
    public int WebSum(){
        return map.size();
    }
}

享元模式在 JDK-Interger 的应用源码分析

1)Integer 中的享元模式
代码分析+Debug 源码+说明
设计模式之java-学习笔记_第21张图片

代码说明

package com.fs.sjms.flyweightPattern;



public class FlyWeight {
    public static void main(String[] args) {
//如果  Integer.valueOf(x) x 在	-128 --- 127 直接,就是使用享元模式返回,如果不在
//范围类,则仍然 new

//小结:
//1. 在 valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的 Integer(new),  否则,就直接从 缓存池返回
//2. valueOf 方法,就使用到享元模式
//3. 如果使用 valueOf 方法得到一个 Integer 实例,范围在 -128 - 127  ,执行速度比 new 快



        Integer x = Integer.valueOf(127); // 得到 x 实例,类型 Integer
        Integer y = new Integer(127); // 得 到 y 实 例 , 类 型 Integer
        Integer z = Integer.valueOf(127);//..
        Integer w = new Integer(127);

        System.out.println(x.equals(y)); // 大小,true
        System.out.println(x == y ); //	false
        System.out.println(x == z ); // true   源码中127在范围内,使用的是享元模式,取得共享的元
        System.out.println(w == x ); // false
        System.out.println(w == y ); // false



        Integer x1 = Integer.valueOf(200);
        Integer x2 = Integer.valueOf(200);

        //而200  不在范围内,就不是从共享的元中获取,而且新new
        System.out.println("x1==x2" + (x1 == x2)); // false
    }
}

享元模式的注意事项和细节

1)在享元模式这样理解,“享”就表示共享,“元”表示对象
2)系统中有大量对象,这些对象消耗大量内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
3)用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用 HashMap/HashTable 存储
4)享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
5)享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.
6)使用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加以控制。
7)享元模式经典的应用场景是需要缓冲池的场景,比如 String 常量池、数据库连接池

十一 代理模式

代理模式(Proxy)

代理模式的基本介绍

1)代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
2)被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
3)代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
4)代理模式示意图
设计模式之java-学习笔记_第22张图片

静态代理

静态代码模式的基本介绍

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

静态代理应用实例

具体要求
1)定义一个接口:ITeacherDao
2)目标对象 TeacherDAO 实现接口 ITeacherDAO
3)使用静态代理方式,就需要在代理对象 TeacherDAOProxy 中也实现 ITeacherDAO
4)调用的时候通过调用代理对象的方法来调用目标对象.
5)特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

思路分析图解(类图)
设计模式之java-学习笔记_第23张图片

静态代理代码

package com.fs.sjms.proxy.jt;

/**
 * 静态代理
 *
 * 应用实例
 * 具体要求
 * 1)定义一个接口:ITeacherDao
 * 2)目标对象 TeacherDAO 实现接口 ITeacherDAO
 * 3)使用静态代理方式,就需要在代理对象 TeacherDAOProxy 中也实现 ITeacherDAO
 * 4)调用的时候通过调用代理对象的方法来调用目标对象.
 * 5)特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
 */
public class StaticDemo {
    public static void main(String[] args) {
        //测试
        //创建被代理类
        TeacherDAO teacherDAO = new TeacherDAO();
        //创建代理类,并制定代理对象
        TeacherDAOProxy teacherDAOProxy = new TeacherDAOProxy(teacherDAO);
        teacherDAOProxy.teacher();
        /*
上课前先备课。。。
老师正在授课
下课后批改作业。。。
         */
    }
}

interface ITeacherDao{
    void teacher();
}

class TeacherDAO implements ITeacherDao{
    @Override
    public void teacher() {
        System.out.println("老师正在授课");
    }
}

//代理类,对被代理类进行争强
class TeacherDAOProxy implements ITeacherDao{

    //被代理的类
    private TeacherDAO teacherDAO = null;

    public TeacherDAOProxy(TeacherDAO teacherDAO) {
        this.teacherDAO = teacherDAO;
    }

    //代理方法
    @Override
    public void teacher() {
        System.out.println("上课前先备课。。。");
        teacherDAO.teacher();
        System.out.println("下课后批改作业。。。");
    }
}

静态代理优缺点

1)优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
2)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类
3)一旦接口增加方法,目标对象与代理对象都要维护

动态代理

动态代理模式的基本介绍

1)代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
2)代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象
3)动态代理也叫做:JDK 代理、接口代理

JDK 中生成代理对象的 API

1)代理类所在包:java.lang.reflect.Proxy
2)JDK 实现代理只需要使用 newProxyInstance 方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h )

JDK动态代理应用实例

应用实例要求
将前面的静态代理改进成动态代理模式(即:JDK 代理模式)
设计模式之java-学习笔记_第24张图片

动态代理案例代码实现

package com.fs.sjms.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * JDK动态代理应用实例
 */
public class JDKProxyDemo {
    public static void main(String[] args) {
        //测试jdk动态代理
        JDKProxyTeacher jdkProxyTeacher = new JDKProxyTeacher(new TeacherDAO());
        ITeacherDao jdkProxyInstance = (ITeacherDao) jdkProxyTeacher.getJDKProxyInstance();
        System.out.println(jdkProxyInstance);
        System.out.println("---------------------------------");
        jdkProxyInstance.teacher();
        System.out.println("---------------------------------");
        jdkProxyInstance.xiaKe();

        /*
com.fs.sjms.proxy.jdk.TeacherDAO@5cad8086
---------------------------------
老师正在授课...
要好好上课,同学们。。。
---------------------------------
下课了,老师要拖下堂。。。
老师,下课要正常下课,不要拖堂。。。
那么就下课吧。。。
         */
    }
}

//老师接口
interface ITeacherDao{
    void teacher();
    void xiaKe();
}

//老师实现类
class TeacherDAO implements ITeacherDao {
    @Override
    public void teacher() {
        System.out.println("老师正在授课...");
    }

    //我们将原本的拖堂这个方法动态代理了,改为正常下课
    @Override
    public void xiaKe() {
        System.out.println("下课了,老师要拖下堂。。。");
    }
}


//动态代理类
class JDKProxyTeacher{
    //被代理对象
    private ITeacherDao teacherDAO = null;

    public JDKProxyTeacher(TeacherDAO teacherDAO) {
        this.teacherDAO = teacherDAO;
    }

    public Object getJDKProxyInstance(){

        //说明
/*
*	public static Object newProxyInstance(ClassLoader loader,
Class[] interfaces, InvocationHandler h)

//1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
//2. Class[] interfaces:  目标对象实现的接口类型,使用泛型方法确认类型
//3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
 */
        return Proxy.newProxyInstance(
                teacherDAO.getClass().getClassLoader(),
                teacherDAO.getClass().getInterfaces(),
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //判断被代理的对象的方法是xiake就对这个方法进行动态代理
                String name = method.getName();
                if (name.equals("xiaKe")){
                    //那么就修改动态代理一下
                    Object invoke = method.invoke(teacherDAO, args);
                    System.out.println("老师,下课要正常下课,不要拖堂。。。");
                    System.out.println("那么就下课吧。。。");
                    return invoke;
                }else if (name.equals("teacher")){
                    //是teacher方法,那么新增业务让 告诉同学好好上课
                    Object invoke = method.invoke(teacherDAO, args);
                    System.out.println("要好好上课,同学们。。。");
                    return invoke;
                }
                return method.invoke(teacherDAO, args);
            }
        });
    }
}

Cglib 代理

Cglib 代理模式的基本介绍

1)静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是 Cglib 代理
2)Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展, 有些书也将Cglib 代理归属到动态代理。
3)Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
4)在 AOP 编程中如何选择代理模式:
1.目标对象需要实现接口,用 JDK 代理
2.目标对象不需要实现接口,用 Cglib 代理
5)Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类

Cglib 代理模式实现步骤

1)需要引入 cglib 的 jar 文件或者maven依赖

        
        <dependency>
            <groupId>cglibgroupId>
            <artifactId>cglibartifactId>
            <version>3.3.0version>
        dependency>

2)在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
3)目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

Cglib 代理模式应用实例

应用实例要求
将前面的案例用 Cglib 代理模式实现
思路图解(类图)
设计模式之java-学习笔记_第25张图片

CGLib 代码实现

package com.fs.sjms.proxy.cglib;


import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLib 动态代理
 */
public class CGLibProxyDemo {
    public static void main(String[] args) {
        //创建被代理类
        Teacher teacher = new Teacher();
        //创建代理工厂类
        CGLibProxyTeacherFactory factory = new CGLibProxyTeacherFactory(teacher);
        Teacher cgLinProxyInstance = (Teacher) factory.getCGLinProxyInstance();
        System.out.println(cgLinProxyInstance);
        System.out.println("---------------------------------");
        cgLinProxyInstance.teacher();
        System.out.println("---------------------------------");
        cgLinProxyInstance.xiaKe();

        /*
com.fs.sjms.proxy.cglib.Teacher@619a5dff
---------------------------------
老师正在授课...
要好好上课,同学们。。。
---------------------------------
下课了,老师要拖下堂。。。
老师,下课要正常下课,不要拖堂。。。
那么就下课吧。。。
         */
    }
}


//老师类 要被cglib代理,不能final修饰
class Teacher  {
    public void teacher() {
        System.out.println("老师正在授课...");
    }

    //我们将原本的拖堂这个方法动态代理了,改为正常下课
    public void xiaKe() {
        System.out.println("下课了,老师要拖下堂。。。");
    }
}


//动态代理类,实现MethodInterceptor 从写 intercept 传递被代理类对象,实现类的动态代理
class CGLibProxyTeacherFactory implements MethodInterceptor{

    private Teacher teacher;

    public CGLibProxyTeacherFactory(Teacher teacher) {
        this.teacher = teacher;
    }

    //返回一个代理对象:	是 target  对象的代理对象
    public Object getCGLinProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(teacher.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //判断被代理的对象的方法是xiake就对这个方法进行动态代理
        String name = method.getName();
        if (name.equals("xiaKe")){
            //那么就修改动态代理一下
            Object invoke = method.invoke(teacher, args);
            System.out.println("老师,下课要正常下课,不要拖堂。。。");
            System.out.println("那么就下课吧。。。");
            return invoke;
        }else if (name.equals("teacher")){
            //是teacher方法,那么新增业务让 告诉同学好好上课
            Object invoke = method.invoke(teacher, args);
            System.out.println("要好好上课,同学们。。。");
            return invoke;
        }
        return method.invoke(teacher, args);
    }
}

几种常见的代理模式介绍— 几种变体

1)防火墙代理
内网通过代理穿透防火墙,实现对公网的访问。
2)缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则 ok,如果取不到资源,再到公网或者数据库取,然后缓存。

3)远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息。
4)同步代理:主要使用在多线程编程中,完成多线程间同步工作同步代理:主要使用在多线程编程中,完成多线程间同步工作

十二 模板方法模式

豆浆制作问题
编写制作豆浆的程序,说明如下:

1)制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
2)通过添加不同的配料,可以制作出不同口味的豆浆
3)选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
4)请使用 模板方法模式 完成 (说明:因为模板方法模式,比较简单,很容易就想到这个方案,因此就直接使用, 不再使用传统的方案来引出模板方法模式 )

模板方法模式基本介绍

基本介绍

1)模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),z 在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
2)简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
3)这种类型的设计模式属于行为型模式。

模板方法模式原理类图
设计模式之java-学习笔记_第26张图片

对原理类图的说明-即(模板方法模式的角色及职责)
1)AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法 operationr2,3,4
2)ConcreteClass 实现抽象方法 operationr2,3,4, 以完成算法中特点子类的步骤

模板方法模式解决豆浆制作问题

1)应用实例要求
编写制作豆浆的程序,说明如下:
制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎通过添加不同的配料,可以制作出不同口味的豆浆
选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红豆、花生豆浆。。。)

2)思路分析和图解(类图)

设计模式之java-学习笔记_第27张图片
模板方法模式的钩子方法
1)在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
2)还是用上面做豆浆的例子来讲解,比如,我们还希望制作纯豆浆,不添加任何的配料,请使用钩子方法对前面的模板方法进行改造

模板模式案例代码演示:

package com.fs.sjms.template;

/**
 * 模板模式
 *
 * 1)应用实例要求
 * 编写制作豆浆的程序,说明如下:
 * 制作豆浆的流程 选材--->添加配料--->浸泡--->放到豆浆机打碎通过添加不同的配料,可以制作出不同口味的豆浆
 * 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红豆、花生豆浆。纯豆浆。。)
 */
public class TemplateDemo {
    public static void main(String[] args) {
        //测试
        SoyMilk peanutSoyMilk = new PeanutSoyMilk();
        peanutSoyMilk.make();
        System.out.println("-----------------------");
        SoyMilk redBeanSoyMilk = new RedBeanSoyMilk();
        redBeanSoyMilk.make();
        System.out.println("-----------------------------");
        SoyMilk pureSoyMilk = new pureSoyMilk();
        pureSoyMilk.make();

        /*
选择好的新鲜黄豆
 加入上好的: 花生
黄豆与:花生浸泡3小时
制作成豆浆。。。
-----------------------
选择好的新鲜黄豆
 加入上好的: 红豆
黄豆与:红豆浸泡3小时
制作成豆浆。。。
-----------------------------
选择好的新鲜黄豆
黄豆与:null浸泡3小时
制作成豆浆。。。
         */
    }
}

//制作豆浆抽象类
abstract class SoyMilk{

    public String dosing;//配料

    //制作豆浆 模板方法, make , 模板方法可以做成 final , 不让子类去覆盖.
    public final void make(){
        select();
        //如果没有配料,那么就没有这一步
        if (isPure()){
            add();
        }
        soak();
        beat();
    }

    private void select(){
        System.out.println("选择好的新鲜黄豆	");
    }

    //抽象方法,来添加什么配料
    public abstract void add();

    private void soak(){
        System.out.println("黄豆与:"+dosing+"浸泡3小时");
    }

    private void beat(){
        System.out.println("制作成豆浆。。。");
    }

    //钩子方法,决定是否需要添加配料
    private boolean isPure(){
        return dosing != null;
    }
}




class PeanutSoyMilk extends SoyMilk {
    public PeanutSoyMilk() {
        super.dosing = "花生";
    }

    @Override
    public void add() {
        System.out.println(" 加入上好的: "+dosing);
    }

}


class RedBeanSoyMilk extends SoyMilk {
    public RedBeanSoyMilk() {
        super.dosing = "红豆";
    }
    @Override
    public void add() {
        System.out.println(" 加入上好的: "+dosing);
    }
}

class pureSoyMilk extends SoyMilk {

    //纯豆浆,空实现
    @Override
    public void add() {

    }

}

模板方法模式在 Spring 框架应用的源码分析

1)Spring IOC 容器初始化时运用到的模板方法模式
2)代码分析+角色分析+说明类图
设计模式之java-学习笔记_第28张图片
3)针对源码的类图(说明层次关系)
设计模式之java-学习笔记_第29张图片

模板方法模式的注意事项和细节

1)基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
2)实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
3)既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
4)该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
5)一般模板方法都加上 final 关键字, 防止子类重写模板方法.
6)模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理

十三 命令模式

智能生活项目需求
看一个具体的需求

1)我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作。
2)这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个 App,分别控制,我们希望只要一个 app
就可以控制全部智能家电。
3)要实现一个 app 控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给 app 调用,这时 就可以考虑使用命令模式。
4)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
5)在我们的例子中,动作的请求者是手机 app,动作的执行者是每个厂商的一个家电产品

命令模式基本介绍

1)命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2)命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3)在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。

4)通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象

命令模式的原理类图
设计模式之java-学习笔记_第30张图片
对原理类图的说明-即(命名模式的角色及职责)
1)Invoker 是调用者角色
2)Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
3)Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
4)ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现 execute

命令模式解决智能生活项目

应用实例要求
1)编写程序,使用命令模式 完成前面的智能家电项目
2)思路分析和图解
设计模式之java-学习笔记_第31张图片

代码实现

package com.fs.sjms.command;

/**
 * 命令模式案例
 * 

* 应用实例要求 * 1)编写程序,使用命令模式 完成前面的智能家电项目 * 2)思路分析和图解 */ public class CommandDemo { public static void main(String[] args) { //使用命令设计模式,完成通过遥控器,对电灯的操作 //创建电灯的对象(接受者) LightReceiver lightReceiver = new LightReceiver(); //创建电灯相关的开关命令 LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver); //需要一个遥控器 RemoteController remoteController = new RemoteController(); //给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作 remoteController.setCommand(0, lightOnCommand, lightOffCommand); System.out.println("--------按下灯的开按钮-----------"); remoteController.onButtonWasPushed(0); System.out.println("--------按下灯的关按钮-----------"); remoteController.offButtonWasPushed(0); System.out.println("--------按下撤销按钮-----------"); remoteController.undoButtonWasPushed(); System.out.println("=========使用遥控器操作电视机=========="); TVReceiver tvReceiver = new TVReceiver(); TVOffCommand tvOffCommand = new TVOffCommand(tvReceiver); TVOnCommand tvOnCommand = new TVOnCommand(tvReceiver); //给我们的遥控器设置命令, 比如 no = 1 是电视机的开和关的操作 remoteController.setCommand(1, tvOnCommand, tvOffCommand); System.out.println("--------按下电视机的开按钮-----------"); remoteController.onButtonWasPushed(1); System.out.println("--------按下电视机的关按钮-----------"); remoteController.offButtonWasPushed(1); System.out.println("-------- 按 下 撤 销 按 钮 -----------"); remoteController.undoButtonWasPushed(); /* --------按下灯的开按钮----------- --------按下灯的关按钮----------- 电灯关闭了.. --------按下撤销按钮----------- 电灯打开了.. =========使用遥控器操作电视机========== --------按下电视机的开按钮----------- 电视机打开了.. --------按下电视机的关按钮----------- 电视机关闭了.. -------- 按 下 撤 销 按 钮 ----------- 电视机打开了.. */ } } //创建命令接口 interface Command { //执行动作(操作) public void execute(); //撤销动作(操作) public void undo(); } class LightOffCommand implements Command { // 聚 合 LightReceiver LightReceiver light; // 构造器 public LightOffCommand(LightReceiver light) { super(); this.light = light; } @Override public void execute() { // 调用接收者的方法 light.off(); } @Override public void undo() { // 调用接收者的方法 light.on(); } } class LightOnCommand implements Command { //聚合 LightReceiver LightReceiver light; //构造器 public LightOnCommand(LightReceiver light) { super(); this.light = light; } @Override public void execute() { //调用接收者的方法 light.on(); } @Override public void undo() { //调用接收者的方法 light.off(); } } class LightReceiver { public void on() { System.out.println(" 电灯打开了.. "); } public void off() { System.out.println(" 电灯关闭了.. "); } } /** * 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做 * 其实,这样是一种设计模式, 可以省掉对空判断 * * @author Administrator */ class NoCommand implements Command { @Override public void execute() { } @Override public void undo() { } } class RemoteController { // 开 按钮的命令数组 Command[] onCommands; Command[] offCommands; // 执行撤销的命令 Command undoCommand; // 构造器,完成对按钮初始化 public RemoteController() { onCommands = new Command[5]; offCommands = new Command[5]; for (int i = 0; i < 5; i++) { onCommands[i] = new NoCommand(); offCommands[i] = new NoCommand(); } } // 给我们的按钮设置你需要的命令 public void setCommand(int no, Command onCommand, Command offCommand) { onCommands[no] = onCommand; offCommands[no] = offCommand; } // 按下开按钮 public void onButtonWasPushed(int no) { // no 0 // 找到你按下的开的按钮, 并调用对应方法 onCommands[no].execute(); // 记录这次的操作,用于撤销 undoCommand = onCommands[no]; } // 按下开按钮 public void offButtonWasPushed(int no) { // no 0 // 找到你按下的关的按钮, 并调用对应方法 offCommands[no].execute(); // 记录这次的操作,用于撤销 undoCommand = offCommands[no]; } // 按下撤销按钮 public void undoButtonWasPushed() { undoCommand.undo(); } } class TVOffCommand implements Command { // 聚 合 TVReceiver TVReceiver tv; // 构造器 public TVOffCommand(TVReceiver tv) { super(); this.tv = tv; } @Override public void execute() { // 调用接收者的方法 tv.off(); } @Override public void undo() { // 调用接收者的方法 tv.on(); } } class TVOnCommand implements Command { // 聚 合 TVReceiver TVReceiver tv; // 构造器 public TVOnCommand(TVReceiver tv) { super(); this.tv = tv; } @Override public void execute() { // 调用接收者的方法 tv.on(); } @Override public void undo() { // 调用接收者的方法 tv.off(); } } class TVReceiver { public void on() { System.out.println(" 电视机打开了.. "); } public void off() { System.out.println(" 电视机关闭了.. "); } }

命令模式在 Spring 框架 JdbcTemplate 应用的源码分析

1)Spring 框架的 JdbcTemplate 就使用到了命令模式
2)代码分析
设计模式之java-学习笔记_第32张图片
StatementCallback 接口 ,类似命令接口(Command)
class QueryStatementCallback implements StatementCallback, SqlProvider , 匿名内部类, 实现了命令接口, 同时也充当命令接收者
命令调用者 是 JdbcTemplate , 其中 execute(StatementCallback action) 方法中,调用 action.doInStatement 方法. 不同的 实现 StatementCallback 接口的对象,对应不同的 doInStatemnt 实现逻辑
另外实现 StatementCallback 命令接口的子类还有 QueryStatementCallback、

命令模式的注意事项和细节

1)将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
2)容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
3)容易实现对请求的撤销和重做
4)命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
5)空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
6)命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发- 反馈机制

十四 访问者模式

测评系统的需求
完成测评系统需求
1)将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)
2)传统方案

传统方式的问题分析

1)如果系统比较小,还是 ok 的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了 ocp 原则, 不利于维护
2)扩展性不好,比如 增加了 新的人员类型,或者管理方法,都不好做
3)引出我们会使用新的设计模式 – 访问者模式

访问者模式基本介绍

1)访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2)主要将数据结构与数据操作分离,解决 数据结构和操作耦合性问题
3)访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口

4)访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时

需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决

访问者模式的原理类图
设计模式之java-学习笔记_第33张图片
对原理类图的说明-
即(访问者模式的角色及职责)

1)Visitor 是抽象访问者,为该对象结构中的 ConcreteElement 的每一个类声明一个 visit 操作
2)ConcreteVisitor :是一个具体的访问值 实现每个有 Visitor 声明的操作,是每个操作实现的部分.
3)ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
4)Element 定义一个 accept 方法,接收一个访问者对象
5)ConcreteElement 为具体元素,实现了 accept 方法

访问者模式应用实例

应用实例要求
1)将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等),请使用访问者模式来说实现

2)思路分析和图解(类图)
设计模式之java-学习笔记_第34张图片

代码实现

package com.fs.sjms.visitor;

import java.util.LinkedList;
import java.util.List;

/**
 * 访问者模式案例
 * 

* 应用实例要求 * 1)将人分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等),请使用访问者模式来说实现 */ public class VisitorDemo { public static void main(String[] args) { //创建 ObjectStructure ObjectStructure objectStructure = new ObjectStructure(); objectStructure.attach(new Man()); objectStructure.attach(new Woman()); //成功 Success success = new Success(); objectStructure.display(success); System.out.println("==============="); Fail fail = new Fail(); objectStructure.display(fail); System.out.println("=======给的是待定的测评========"); Wait wait = new Wait(); objectStructure.display(wait); /* 该男同学歌唱比赛成功晋级。。。。 该女同学歌唱比赛成功晋级。。。。 =============== 该男同学歌唱比赛失败。。。。 该女同学歌唱比赛失败。。。。 =======给的是待定的测评======== 该男同学歌唱比赛待定。。。。 该女同学歌唱比赛待定。。。。 */ } } interface Action { //评分接口 void score(Person person); } //人类 abstract class Person { abstract void accept(Action action); } //说明 //1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递 Woman 中(第一次分派) //2. 然后 Woman 类调用作为参数的 "具体方法" 中方法 score, 同时将自己(this)作为参数 // 传入,完成第二次的分派 class Man extends Person { @Override void accept(Action action) { action.score(this); } } class Woman extends Person { @Override void accept(Action action) { action.score(this); } } //评分等级为成功 class Success implements Action { @Override public void score(Person person) { if (person.getClass().getName().equals(Man.class.getName())) { System.out.println("该男同学歌唱比赛成功晋级。。。。"); } else if (person.getClass().getName().equals(Woman.class.getName())) { System.out.println("该女同学歌唱比赛成功晋级。。。。"); } } } //评分等级为失败 class Fail implements Action { @Override public void score(Person person) { if (person.getClass().getName().equals(Man.class.getName())) { System.out.println("该男同学歌唱比赛失败。。。。"); } else if (person.getClass().getName().equals(Woman.class.getName())) { System.out.println("该女同学歌唱比赛失败。。。。"); } } } //评分等级为待定 class Wait implements Action { @Override public void score(Person person) { if (person.getClass().getName().equals(Man.class.getName())) { System.out.println("该男同学歌唱比赛待定。。。。"); } else if (person.getClass().getName().equals(Woman.class.getName())) { System.out.println("该女同学歌唱比赛待定。。。。"); } } } //数据结构,管理很多人(Man , Woman) class ObjectStructure { //维护了一个集合 private List<Person> persons = new LinkedList<>(); //增加到 list public void attach(Person p) { persons.add(p); } //移除 public void detach(Person p) { persons.remove(p); } //显示测评情况 public void display(Action action) { for (Person p : persons) { p.accept(action); } } }

4)应用案例的小结-双分派

-上面提到了双分派,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型

  • 以上述实例为例,假设我们要添加一个 Wait 的状态类,考察 Man 类和 Woman 类的反应,由于使用了双分派,只需增加一个 Action 子类即可在客户端调用即可,不需要改动任何其他类的代码。

访问者模式的注意事项和细节

优点
1)访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
2)访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统
缺点
1)具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
2)违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
3)因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.

十五 迭代器模式

看一个具体的需求
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。

传统的方式的问题分析

1)将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
2)实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的遍历的操作

3)解决方案:=> 迭代器模式

迭代器模式基本介绍

基本介绍

1)迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
2)如果我们的集合元素是用不同的方式实现的,有数组,还有 java 的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3)迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。
19.5迭代器模式的原理类图

设计模式之java-学习笔记_第35张图片
对原理类图的说明-即(迭代器模式的角色及职责)
1)Iterator : 迭代器接口,是系统提供,含义 hasNext, next, remove
2)ConcreteIterator : 具体的迭代器类,管理迭代
3)Aggregate :一个统一的聚合接口, 将客户端和具体聚合解耦
4)ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历集合
5)Client :客户端, 通过 Iterator 和 Aggregate 依赖子类

迭代器模式应用实例

1)应用实例要求
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。

2)设计思路分析
设计模式之java-学习笔记_第36张图片

代码实现

package com.fs.sjms.iteration;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 迭代器模式案例
 * 

* 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。 */ public class IterationDemo { public static void main(String[] args) { //创建学院 List<College> collegeList = new ArrayList<College>(); ComputerCollege computerCollege = new ComputerCollege(); InfoCollege infoCollege = new InfoCollege(); collegeList.add(computerCollege); collegeList.add(infoCollege); OutPutImpl outPutImpl = new OutPutImpl(collegeList); outPutImpl.printCollege(); /* === 计算机学院===== Java 专业 PHP 专业 大数据专业 === 信息工程学院===== 信息安全专业 网络安全专业 服务器安全专业 */ } } interface College { public String getName(); //增加系的方法 public void addDepartment(String name, String desc); //返回一个迭代器,遍历 public Iterator createIterator(); } class ComputerCollege implements College { Department[] departments; int numOfDepartment = 0;// 保存当前数组的对象个数 public ComputerCollege() { departments = new Department[5]; addDepartment("Java 专业", " Java 专业 "); addDepartment("PHP 专业", " PHP 专业 "); addDepartment("大数据专业", " 大数据专业 "); } @Override public String getName() { return "计算机学院"; } @Override public void addDepartment(String name, String desc) { Department department = new Department(name, desc); departments[numOfDepartment] = department; numOfDepartment += 1; } @Override public Iterator createIterator() { return new ComputerCollegeIterator(departments); } } class ComputerCollegeIterator implements Iterator { //这里我们需要 Department 是以怎样的方式存放=>数组 Department[] departments; int position = 0; //遍历的位置 public ComputerCollegeIterator(Department[] departments) { this.departments = departments; } //判断是否还有下一个元素 @Override public boolean hasNext() { if (position >= departments.length || departments[position] == null) { return false; } else { return true; } } @Override public Object next() { Department department = departments[position]; position += 1; return department; } //删除的方法,默认空实现 public void remove() { } } //系 class Department { private String name; private String desc; public Department(String name, String desc) { super(); this.name = name; this.desc = desc; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } class InfoColleageIterator implements Iterator { List<Department> departmentList; // 信息工程学院是以 List 方式存放系 int index = -1;//索引 public InfoColleageIterator(List<Department> departmentList) { this.departmentList = departmentList; } //判断 list 中还有没有下一个元素 @Override public boolean hasNext() { if (index >= departmentList.size() - 1) { return false; } else { index += 1; return true; } } @Override public Object next() { return departmentList.get(index); } // 空 实 现 public void remove() { } } class InfoCollege implements College { List<Department> departmentList; public InfoCollege() { departmentList = new ArrayList<Department>(); addDepartment("信息安全专业", " 信息安全专业 "); addDepartment("网络安全专业", " 网络安全专业 "); addDepartment("服务器安全专业", " 服务器安全专业 "); } @Override public String getName() { return "信息工程学院"; } @Override public void addDepartment(String name, String desc) { Department department = new Department(name, desc); departmentList.add(department); } @Override public Iterator createIterator() { return new InfoColleageIterator(departmentList); } } class OutPutImpl { //学院集合 List<College> collegeList; public OutPutImpl(List<College> collegeList) { this.collegeList = collegeList; } //遍历所有学院,然后调用 printDepartment 输出各个学院的系 public void printCollege() { //从 collegeList 取出所有学院, Java 中的 List 已经实现 Iterator<College> iterator = collegeList.iterator(); while (iterator.hasNext()) { //取出一个学院 College college = iterator.next(); System.out.println("=== " + college.getName() + "====="); printDepartment(college.createIterator()); //得到对应迭代器 } } //输出 学院输出 系 public void printDepartment(Iterator iterator) { while (iterator.hasNext()) { Department d = (Department) iterator.next(); System.out.println(d.getName()); } } }

迭代器模式在 JDK-ArrayList 集合应用的源码分析

1)JDK 的 ArrayList 集合中就使用了迭代器模式
2)代码分析+类图+说明
设计模式之java-学习笔记_第37张图片
设计模式之java-学习笔记_第38张图片
3)对类图的角色分析和说明
内部类 Itr 充当具体实现迭代器 Iterator 的类, 作为 ArrayList 内部类
List 就是充当了聚合接口,含有一个 iterator() 方法,返回一个迭代器对象
ArrayList 是实现聚合接口 List 的子类,实现了 iterator()
Iterator 接口系统提供
迭代器模式解决了 不同集合(ArrayList ,LinkedList) 统一遍历问题

迭代器模式的注意事项和细节

优点
1)提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。
2)隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。
3)提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到了迭代器。
4)当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式

缺点
每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类

十六 观察者模式

观察者模式原理

1)观察者模式类似订牛奶业务
2)奶站/气象局:Subject
3)用户/第三方网站:Observer

Subject:登记注册、移除和通知
1)registerObserver 注 册
2)removeObserver 移 除
3)notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送, 看具体需求定
Observer:接收输入

观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为 Observer,Subject
通知 Observer 变化,比如这里的奶站是 Subject,是 1 的一方。用户时 Observer,是多的一方。

观察者模式解决天气预报需求

类图说明
设计模式之java-学习笔记_第39张图片

20.4.2代码实现

package com.fs.sjms.observer;

import java.util.ArrayList;

/**
 * 观察者模式案列
 * 

* 1)气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。 * 2)需要设计开放型 API,便于其他第三方也能接入气象站获取数据。 * 3)提供温度、气压和湿度的接口 * 4)测量数据更新时,要能实时的通知给第三方 */ public class ObserverDemo { public static void main(String[] args) { //创建一个 WeatherData WeatherData weatherData = new WeatherData(); //创建观察者 CurrentConditions currentConditions = new CurrentConditions(); BaiduSite baiduSite = new BaiduSite(); // 注 册 到 weatherData weatherData.registerObserver(currentConditions); weatherData.registerObserver(baiduSite); // 测 试 System.out.println("通知各个注册的观察者, 看看信息"); weatherData.setData(10f, 100f, 30.3f); weatherData.removeObserver(currentConditions); //测试 System.out.println(); System.out.println("通知各个注册的观察者, 看看信息"); weatherData.setData(10f, 100f, 30.3f); /* 通知各个注册的观察者, 看看信息 ***Today mTemperature: 10.0*** ***Today mPressure: 100.0*** ***Today mHumidity: 30.3*** ===百度网站==== ***百度网站 气温 : 10.0*** ***百度网站 气压: 100.0*** ***百度网站 湿度: 30.3*** 通知各个注册的观察者, 看看信息 ===百度网站==== ***百度网站 气温 : 10.0*** ***百度网站 气压: 100.0*** ***百度网站 湿度: 30.3*** */ } } interface Subject { //注册一个观察者 void registerObserver(Observer observer); //删除一个观察者 void removeObserver(Observer observer); //推送所有观察者 void notifyObservers(); } interface Observer { //更新方法 void update(float temperature, float pressure, float humidity); } //气象中心类 class WeatherData implements Subject { private float temperatrue; private float pressure; private float humidity; //观察者集合 private ArrayList<Observer> observers; //加入新的第三方 public WeatherData() { observers = new ArrayList<Observer>(); } public float getTemperature() { return temperatrue; } public float getPressure() { return pressure; } public float getHumidity() { return humidity; } public void dataChange() { //调用 接入方的 update notifyObservers(); } //当数据有更新时,就调用 setData public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; //调用 dataChange, 将最新的信息 推送给 接入方 currentConditions dataChange(); } //注册一个观察者 @Override @Override public void registerObserver(Observer o) { observers.add(o); } //移除一个观察者 @Override @Override public void removeObserver(Observer o) { if (observers.contains(o)) { observers.remove(o); } } //遍历所有的观察者,并通知 @Override @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { observers.get(i).update(this.temperatrue, this.pressure, this.humidity); } } } class BaiduSite implements Observer { // 温度,气压,湿度 private float temperature; private float pressure; private float humidity; // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } // 显 示 public void display() { System.out.println("===百度网站===="); System.out.println("***百度网站 气温 : " + temperature + "***"); System.out.println("***百度网站 气压: " + pressure + "***"); System.out.println("***百度网站 湿度: " + humidity + "***"); } } class CurrentConditions implements Observer { // 温度,气压,湿度 private float temperature; private float pressure; private float humidity; // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } // 显 示 public void display() { System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***"); } }

观察者模式的好处

1)观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。
2)这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类 WeatherData 不会修改代码, 遵守了 ocp 原则。

观察者模式在 Jdk 应用的源码分析

1)Jdk 的 Observable 类就使用了观察者模式
2)代码分析+模式角色分析
3)模式角色分析
Observable 的作用和地位等价于 我们前面讲过 Subject
Observable 是类,不是接口,类中已经实现了核心的方法 ,即管理 Observer 的方法 add… delete … notify…
Observer 的作用和地位等价于我们前面讲过的 Observer, 有 update
Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式

十七 中介者模式

中介者模式基本介绍

基本介绍

1)中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
2)中介者模式属于行为型模式,使代码易于维护
3)比如 MVC 模式,C(Controller 控制器)是 M(Model 模型)和 V(View 视图)的中介者,在前后端交互时起到了中间人的作用

中介者模式的原理类图

设计模式之java-学习笔记_第40张图片

对原理类图的说明-即(中介者模式的角色及职责)
1)Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
2)Colleague 是抽象同事类
3)ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理
HashMap,并接受某个同事对象消息,完成相应的任务
4)ConcreteColleague 具体的同事类,会有很多, 每个同事只知道自己的行为, 而不了解其他同事类的行为(方法), 但 是他们都依赖中介者对象

中介者模式应用实例-智能家庭管理

1)应用实例要求
完成前面的智能家庭的项目,使用中介者模式
2)思路分析和图解(类图)
设计模式之java-学习笔记_第41张图片

3)代码实现

package com.fs.sjms.intermediaryAgent;

import java.util.HashMap;

public class IntermediaryAgentDemo {

    public static void main(String[] args) {
//创建一个中介者对象
        Mediator mediator = new ConcreteMediator();


//创建 Alarm  并且加入到	ConcreteMediator 对象的HashMap
        Alarm alarm = new Alarm(mediator, "alarm");

//创建了 CoffeeMachine 对象,并	且加入到	ConcreteMediator 对象的HashMap
        CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine");

//创建  Curtains , 并	且加入到	ConcreteMediator 对象的HashMap
        Curtains curtains = new Curtains(mediator, "curtains");
        TV tV = new TV(mediator, "TV");

//让闹钟发出消息
        alarm.SendAlarm(0);
        coffeeMachine.FinishCoffee();
        alarm.SendAlarm(1);
    }


}


//同事抽象类
abstract class Colleague {
    private Mediator mediator;
    public String name;

    public Colleague(Mediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;

    }

    public Mediator GetMediator() {
        return this.mediator;
    }

    public abstract void SendMessage(int stateChange);
}

//具体的同事类
class Alarm extends Colleague {
    //构造器
    public Alarm(Mediator mediator, String name) {
        super(mediator, name);
        //在创建 Alarm 同事对象时,将自己放入到 ConcreteMediator 对象中[集合]
        mediator.Register(name, this);
    }

    public void SendAlarm(int stateChange) {
        SendMessage(stateChange);
    }

    @Override
    public void SendMessage(int stateChange) {
        // 调 用 的 中 介 者 对 象 的 getMessage
        this.GetMediator().GetMessage(stateChange, this.name);
    }




}

class CoffeeMachine extends Colleague {

    public CoffeeMachine(Mediator mediator, String name) {
        super(mediator, name);
        mediator.Register(name, this);
    }

    @Override
    public void SendMessage(int stateChange) {
        this.GetMediator().GetMessage(stateChange, this.name);
    }

    public void StartCoffee() {
        System.out.println("It's time to startcoffee!");
    }

    public void FinishCoffee() {
        System.out.println("After 5 minutes!");
        System.out.println("Coffee is ok!");
        SendMessage(0);
    }
}


//具体的中介者类
class ConcreteMediator extends Mediator {
    //集合,放入所有的同事对象
    private HashMap<String, Colleague> colleagueMap;
    private HashMap<String, String> interMap;


    public ConcreteMediator() {
        colleagueMap = new HashMap<String, Colleague>();
        interMap = new HashMap<String, String>();
    }


    @Override
    public void Register(String colleagueName, Colleague colleague) {
        colleagueMap.put(colleagueName, colleague);

        if (colleague instanceof Alarm) {
            interMap.put("Alarm", colleagueName);
        } else if (colleague instanceof CoffeeMachine) {
            interMap.put("CoffeeMachine", colleagueName);
        } else if (colleague instanceof TV) {
            interMap.put("TV", colleagueName);
        } else if (colleague instanceof Curtains) {
            interMap.put("Curtains", colleagueName);
        }

    }

    //具体中介者的核心方法
    //1. 根据得到消息,完成对应任务
    //2. 中介者在这个方法,协调各个具体的同事对象,完成任务
    @Override
    public void GetMessage(int stateChange, String colleagueName) {

        //处理闹钟发出的消息
        if (colleagueMap.get(colleagueName) instanceof Alarm) {
            if (stateChange == 0) {
                ((CoffeeMachine) (colleagueMap.get(interMap
                        .get("CoffeeMachine")))).StartCoffee();
                ((TV) (colleagueMap.get(interMap.get("TV")))).StartTv();
            } else if (stateChange == 1) {
                ((TV) (colleagueMap.get(interMap.get("TV")))).StopTv();
            }

        } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) {
            ((Curtains) (colleagueMap.get(interMap.get("Curtains"))))
                    .UpCurtains();

        } else if (colleagueMap.get(colleagueName) instanceof TV) {
            //如果 TV 发现消息

        } else if (colleagueMap.get(colleagueName) instanceof Curtains) {
            //如果是以窗帘发出的消息,这里处理...
        }
    }

    @Override
    public void SendMessage() {
    }

}


class Curtains extends Colleague {

    public Curtains(Mediator mediator, String name) {
        super(mediator, name);
        mediator.Register(name, this);
    }

    @Override
    public void SendMessage(int stateChange) {
        this.GetMediator().GetMessage(stateChange, this.name);
    }

    public void UpCurtains() {
        System.out.println("I am holding Up Curtains!");
    }

}


abstract class Mediator {
    //将给中介者对象,加入到集合中
    public abstract void Register(String colleagueName, Colleague colleague);

    //接收消息, 具体的同事对象发出
    public abstract void GetMessage(int stateChange, String colleagueName);

    public abstract void SendMessage();
}


class TV extends Colleague {

    public TV(Mediator mediator, String name) {
        super(mediator, name);
        mediator.Register(name, this);
    }

    @Override
    public void SendMessage(int stateChange) {
        this.GetMediator().GetMessage(stateChange, this.name);
    }

    public void StartTv() {
        System.out.println("It's time to StartTv!");
    }

    public void StopTv() {
        System.out.println("StopTv!");
    }
}

中介者模式的注意事项和细节

1)多个类相互耦合,会形成网状结构, 使用中介者模式将网状结构分离为星型结构,进行解耦
2)减少类间依赖,降低了耦合,符合迪米特原则
3)中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
4)如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意

十八 备忘录模式

备忘录模式基本介绍

基本介绍

1)备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态

2)可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
3)备忘录模式属于行为型模式

备忘录模式的原理类图
设计模式之java-学习笔记_第42张图片

对原理类图的说明-即(备忘录模式的角色及职责)
1)originator : 对象(需要保存状态的对象)
2)Memento : 备忘录对象,负责保存好记录,即 Originator 内部状态
3)Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
4)说明:如果希望保存多个 originator 对象的不同时间的状态,也可以,只需要要 HashMap

代码实现

package com.fs.sjms.memorandum;

import java.util.ArrayList;
import java.util.List;

/**
 * 备忘录模式
 */
public class MemorandumDemo {
    public static void main(String[] args) {

        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState(" 状态#1  攻击力 100 ");

//保存了当前的状态
        caretaker.add(originator.saveStateMemento());

        originator.setState(" 状 态 #2 攻 击 力 80 ");
        caretaker.add(originator.saveStateMemento());
        originator.setState(" 状 态 #3 攻 击 力 50 ");
        caretaker.add(originator.saveStateMemento());


        System.out.println("当前的状态是 =" + originator.getState());
//希望得到状态 1, 将 originator 恢复到状态 1
        originator.getStateFromMemento(caretaker.get(0));
        System.out.println("恢复到状态 1 ,  当前的状态是");
        System.out.println("当前的状态是 =" + originator.getState());
    }

    /*
当前的状态是 = 状 态 #3 攻 击 力 50
恢复到状态 1 ,  当前的状态是
当前的状态是 = 状态#1  攻击力 100
     */
}


class Memento {
    private String state;

    //构造器
    public Memento(String state) {
        super();
        this.state = state;
    }

    public String getState() {
        return state;
    }

}

class Originator {
    private String state;

    //状态信息
    public String getState() {
        return state;
    }


    public void setState(String state) {
        this.state = state;
    }

    //编写一个方法,可以保存一个状态对象 Memento
    //因此编写一个方法,返回 Memento
    public Memento saveStateMemento() {
        return new Memento(state);
    }

    //通过备忘录对象,恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

class Caretaker {

    //在 List 集合中会有很多的备忘录对象
    private List<Memento> mementoList = new ArrayList<Memento>();


    public void add(Memento memento) {
        mementoList.add(memento);
    }

    //获取到第 index 个 Originator 的 备忘录对象(即保存状态)
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

备忘录模式的注意事项和细节

1)给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
2)实现了信息的封装,使得用户不需要关心状态的保存细节
3)如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
4)适用的应用场景:1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理

5)为了节约内存,备忘录模式可以和原型模式配合使用

十九 解析器模式

解释器模式基本介绍

基本介绍

  1. 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
  2. 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器, 使用该解释器来解释语言中的句子(表达式)
  3. 应用场景
    -应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
    -一些重复出现的问题可以用一种简单的语言来表达

-一个简单语法需要解释的场景
4) 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等

解释器模式的原理类图

在这里插入图片描述

 对原理类图的说明-即(解释器模式的角色及职责)

  1. Context: 是环境角色,含有解释器之外的全局信息.
  2. AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
  3. TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
  4. NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作.
  5. 说明: 输入 Context he TerminalExpression 信息通过 Client 输入即可

解释器模式来实现四则

  1. 应用实例要求
    通过解释器模式来实现四则运算, 如计算 a+b-c 的值

  2. 思路分析和图解(类图)
    设计模式之java-学习笔记_第43张图片

代码实现

package com.fs.sjms.interpreter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * 使用解释器模式
 */
public class InterpreterDemo {


    public static void main(String[] args) throws IOException {
        String expStr = getExpStr(); // a+b
        HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20}


        Calculator calculator = new Calculator(expStr);
        System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
    }

    // 获得表达式
    public static String getExpStr() throws IOException {
        System.out.print("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

    // 获得值映射
    public static HashMap<String, Integer> getValue(String expStr) throws IOException {
        HashMap<String, Integer> map = new HashMap<>();

        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.print("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }


        return map;
    }


}

/*
抽象类表达式,通过Map键值对,可以获取到变量的值
 */
abstract class Expression {
    //解释公式和数值,key就是公式表达式 参数[a,b,c]  value就是具体的值
    public abstract int interpreter(Map<String, Integer> map);
}


/*
变量的容器
 */
class VerExpression extends Expression {
    private String key;

    public VerExpression(String key) {
        this.key = key;
    }

    //会拿到的map  会更具变量的名称返回对应的值
    @Override
    public int interpreter(Map<String, Integer> map) {
        return map.get(this.key);
    }
}


/**
 * 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系,
 * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是 Expression 类的实现类
 */
class SymbolExpression extends Expression {
    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    //因为 SymbolExpression  是让其子类来实现,因此 interpreter 是一个默认实现
    @Override
    public int interpreter(Map<String, Integer> map) {
        return 0;
    }
}


/**
 * 加法解释器
 */
class AddExpression extends SymbolExpression {


    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    //处理相加
    //var 仍然是 {a=10,b=20}..
    //一会我们 debug  源码,就 ok
    public int interpreter(Map<String, Integer> var) {
        //super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10
        //super.right.interpreter(var): 返回 right 表达式对应值 b = 20
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}


class Calculator {

    // 定义表达式
    private Expression expression;

// 构造函数传参,并解析


    public Calculator(String expStr) { // expStr = a+b
        // 安排运算先后顺序
        Stack<Expression> stack = new Stack<>();
        // 表达式拆分成字符数组
        char[] charArray = expStr.toCharArray();// [a, +, b]


        Expression left = null;
        Expression right = null;
        //遍历我们的字符数组,  即遍历	[a, +, b]
        //针对不同的情况,做处理
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+': //
                    left = stack.pop();// 从 stack 取 出 left => "a"
                    right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
                    stack.push(new AddExpression(left, right));// 然后根据得到 left 和 right 构建 AddExpresson 加入stack
                    break;
                case '-': //
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default:
                    //如果是一个 Var 就创建要给 VarExpression 对象,并 push 到 stack
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
            }
        }
        //当遍历完整个 charArray  数组后,stack 就得到最后 Expression
        this.expression = stack.pop();
    }


    public int run(Map<String, Integer> var) {
        //最后将表达式 a+b 和 var = {a=10,b=20}
        //然后传递给 expression 的 interpreter 进行解释执行
        return this.expression.interpreter(var);
    }
}


/**
 * 变量的解释器
 */
class VarExpression extends Expression {


    private String key; // key=a,key=b,key=c


    public VarExpression(String key) {
        this.key = key;
    }

    // var 就是{a=10, b=20}
    // interpreter 根据 变量名称,返回对应值
    @Override
    public int interpreter(Map<String, Integer> var) {
        return var.get(this.key);
    }
}


class SubExpression extends SymbolExpression {


    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    //求出 left 和 right  表达式相减后的结果
    public int interpreter(Map<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}

解释器模式的注意事项和细节

  1. 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  2. 应用场景:编译器、运算表达式计算、正则表达式、机器人等
  3. 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.

APP 抽奖活动问题

请编写程序完成 APP 抽奖活动 具体要求如下:

  1. 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是 10%
  2. 奖品数量固定,抽完就不能抽奖
  3. 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
  4. 活动的四个状态转换关系图(右图)
    设计模式之java-学习笔记_第44张图片

状态模式基本介绍

基本介绍

  1. 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
  2. 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类

 对原理类图的说明-即(状态模式的角色及职责)

  1. Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
  2. State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
  3. ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为
    24.4 状态模式解决 APP 抽奖问
  4. 应用实例要求
    完成 APP 抽奖活动项目,使用状态模式.
  5. 思路分析和图解(类图)
    设计模式之java-学习笔记_第45张图片

-定义出一个接口叫状态接口,每个状态都实现它。
-接口有扣除积分方法、抽奖方法、发放奖品方法

  1. 代码实现
package com.fs.sjms.state;

import java.util.Random;

public class StateDemo {
    public static void main(String[] args) {
// 创建活动对象,奖品有 1 个奖品
        RaffleActivity activity = new RaffleActivity(1);

// 我们连续抽 300 次奖
        for (int i = 0; i < 30; i++) {
            System.out.println("--------第" + (i + 1) + "次抽奖----------");
// 参加抽奖,第一步点击扣除积分
            activity.debuctMoney();

// 第二步抽奖
            activity.raffle();
        }
    }
}


/**
 * 可以抽奖的状态
 *
 * @author Administrator
 */
class CanRaffleState extends State {

    RaffleActivity activity;

    public CanRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    //已经扣除了积分,不能再扣 @Override
    public void deductMoney() {
        System.out.println("已经扣取过了积分");
    }


    //可以抽奖, 抽完奖后,根据实际情况,改成新的状态
    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等!");
        Random r = new Random();
        int num = r.nextInt(10);
// 10%中奖机会
        if (num == 0) {
// 改 变 活 动 状 态 为 发 放 奖 品  context
            activity.setState(activity.getDispenseState());
            return true;
        } else {
            System.out.println("很遗憾没有抽中奖品!");
// 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
            return false;
        }

    }

    // 不能发放奖品
    @Override
    public void dispensePrize() {
        System.out.println("没中奖,不能发放奖品");
    }
}


/**
 * 奖品发放完毕状态
 * 说明,当我们 activity 改变成 DispenseOutState, 抽奖活动结束
 *
 * @author Administrator
 */
class DispenseOutState extends State {

    // 初始化时传入活动引用
    RaffleActivity activity;


    public DispenseOutState(RaffleActivity activity) {
        this.activity = activity;
    }

    @Override
    public void deductMoney() {
        System.out.println("奖品发送完了,请下次再参加");
    }


    @Override
    public boolean raffle() {
        System.out.println("奖品发送完了,请下次再参加");
        return false;


    }


    @Override
    public void dispensePrize() {
        System.out.println("奖品发送完了,请下次再参加");
    }
}


/**
 * 发放奖品的状态
 *
 * @author Administrator
 */
class DispenseState extends State {

    // 初始化时传入活动引用,发放奖品后改变其状态
    RaffleActivity activity;


    public DispenseState(RaffleActivity activity) {
        this.activity = activity;
    }


    @Override
    public void deductMoney() {
        System.out.println("不能扣除积分");
    }


    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }

    //发放奖品 @Override
    public void dispensePrize() {
        if (activity.getCount() > 0) {
            System.out.println("恭喜中奖了");
// 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
        } else {
            System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
            activity.setState(activity.getDispensOutState());
//System.out.println("抽奖活动结束");
//System.exit(0);
        }


    }
}


/**
 * 不能抽奖状态
 *
 * @author Administrator
 */
class NoRaffleState extends State {

    // 初始化时传入活动引用,扣除积分后改变其状态
    RaffleActivity activity;


    public NoRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
    @Override
    public void deductMoney() {
        System.out.println("扣除 50 积分成功,您可以抽奖了");
        activity.setState(activity.getCanRaffleState());
    }


    // 当前状态不能抽奖
    @Override
    public boolean raffle() {
        System.out.println("扣了积分才能抽奖喔!");
        return false;
    }

    // 当前状态不能发奖品
    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}


/**
 * 抽奖活动 //
 *
 * @author Administrator
 */
class RaffleActivity {

    // state 表示活动当前的状态,是变化
    State state = null;


    // 奖品数量
    int count = 0;

    // 四个属性,表示四种状态
    State noRafflleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);

    State dispenseState = new DispenseState(this);
    State dispensOutState = new DispenseOutState(this);

    //构造器
//1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
//2. 初始化奖品的数量
    public RaffleActivity(int count) {
        this.state = getNoRafflleState();
        this.count = count;
    }

    //扣分, 调用当前状态的 deductMoney
    public void debuctMoney() {
        state.deductMoney();
    }

    //抽奖
    public void raffle() {
// 如果当前的状态是抽奖成功


        if (state.raffle()) {
//领取奖品
            state.dispensePrize();
        }


    }


    public State getState() {
        return state;
    }


    public void setState(State state) {
        this.state = state;
    }

    //这里请大家注意,每领取一次奖品,count--
    public int getCount() {
        int curCount = count;
        count--;
        return curCount;
    }


    public void setCount(int count) {
        this.count = count;
    }


    public State getNoRafflleState() {
        return noRafflleState;
    }


    public void setNoRafflleState(State noRafflleState) {
        this.noRafflleState = noRafflleState;
    }


    public State getCanRaffleState() {
        return canRaffleState;
    }


    public void setCanRaffleState(State canRaffleState) {
        this.canRaffleState = canRaffleState;
    }


    public State getDispenseState() {
        return dispenseState;
    }


    public void setDispenseState(State dispenseState) {
        this.dispenseState = dispenseState;
    }

    public State getDispensOutState() {
        return dispensOutState;


    }


    public void setDispensOutState(State dispensOutState) {
        this.dispensOutState = dispensOutState;
    }
}


/**
 * 状态抽象类
 *
 * @author Administrator
 */
abstract class State {


    // 扣除积分 - 50
    public abstract void deductMoney();

    // 是否抽中奖品
    public abstract boolean raffle();

    // 发放奖品
    public abstract void dispensePrize();
}

状态模式的注意事项和细节

  1. 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  2. 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
  3. 符合“开闭原则”。容易增删状态
  4. 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
  5. 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式

二十 策略者模式

编写鸭子项目,具体要求如下:

  1. 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
  2. 显示鸭子的信息
    25.4 策略模式基本介绍
  3. 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  4. 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。

策略模式的原理类图

设计模式之java-学习笔记_第46张图片

说明:从上图可以看到,客户 context 有成员变量 strategy 或者其他的策略接口
,至于需要使用到哪个策略,我们可以在构造器中指定

策略模式解决鸭子问题

  1. 应用实例要求
    编写程序完成前面的鸭子项目,要求使用策略模式
  2. 思路分析(类图)
    策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
    设计模式之java-学习笔记_第47张图片

代码实现

package com.fs.sjms.strategy;

public class StrategyDemo {
    public static void main(String[] args) {
        //测试策略模式
        System.out.println("北京鸭");
        PekingDuck pekingDuck = new PekingDuck();
        pekingDuck.disPlay();
        System.out.println("野鸭");
        WildDuck wildDuck = new WildDuck();
        wildDuck.disPlay();
        System.out.println("玩具鸭");
        ToyDuck toyDuck = new ToyDuck();
        toyDuck.disPlay();

        /*
飞行一般鸭鸭
GAGA叫
野鸭
飞行冠军鸭鸭
GEGE叫
玩具鸭
不会飞行鸭鸭
既然不叫
         */
    }
}


//飞的策略接口
interface FlyBehavior{
    void fly();
}

//叫的策略接口
interface QuackBehavior{
    void quack();
}

//不会飞的具体实现类
class NoFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("不会飞行鸭鸭");
    }
}

//会飞的具体实现类
class GoodFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("飞行冠军鸭鸭");
    }
}

//飞的具体实现类
class BeadFlyBehavior implements FlyBehavior{
    @Override
    public void fly() {
        System.out.println("飞行一般鸭鸭");
    }
}

//gege叫的策略实现类
class GeGeQuackBehavior implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("GEGE叫");
    }
}

//GaGa叫的策略实现类
class GaGaQuackBehavior implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("GAGA叫");
    }
}

//不会叫的策略实现类
class NoQuackBehavior implements QuackBehavior{
    @Override
    public void quack() {
        System.out.println("既然不叫");
    }
}

//鸭子类
abstract class Duck{
    //飞的属性
    public FlyBehavior flyBehavior;
    //叫的属性
    public QuackBehavior quackBehavior;

    public abstract void disPlay();

    public void fly(){
        if (flyBehavior!=null){
            this.flyBehavior.fly();
        }else {
            System.out.println("默认fly");
        }
    }

    public void quack(){
        if (quackBehavior!=null){
            this.quackBehavior.quack();
        }else {
            System.out.println("默认quack");
        }
    }
}

//北京鸭
class PekingDuck extends Duck{

    @Override
    public void disPlay() {
        //北京鸭飞行一般
        super.flyBehavior = new BeadFlyBehavior();
        //北京呀gege叫
        super.quackBehavior = new GaGaQuackBehavior();
        fly();
        quack();
    }
}

//野鸭
class WildDuck extends Duck{
    @Override
    public void disPlay() {
        //北京鸭飞行一般
        super.flyBehavior = new GoodFlyBehavior();
        //北京呀gege叫
        super.quackBehavior = new GeGeQuackBehavior();
        fly();
        quack();
    }

}

//玩具鸭
class ToyDuck extends Duck{
    @Override
    public void disPlay() {
        //玩具鸭
        super.flyBehavior = new NoFlyBehavior();
        //玩具鸭
        super.quackBehavior = new NoQuackBehavior();
        fly();
        quack();
    }

}

策略模式在 JDK-Arrays 应用的源码分析

  1. JDK 的 Arrays 的 Comparator 就使用了策略模式
    设计模式之java-学习笔记_第48张图片

策略模式的注意事项和细节

  1. 策略模式的关键是:分析项目中变化部分与不变部分
  2. 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
  3. 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
  4. 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
  5. 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞

二十一 职责链模式 又叫 责任链模式

学校 OA 系统的采购审批项目:需求是

采购员采购教学器材

  1. 如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)
  2. 如果金额 小于等于 10000, 由院长审批 (5000
  3. 如果金额 小于等于 30000, 由副校长审批 (10000
  4. 如果金额 超过 30000 以上,有校长审批 ( 30000 请设计程序完成采购审批项目
    26.2 传统方案解决 OA 系统审批,传统的设计方案(类图)
    设计模式之java-学习笔记_第49张图片

26.3 传统方案解决 OA 系统审批问题分析

  1. 传统方式是:接收到一个采购请求后,根据采购金额来调用对应的 Approver (审批人)完成审批。

  2. 传统方式的问题分析 : 客户端这里会使用到 分支判断(比如 switch) 来对不同的采购请求处理, 这样就存在如下问题 (1) 如果各个级别的人员审批金额发生变化,在客户端的也需要变化 (2) 客户端必须明确的知道 有多少个审批级别和访问

  3. 这样 对一个采购请求进行处理 和 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护

  4. 解决方案 =》 职责链模式

职责链模式基本介绍

基本介绍

  1. 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。

  2. 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

  3. 这种类型的设计模式属于行为型模式

    职责链模式的原理类图
    设计模式之java-学习笔记_第50张图片

 对原理类图的说明-即(职责链模式的角色及职责)

  1. Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
  2. ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
  3. Request , 含义很多属性,表示一个请求

职责链模式解决 OA 系统采购审批

  1. 应用实例要求
    编写程序完成学校 OA 系统的采购审批项目:需求采购员采购教学器材
    如果金额 小于等于 5000, 由教学主任审批如果金额 小于等于 10000, 由院长审批
    如果金额 小于等于 30000, 由副校长审批如果金额 超过 30000 以上,有校长审批
  2. 思路分析和图解(类图)
    设计模式之java-学习笔记_第51张图片

代码实现

package com.fs.sjms.responsibility;

/*
责任链模式
 */
public class ResponsibilityDemo {
    public static void main(String[] args) {
        PurchaseRequest purchaseRequest = new PurchaseRequest(1, 1000, 1);
        DepartmentApprove departmentApprove = new DepartmentApprove("张主任");
        CollegeApprove collegeApprove = new CollegeApprove("李院长");
        ViceSchoolApprove viceSchoolApprove = new ViceSchoolApprove("王副校");
        MasterSchoolApprove masterSchoolApprove = new MasterSchoolApprove("付校长");

        //将各个审批的下一个处理者设置好
        departmentApprove.setApprove(collegeApprove);
        collegeApprove.setApprove(viceSchoolApprove);
        viceSchoolApprove.setApprove(masterSchoolApprove);
        //形成环状
        masterSchoolApprove.setApprove(departmentApprove);

        //直接让副校长处理,也会打到张主任那里去,这就是责任链模式,责任分明
        viceSchoolApprove.processRequest(purchaseRequest);
    }
}


//请求类
class PurchaseRequest{
    private int type = 0;//请求类型
    private float price = 0F;//请求金额
    private int id = 0;

    //满参构造
    public PurchaseRequest(int type, float price, int id) {
        this.type = type;
        this.price = price;
        this.id = id;
    }

    public int getType() {
        return type;
    }

    public float getPrice() {
        return price;
    }


    public int getId() {
        return id;
    }
}

abstract class Approve{
    Approve approve;//下一个处理者
    String name;//名字

    public Approve(String name) {
        this.name = name;
    }

    //下一个处理者
    public void setApprove(Approve approve) {
        this.approve = approve;
    }

    //处理审批请求的方法,得到一个请求,处理是子类完成的,因此该方法做成抽象
    public abstract void  processRequest(PurchaseRequest purchaseRequest);

}

//部门处理请求
class DepartmentApprove extends Approve{

    public DepartmentApprove(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice()<=5000){
            System.out.println("请求编号 id = "+purchaseRequest.getId()+"  被 "+this.name+" 处理了");
        }else {
            //处理不了,让下一个处理着来请求
            approve.processRequest(purchaseRequest);
        }
    }
}

//学院处理请求
class CollegeApprove extends Approve{

    public CollegeApprove(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice()>=5000 && purchaseRequest.getPrice()<=10000){
            System.out.println("请求编号 id = "+purchaseRequest.getId()+"  被 "+this.name+" 处理了");
        }else {
            //处理不了,让下一个处理着来请求
            approve.processRequest(purchaseRequest);
        }
    }
}


//副校长处理请求
class ViceSchoolApprove extends Approve{

    public ViceSchoolApprove(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice()>=10000 && purchaseRequest.getPrice()<=30000){
            System.out.println("请求编号 id = "+purchaseRequest.getId()+"  被 "+this.name+" 处理了");
        }else {
            //处理不了,让下一个处理着来请求
            approve.processRequest(purchaseRequest);
        }
    }
}



//校长处理请求
class MasterSchoolApprove extends Approve{

    public MasterSchoolApprove(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
        if (purchaseRequest.getPrice()>30000){
            System.out.println("请求编号 id = "+purchaseRequest.getId()+"  被 "+this.name+" 处理了");
        }else {
            //处理不了,让下一个处理着来请求
            approve.processRequest(purchaseRequest);
        }
    }
}

职责链模式在 SpringMVC 框架应用的源码分析

  1. SpringMVC-HandlerExecutionChain 类就使用到职责链模式

  2. SpringMVC 请求流程简图

  3. 代码分析+Debug 源码+说明
    设计模式之java-学习笔记_第52张图片

  4. 对源码总结
     springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
     在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
     HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
     HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.

职责链模式的注意事项和细节

  1. 将请求和处理分开,实现解耦,提高系统的灵活性

  2. 简化了对象,使对象不需要知道链的结构

  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能

  4. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂

  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat
    对 Encoding 的处理、拦截器

你可能感兴趣的:(java,设计模式,java)