设计模式

设计模式

设计模式层次结构

  1. 第一层:刚接触编程不久,没有听说过什么设计模式
  2. 第二层:有很长时间编程经验,自己写了很多代码,其中用到了设计模式,但是自己不知道
  3. 第三层:学习过设计模式,发现自己已经在使用了,并发现一些新的模式挺好用
  4. 第四层:阅读了很多别人写的源码和框架,其中看到别人设计模式,并且能领会设计模式的精妙和使用的好处
  5. 第五层:代码写着写着,习惯性的就是用了设计模式

设计模式主要目的
设计模式目的:提高软件维护,降低软件复杂度

创见型模式

单例模式

饿汉式(静态常量)

  1. 私有化构造方法防止new
  2. 类的内部创建对象,类的加载器创建,不会出现线程安全问题
  3. 向外暴露public方法来获取该对象

代码如下:

/**
 * 饿汉式静态常量方式
  * @author HiWin10
 *  @date 2020/8/5
 * 单例设计模式 第一种
  *  1. 私有化构造方法
  *  2. static final 修饰类属性
  *  3. getInstance()方法获取
  */
class Singleton {
    private static final Singleton singleton = new Singleton();   private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
  }
}

总结
优点:写法简单,类加载器加载时就完成了实例化,避免了线程安全。

缺点:在类加载时就实例化了对象,没有懒加载效果,也就是说如果从来没有用过这个实例,就会造成内存浪费

结论:这种单例模式可用,但是会造成内存浪费

饿汉式(静态代码块)

/**
 * 饿汉式(静态代码快方式),线程安全
 * @author HiWin10
 * @date 2020/8/5
 */
class Singleton {
    private Singleton(){}

    private static Singleton singleton;   static {
        singleton = new Singleton();
  }

    public static Singleton getInstance() {
        return singleton;
  }
}

总结
这个实现方式和上面的效一样,只要加载了这个类,没有使用就会造成内存浪费

懒汉式(线程不安全)

/**
 * 懒汉式,线程不安全
  * @author HiWin10
 * @date 2020/8/5 
 */ 
 public class SingletonTest03 {
    public static void main(String[] args) {
        // 演示线程不安全
  Runnable runnable = new Runnable() {
            @Override
  public void run() {
                System.out.println(Thread.currentThread().getName() + "   " + Singleton.getInstance().hashCode());
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(runnable).start();
        }
    }
}

class Singleton {
    private Singleton() {}
    private static Singleton singleton;

    public static Singleton getInstance(){
        if (singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}
/**
 * 输出结果
Thread-0   1416179411
Thread-2   1413474096
Thread-3   1413474096
Thread-1   1413474096
Thread-7   1413474096
Thread-8   1413474096
**/

总结

  1. 起到了懒加载效果,但是只能在单线程下使用
  2. 如果多线程下,很有可能一个线程到if (singleton == null)里面挂起了,然后下一个线程也刚好进去,线程不安全
  3. 实际开发中,不使用这种方式

懒汉式(线程安全,同步方法)

/**
 * @author HiWin10 * @date 2020/8/5 */ 
 public class SingletonTest04 {
    public static void main(String[] args) {
        // 演示线程不安全
  Runnable runnable = new Runnable() {
            @Override
  public void run() {
                System.out.println(Thread.currentThread().getName() + "\t" + Singleton.getInstance().hashCode());
            }
        };
        for (int i = 0; i < 100; i++) {
            new Thread(runnable).start();
        }
    }
}

class Singleton {
    private Singleton() {}
    private static Singleton singleton;

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

总结

  1. 解决了线程安全问题。
  2. 效率太低,每次方法有线程进去的时候就会上锁,其他线程只能等待
  3. 实际开发中,不推荐使用

懒汉式(线程不安全,同步代码块)

public class SingletonTest05 {
    public static void main(String[] args) {
        // 演示线程不安全
  Runnable runnable = new Runnable() {
            @Override
  public void run() {
                System.out.println(Thread.currentThread().getName() + "\t" + Singleton.getInstance().hashCode());
            }
        };
        for (int i = 0; i < 10000; i++) {
            new Thread(runnable).start();
        }
    }
}

class Singleton {
    private Singleton() {}
    private static Singleton singleton;

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}
/**
 * 输出结果
Thread-1	2096868475
Thread-26	1272488711
Thread-27	1272488711
**/

双重检测(线程安全,同步代码块)

/**
 * @author HiWin10 * @date 2020/8/5 * 双重检测,线程安全,同步代码块
  */ 
public class SingletonTest06 {
    public static void main(String[] args) {
        // 演示线程不安全
  Runnable runnable = new Runnable() {
            @Override
  public void run() {
                System.out.println(Thread.currentThread().getName() + "\t" + Singleton.getInstance().hashCode());
            }
        };
        for (int i = 0; i < 10000; i++) {
            new Thread(runnable).start();
        }
    }
}
class Singleton {
    private Singleton() {}
    private static volatile Singleton singleton;

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

总结

  1. 进行了两次判断,线程安全了
  2. 有两次if判断,直接return实例化对象,避免反复进行方法同步
  3. 线程安全,延迟加载,效率高?
  4. 结论:实际开发中,推荐使用这样方式

静态内部类(线程安全,效率高)

class Singleton {
    private Singleton() {}

    private static class SingletonInstance {
        private static final Singleton SINGLETON = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.SINGLETON;
    }
}

总结

  1. 静态内部类方式,借用类加载机制,线程肯定安全
  2. 实现了懒加载机制,如果不用Singleton.getInstance()方法,此类肯定不会创建。
  3. 效率高,
  4. 推荐使用

枚举

enum Singleton {
    INSTANCE; //属性
  public void sayOk() {
        System.out.println("OK");
  }
}

总结

  1. 不仅可以避免多线程问题,还可以避免反序列化
  2. 推荐使用

总结

  1. 单例模式主要是保证一个类在该程序中只能有一个对象实例。
  2. 一定要通过方法来获取该对象,避免new
  3. 单例模式使用场景:Windows的任务管理器就是很经典的单例模式;在Servlet编程中,每个Servlet也是单例的;在Spring中,每个Bean默认就是单例的,这样做有点是Spring容器可以管理

工厂模式

概念

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

工厂模式的分类

  1. 简单工厂模式

    • 用来生产同一等级结构中任意产品,(对于新产品增加只能修改源代码)
  2. 工厂方法模式

    • 用来生产同一等级结构中的固定产品(支持增加任意产品,但是源文件会相对应增加)
  3. 抽象工厂模式

    • 用来生辰不同产品族的全部产品。(对于增加新的产品,无能为力,支持增加产品族)

简单工厂

通过静态方法中要传递的参数来确定要创建的是哪个类

/**
 * 利用简单工厂模式来生成对象
 * @author HiWin10
 * @date 2020/8/6
 */ 
 public class RunTest01 {
    public static void main(String[] args) {
        // 1. 创建工厂,利用工厂创建对象
  Animal cat = SimpFactory.getAnimal("猫");
  cat.eat();    Animal dog = SimpFactory.getAnimal("狗");
  dog.eat();    Animal test = SimpFactory.getAnimal("test");
  test.eat();    }
}

简单工厂类

/**
 * 简单工厂模式以创建动物为例子
  * @author HiWin10
 * @date 2020/8/6
 */ 
 public class SimpFactory {
    public static Animal getAnimal(String type){
        if (type.equals("狗")) {
            return new Dog();
  } else if (type.equals("猫")) {
            return new Cat();
  } else {
            throw new RuntimeException( type +  "没有此类");
  }
    }
}

动物类

/**
 * 动物类,
  * 有一个方法,eat吃东西类
  * @author HiWin10
 * @date 2020/8/6
 */ 
 public interface Animal {
    public void eat(); 
 }

/**
 * @author HiWin10
 * @date 2020/8/6
 */ 
 public class Cat implements Animal {
    @Override
  public void eat() {
        System.out.println("小猫吃东西");
  }
}

/**
 * 狗类
  * @author HiWin10
 * @date 2020/8/6
 */ 
 public class Dog implements Animal {
    @Override
  public void eat() {
        System.out.println("小狗吃东西");
  }
}

方法工厂

简单工厂的升级版,每增加一个功能,就会实现一个新功能创建的工厂

代码示例

工厂方法有两种类

  1. 动物接口类 和 对应动物实现类
  2. 创建动物的工厂和 对应创建动物的实现工厂

动物接口类

public interface Animal {
    // 所有动物的吃方法
    public void eat();
}

动物实现方法-狗类

public class DogImpl implements Animal {
    @Override
    public void eat() {
        System.out.println("狗狗在吃饭~!");
    }
}

动物实现方法-老虎类

public class TigerImpl implements Animal {
    @Override
    public void eat() {
        System.out.println("老虎在吃肉~!");
    }
}

工厂方法模式

总工厂

public interface AnimalFactory {
    // 创建具体对象方法
    public Animal create();
}

对应功能工厂-狗类

// 实现动物工厂类接口
public class DogFactory implements AnimalFactory {
    @Override
    public Animal create() {
        return new DogImpl();
    }
}

对应功能工厂-老虎类

public class TigerFactory implements AnimalFactory {
    @Override
    public Animal create() {
        return new TigerImpl();
    }
}

测试代码

public class Dome1 {
    public static void main(String[] args) {
        Animal dog = new DogFactory().create();
        dog.eat();

        Animal tiger = new TigerFactory().create();
        tiger.eat();
    }
}

抽象工厂

  1. 用来生成不同产品族的全部产品(对于增加新的单个产品无能为力;支持增加产品族)

简单的说:每个产品都有高端和低端产品,然后抽象工厂模式作用就是创建所以高端产品 或 创建所有低端产品

类关系图

代码示例

各个产品

发动机产品

/**
 * 发动机
 */
public interface Engine {
    public void run();
    public void start();
}

高端发动机

/**
 * 高端发动机
 */
public class HighEngineImpl implements Engine {
    @Override
    public void run() {
        System.out.println("转的快~!");
    }

    @Override
    public void start() {
        System.out.println("启动快!可以自动启停~!");
    }
}

低端发动机

/**
 * 低端发动机
 */
public class LowEngineImpl implements Engine {
    @Override
    public void run() {
        System.out.println("转的慢!");
    }

    @Override
    public void start() {
        System.out.println("启动慢!还不能自动的启停!");
    }
}

座椅

/**
 * 座椅
 */
public interface Seat {
    public void massage();
}

高端座椅

/**
 * 高端座椅
 */
public class HighSeatImpl implements Seat {
    @Override
    public void massage() {
        System.out.println("高端产品->可以自动按摩~!");
    }
}

低端座椅

/**
 * 低端座椅
 */
public class LowSeatImpl implements Seat {
    @Override
    public void massage() {
        System.out.println("普通座椅!");
    }
}

轮子

/**
 * 轮子
 */
public interface Type {
    public void revolve();
}

高端轮子

/**
 * 高端轮子
 */
public class HighType implements Type {
    @Override
    public void revolve() {
        System.out.println("高端产品->轮子旋转不容易坏~!");
    }
}

低端轮子

/**
 * 低端轮子
 */
public class LowType implements Type {
    @Override
    public void revolve() {
        System.out.println("普通轮子!");
    }
}

2.4、总结

简单工厂模式(静态工厂模式)

  • 虽然某种程度不符合设计原则,但实际使用最多

工厂方法模式

  • 不修改已有类的前提下,通过增加新的工厂类实现扩展

抽象工厂模式

  • 不可以增加产品,可以增加产品族

原型模式

简单的说就是C++中的拷贝构造函数,不过要主要深拷贝还是浅拷贝

实现拷贝两个步骤

  1. 实现Cloneable接口

  2. 重写Object的clone()方法

浅拷贝

  1. 实现Cloneable接口
  2. 重写clone()方法
/**
 * 人的类
 * @author HiWin10
 * @date 2020/8/8
 */
@Data
public class Person implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private Date birthday;

    public Person(String name, String sex, int age, Date birthday) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthday = birthday;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person copy = (Person) super.clone();
        return copy;
    }
}

/**
 * 类的浅拷贝
 * @author HiWin10
 * @date 2020/8/8
 */
public class SimpCopy {
    public static void main(String[] args) throws Exception {
        Person person1 = new Person("小红", "女", 14, new Date());
        Person person2 = person1.clone();
        System.out.println(person1);
        System.out.println(person2);
    }
}

深拷贝

深拷贝的话有三种方式:

  1. 序列化完成
  2. clone()方法中针对的给引用类型使用clone()方法
  3. 将对象转化为json字符串然后再转换回来。

针对性的给引用类型设置clone方法

public class SimpCopy {
    public static void main(String[] args) throws Exception {
        Person person1 = new Person("小红", "女", 14, new Date());
        Person person2 = person1.clone();

        // 先来修改一个时间
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = dateFormat.parse("2000-01-01");
        System.out.println("----------修改时间后----");
        person1.setBirthday(parse);
        System.out.println(person1);
        System.out.println(person2);
    }
}
@Data
public class Person implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private Date birthday;

    public Person(String name, String sex, int age, Date birthday) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthday = birthday;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person copy = (Person) super.clone();
        // 深拷贝
        Date copyBirthday = (Date) this.birthday.clone();
        copy.setBirthday(copyBirthday);
        return copy;
    }
}

序列化完成深拷贝

/**
 * 人的类
 * @author HiWin10
 * @date 2020/8/8
 */
@Data
public class Person implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private Date birthday;

    @Override
    public Person clone() throws CloneNotSupportedException {
        Person copy = (Person) super.clone();
        // 深拷贝
        Date copyBirthday = (Date) this.birthday.clone();
        copy.setBirthday(copyBirthday);
        return copy;
    }

    public Person deepCopy() {
        // 输出流
        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);

            Person deepCopy = (Person) ois.readObject();

            return deepCopy;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

使用json转换工具完成深拷贝

建造者模式

就是说一个复杂的对象,需要其他对象的配合才能创建,这个时候就需要建造者模式

概念

  1. 建造者模式是一种对象构建模式。他可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同对象
  2. 建造者对象是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建他们,用户可以不需要指定内部细节

四个角色

Product:(产品角色)一个具体产品角色

Builder:(抽象建造者)创建一个Product对象各个部件指定 接口/抽象类

ConcreateBuilder:(具体建造者)实现接口,构建和装配各个组件

Director:(指挥者)构建一个Builder接口对象。创建一个复杂对象。 主要两个作用:1)隔离了客户与对象的生产过程;2)负责控制产品对象的生成过程

具体案例

创建一个飞船需要很多零件,然后进行组件,建造者完成各个零件创建,指挥者来只会零件如何组装

AirshipBuilder:建造者抽象类,完成各个产品的创建

AirshipDirector:指挥者,完成产品的装,并生成最终产品Airship

/**
 * 飞机类,建造者的结果类
 * @author HiWin10
 * @date 2020/8/22
 */
public class Airship {
    private Engine engine;  //发动机
    private EscapeTower escapeTower;  // 逃逸塔
    private OrbitalModule orbitalModule; // 轨道舱

    public Airship(Engine engine, EscapeTower escapeTower, OrbitalModule orbitalModule) {
        this.engine = engine;
        this.escapeTower = escapeTower;
        this.orbitalModule = orbitalModule;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public EscapeTower getEscapeTower() {
        return escapeTower;
    }

    public void setEscapeTower(EscapeTower escapeTower) {
        this.escapeTower = escapeTower;
    }

    public OrbitalModule getOrbitalModule() {
        return orbitalModule;
    }

    public void setOrbitalModule(OrbitalModule orbitalModule) {
        this.orbitalModule = orbitalModule;
    }
}

/**
 * 发动机 产品类
 */
class Engine {
    private String name;

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

    public String getName() {
        return name;
    }

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

/**
 * 逃逸塔-产品类
 */
class EscapeTower {
    private String name;

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

    public String getName() {
        return name;
    }

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

/**
 * 轨道舱-产品类
 */
class OrbitalModule {
    private String name;

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

    public String getName() {
        return name;
    }

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

建造者

/**
 * 建造者接口
 * @author HiWin10
 * @date 2020/8/22
 */
public interface AirshipBuilder {
    Engine builderEngine();
    OrbitalModule builderOrbitalModule();
    EscapeTower builderEscapeTower();
}


/**
 * 建造者实现,尚学堂宇宙飞船建造者
 * 构建各种产品-由指挥者来进行建造
 * @author HiWin10
 * @date 2020/8/22
 */
public class SxtAirshipBuilder implements AirshipBuilder {

    @Override
    public Engine builderEngine() {
        System.out.println("构建了发动机~!");
        return new Engine("尚学堂发动机");
    }

    @Override
    public OrbitalModule builderOrbitalModule() {
        System.out.println("构建了轨道舱~!");
        return new OrbitalModule("尚学堂轨道舱");
    }

    @Override
    public EscapeTower builderEscapeTower() {
        System.out.println("构建了逃逸塔~!");
        return new EscapeTower("尚学堂逃逸塔");
    }
}

指挥者

/**
 * 指挥者接口
 * @author HiWin10
 * @date 2020/8/22
 */
public interface AirshipDirector {
    /**
     * 组装方法-创建最终产品-飞船
     * @return
     */
    public Airship createAirship();
}


/**
 * 具体指挥者
 * @author HiWin10
 * @date 2020/8/22
 */
public class SxtAirshipDirector implements AirshipDirector {
    /**
     * 需要一个建造者引用
     */
    private AirshipBuilder airshipBuilder;

    public SxtAirshipDirector(AirshipBuilder airshipBuilder) {
        this.airshipBuilder = airshipBuilder;
    }

    @Override
    public Airship createAirship() {
        Engine e = this.airshipBuilder.builderEngine();
        EscapeTower escapeTower = this.airshipBuilder.builderEscapeTower();
        OrbitalModule orbitalModule = this.airshipBuilder.builderOrbitalModule();
        Airship airship = new Airship(e, escapeTower, orbitalModule);
        return airship;
    }
}
/**
 * @author HiWin10
 * @date 2020/8/22
 */
public class Client {
    public static void main(String[] args) {
        AirshipDirector director = new SxtAirshipDirector(new SxtAirshipBuilder());
        Airship airship = director.createAirship();
        System.out.println(airship);
    }
}

结构型模式

适配器模式

显示中的例子

充电插口转换器。

type-c转usb

适配器模式介绍

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

具体原理

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

类适配器模式

场景说明

生活中手机要充电,正常来说电流标准电压是220V,为了让手机正常充电,需要转换为5V电流

代码示例

标准电压

/**
 * 被适配者,电源220V
 * @author HiWin10
 * @date 2020/8/18
 */
public class Voltage220V {
    public int output220V() {
        int src = 220;
        System.out.println("电压:" + src + "V");
        return src;
    }
}
/**
 * 目标,输出5V电压
 * @author HiWin10
 * @date 2020/8/18
 */
public interface Volatge5V {
    public int output5V();
}
/**
 * 类适配器
 * @author HiWin10
 * @date 2020/8/18
 * 继承了被适配者,实现了目标接口,
 * 我们发现,该类暴露了被适配者方法。Java是单继承,不推荐使用继承
 */
public class VoltageAdapter extends Voltage220V implements Volatge5V {
    @Override
    public int output5V() {
        int src = output220V();
        int dst = src / 44;
        return dst;
    }
}

实际使用

/**
 * 使用的手机类
 * @author HiWin10
 * @date 2020/8/18
 */
public class Phone {
    public void charging(VoltageAdapter voltageAdapter) {
        int src = voltageAdapter.output5V();
        if (src == 5){
            System.out.println("电压正常,可以充电,-> " + src + "V");
        } else {
            System.out.println("电压错误,不安全,不能充电 " + src + "V");
        }
    }
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

注意事项

  1. Java是单继承,所以类适配器需要继承src类这一点算是一个缺点
  2. src类中Adapter中会暴露方法,也增加了使用成本
  3. 由于其继承了src类,所以它可以根据需要重写src类方法,使得Adapter的灵活性增强了

对象适配器模式

和上面类似,只是不再使用继承,而是使用组合方式。

代码示例

/**
 * 被适配这,电源220V
 * @author HiWin10
 * @date 2020/8/18
 */
public class Voltage220V {
    public int output220V() {
        int src = 220;
        System.out.println("电压:" + src + "V");
        return src;
    }
}

/**
 * 目标,输出5V电压
 * @author HiWin10
 * @date 2020/8/18
 */
public interface Volatge5V {
    public int output5V();
}

/**
 * 对象适配器
 * @author HiWin10
 * @date 2020/8/18
 * 对象适配器就不是使用继承了,而是使用对象。聚合方式降低使用成本
 */
public class VoltageAdapter implements Volatge5V {
    private Voltage220V output220V;

    public VoltageAdapter(Voltage220V output220V) {
        this.output220V = output220V;
    }

    @Override
    public int output5V() {
        int dst = 0;
        if (null != this.output220V){
            dst = this.output220V.output220V() / 44;
        }
        return dst;
    }
}

/**
 * 使用的手机类
 * @author HiWin10
 * @date 2020/8/18
 */
public class Phone {
    public void charging(VoltageAdapter voltageAdapter) {
        int src = voltageAdapter.output5V();
        if (src == 5){
            System.out.println("电压正常,可以充电,-> " + src + "V");
        } else {
            System.out.println("电压错误,不安全,不能充电 " + src + "V");
        }
    }
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}

接口适配器模式

概念

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

代码示例

/**
 * 目标类, 里面有很多要适配的方法,
 * 实际上不会使用到这么多方法,要做的就是不要都实现,
 * 比如说现在只用到m1,m2方法,、
 * 解决方法就是写一个抽象类
 * @author HiWin10
 * @date 2020/8/18
 */
public interface Interface1 {
    public void m1();
    public void m2();
    public void m3();
    public void m4();
}


/**
 * 抽象类,用于实现接口的空实现。
 * 目的是,让使用者只重写需要适配的类
 * @author HiWin10
 * @date 2020/8/18
 */
public abstract class AbsAdapter implements Interface1 {
    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}



/**
 * @author HiWin10
 * @date 2020/8/18
 * 所以说接口适配器目的就是只实现需要使用的方法,不需要使用的不实现。
 */
public class Client {
    public static void main(String[] args) {
        AbsAdapter absAdapter = new AbsAdapter() {
            @Override
            public void m1() {
                System.out.println("哈哈m1方法被使用了");
            }

            @Override
            public void m2() {
                System.out.println("嘿嘿m2方法被使用了");
            }
        };

        absAdapter.m1();
        absAdapter.m2();
    }
}

桥接设计模式

手机操作问题

如上面所示,按照传统方式来设计的话,肯定是这样的

传统方式设计的话,结构简单,表达的非常清晰,但是如果品牌或者手机多了相对应的类也会非常多。而解决办法就是使用桥接模式。

概念

  1. 桥接模式是指,将实现与抽象放在两个不同层次中,使两个层次可以独立改变
  2. Bridge模式基于类的最小设计原则,通过使用封装、聚合以及集成等行为让不同的类承担不同的职责。它的主要特点是把抽象与行为实现分离。从而保证各个部分的独立以及方便对他们的扩展
  3. 遵循开闭原则

Phone是手机的类型

Brand是手机的品牌

使用此模式可以非常灵活的根据类型以及手机来匹配各种样式的手机

/**
 * 手机功能
 * @author HiWin10
 * @date 2020/8/18
 */
public interface Brand {
    public void open();
    public void close();
    public void call();
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class OPPO implements Brand {
    @Override
    public void open() {
        System.out.println("OPPO手机开机");
    }

    @Override
    public void close() {
        System.out.println("OPPO手机关机");
    }

    @Override
    public void call() {
        System.out.println("OPPO手机打电话");
    }
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class XiaoMi implements Brand {
    @Override
    public void open() {
        System.out.println("小米手机开机");
    }

    @Override
    public void close() {
        System.out.println("小米手机关机");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话");
    }
}
/**
 * 手机类 手机样式类:如折叠,推盖
 * @author HiWin10
 * @date 2020/8/18
 */
public abstract class Phone {
    private Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }

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

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

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

}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class FoldePhone extends Phone {

    public FoldePhone(Brand brand) {
        super(brand);
    }

    @Override
    public void open() {
        super.open();
        System.out.println("触摸屏手机");
    }

    @Override
    public void close() {
        super.close();        System.out.println("触摸屏手机");
    }

    @Override
    public void call() {
        super.call();        System.out.println("触摸屏手机");
    }
}

/**
 * 直立样式手机
 * @author HiWin10
 * @date 2020/8/18
 */
public class UpRightPhone extends Phone {
    @Override
    public void open() {
        super.open();
        System.out.println("直立样式手机");
    }

    @Override
    public void close() {
        super.close();        System.out.println("直立样式手机");

    }

    @Override
    public void call() {
        super.call();        System.out.println("直立样式手机");

    }

    public UpRightPhone(Brand brand) {
        super(brand);        System.out.println("直立样式手机");

    }
}
/**
 * 不同样式的手机下有不同样式的品牌手机
 * @author HiWin10
 * @date 2020/8/18
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = new UpRightPhone(new XiaoMi());
        phone.open();
        phone.call();

        System.out.println("----------");

        phone = new FoldePhone(new XiaoMi());
        phone.open();
        phone.close();

        System.out.println("----------");

        phone = new FoldePhone(new OPPO());
        phone.close();
        phone.call();
    }
}

装饰器模式

自由的对原本的对象增强

职责

  • 动态的为一个对象增加新的功能

  • 装饰模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀

实现细节

  • Component抽象构建角色
    • 真是对象和装饰对象共同的接口。这样客户端对象就能够以真实对象相同的方式通装饰对象交互。
  • ConcreteComponent具体构建角色(真实对象):
    • io流中的FileInputStreamFileOutputStream
  • Decorator装饰角色
    • 持有一个抽象构建的引用装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样就能在真实对象调用前后增加新的功能
  • ConcreteDecorator具体装饰角色
    • 负责给构建对象增加新的职责

装饰模式类的体系结构

具体代码实现

共同的接口

/**
 * 共同的接口
 */
public interface AllMycat {
    public void move();
}

ConcreteComponent具体构建角色(真实对象)

/**
 * 真实角色
 */
public class Car implements AllMycat {
    @Override
    public void move() {
        System.out.println("路上跑~!");
    }
}

Decorator装饰角色

/**
 * 装饰角色
 */
public class SuperCar implements AllMycat {
    private AllMycat cat;

    public SuperCar(AllMycat cat) {
        this.cat = cat;
    }

    @Override
    public void move() {
        cat.move();
    }
}

具体装饰角色

/**
 * `ConcreteDecorator`具体装饰角色
 */
public class FlyCat extends SuperCar {
    public FlyCat(AllMycat cat) {
        super(cat);
    }

    public void fly(){
        System.out.println("新功能:天上飞~!");
    }

    @Override
    public void move() {
        super.move();
        this.fly();
    }

}
/**
 * `ConcreteDecorator`具体装饰角色
 */
public class WaterCar extends SuperCar {
    public WaterCar(AllMycat cat) {
        super(cat);
    }

    public void water(){
        System.out.println("新功能:水上漂~!");
    }

    @Override
    public void move() {
        super.move();
        this.water();
    }

}

测试的代码

public class Dome1 {
    public static void main(String[] args) {
        Car car = new Car();
        // 新增功能  天上飞
        SuperCar superCar = new FlyCat(car);
        superCar.move();
        System.out.println("-----");

        // 新增功能 水上漂
        SuperCar superCar1 = new WaterCar(car);
        superCar1.move();
        System.out.println("-----");
        // 新增 水上  和 天上
        SuperCar superCar2 = new FlyCat(new WaterCar(new Car()));
        superCar2.move();

    }
}

开发的应用场景

  • IO中的输入流和输出流的设计

  • ServletAPI中提供了一个request对象的装饰设计模式的默认实现类HttpServletRequestWrapper,HttpServletRequestWrapper增强了request对象的功能

  • IO流实现细节

总结

  • 装饰模式也叫包装器模式
  • 装饰模式降低系统的耦合度,可以动态的增加或删除对象职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体装饰类

优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加
  • 可以对一个对象进行多次装饰,创造出不同的行为组合,使得功能更加强大
  • 具体构建类和具体装饰类可以独立变化, 用户可以根据需要自己增加新的构建子类和具体装饰子类

缺点

  • 产生很多小对象,大量小对象占用内存,一定程度上影响性能
  • 装饰模式易于出错,调试排查比较麻烦。

另一个例子

  1. 咖啡种类/单品咖啡:Espresso意大利咖啡ShortBlackDecaf无因咖啡
  2. 调料:MilkSoy(豆浆)Chocolate
  3. 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便

解决办法

/**
 * 顶级类,物品类
 * @author HiWin10
 * @date 2020/8/18
 */
public abstract class Drink {
    public String dsc;   // 描述
    public Float price;  // 价格

    public String getDsc() {
        return dsc;
    }

    public void setDsc(String dsc) {
        this.dsc = dsc;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    // 需要子类重写的方法,计算费用的抽象方法
    public abstract Float cost();
}
/**
 * 咖啡的类
 * @author HiWin10
 * @date 2020/8/18
 */
public class Coffee extends Drink {
    @Override
    public Float cost() {
        return super.getPrice();  // 获取到本咖啡的价格
    }
}


/**
 * 意大利咖啡
 * @author HiWin10
 * @date 2020/8/18
 */
public class Espresso extends Coffee{
    public Espresso() {
        setDsc(" 意大利咖啡 ");
        setPrice(6.0F);
    }
}

/** shortblack咖啡
 * @author HiWin10
 * @date 2020/8/18
 */
public class ShortBlack extends Coffee {
    public ShortBlack() {
        setDsc(" shortblack ");
        setPrice(4.0F);
    }
}

/**
 * 无因咖啡
 * @author HiWin10
 * @date 2020/8/18
 */
public class Decaf extends Coffee {
    public Decaf() {
        setDsc(" 无因咖啡 ");
        setPrice(1.0F);
    }
}
/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Decorator extends Drink {
    private Drink obj;

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

    @Override
    public Float cost() {
        return super.getPrice() + obj.cost();
    }

    @Override
    public String getDsc() {
        return dsc + " " + getPrice() + " && " + obj.getDsc() + "  " + obj.getPrice();
    }

    @Override
    public Float getPrice() {
        return super.getPrice() + obj.getPrice();
    }
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Milk extends Decorator {
    public Milk(Drink obj) {
        super(obj);
        setDsc(" 牛奶 ");
        setPrice(2.0F);
    }
}

/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Soy extends Decorator {
    public Soy(Drink obj) {
        super(obj);
        setDsc(" 豆浆 ");
        setPrice(4.0F);
    }
}

/**
 * 巧克力
 * @author HiWin10
 * @date 2020/8/18
 */
public class Chocolate extends Decorator{
    public Chocolate(Drink obj) {
        super(obj);
        setDsc("巧克力");
        setPrice(10.0F);
    }
}
/**
 * @author HiWin10
 * @date 2020/8/18
 */
public class Client {
    public static void main(String[] args) {
        Drink decaf = new Decaf();

        // 添加巧克力
        decaf = new Chocolate(decaf);
        System.out.println(decaf.getDsc());


        // 添加豆浆
        decaf = new Soy(decaf);
        System.out.println(decaf.getDsc());
        System.out.println(decaf.getPrice());

    }
}

组合模式

需求

编写程序展示一个学校系结构,在一个页面中展示出学校中所有专业

组合模式概念

  1. 组合模式依据树形结构来组合对象,用来表示部分以及整体层次
  2. 组合模式用户对单个对象和组合对象的访问具有一致性。既:组合能让客户以一致的方式处理个别对象以及组合对象。
  3. 只要是以树形结构来显示对象,就会使用组合模式

Component相当于组合对象声明接口,适当情况下,实现所有类共有接口默认行为,用于访问和管理Component的子节点

Leaf组合模式表示叶子节点,叶子节点下没有节点了

Composite:非叶子节点,用于存储子部件,在Component接口中实现子部件的相关操作

代码示例

/**
 * 抽象类用于定义的规范,大体来说是一个节点、
 * 需求:一个学校包含系、系下有专业,使用树形来表示出来。
 * name是名字
 * dsc 是 学校/系/专业的描述
 * print是每个节点输出的方式不一样
 * @author HiWin10
 * @date 2020/8/19
 */
public abstract class OrganizationComponent {
    private String name; // 名字
    private String dsc; // 描述

    public void add(OrganizationComponent add){
        // 类型不匹配异常
        throw new UnsupportedOperationException();
    }

    public void remove(OrganizationComponent remove) {
        throw  new UnsupportedOperationException();
    }

    // 此方法需要下面子类具体实现
    public abstract void print();

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

    public String getName() {
        return name;
    }

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

    public String getDsc() {
        return dsc;
    }

    public void setDsc(String dsc) {
        this.dsc = dsc;
    }
}


/**
 * 学校类
 * @author HiWin10
 * @date 2020/8/19
 */
public class University extends OrganizationComponent {
    // 包含很多系类
    private  List<OrganizationComponent> list = new ArrayList<>();

    public University(String name, String dsc) {
        super(name, dsc);
    }

    @Override
    public void add(OrganizationComponent add) {
        list.add(add);
    }

    @Override
    public void remove(OrganizationComponent remove) {
        list.remove(remove);
    }

    @Override
    public void print() {
        // 先输出该学校名字,然后输出该学校下的系
        System.out.println("--------"+getName()+"----------");
        for (OrganizationComponent item : list) {
            item.print();
        }
    }
}

/**
 * 系类
 * @author HiWin10
 * @date 2020/8/19
 */
public class College extends OrganizationComponent {
    // 包含很多专业
    private  List<OrganizationComponent> list = new ArrayList<>();

    public College(String name, String dsc) {
        super(name, dsc);
    }


    @Override
    public void add(OrganizationComponent add) {
        list.add(add);
    }

    @Override
    public void remove(OrganizationComponent remove) {
        list.remove(remove);
    }

    @Override
    public void print() {
        // 先输出该系名字,然后输出该系下的专业
        System.out.println("--------"+getName()+"----------");
        for (OrganizationComponent item : list) {
            item.print();
        }
    }
}


/**
 * 系下的专业,也就是根节点了,所以不需要重写add/remove方法
 * @author HiWin10
 * @date 2020/8/19
 */
public class Department extends OrganizationComponent {


    public Department(String name, String dsc) {
        super(name, dsc);
    }

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

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        // 建立学校
        OrganizationComponent university = new University("清华大学", "...");

        // 建立系
        OrganizationComponent college1 = new College("计算机", "IT");
        OrganizationComponent college2 = new College("金融", "...");

        // 建立系下的专业
        OrganizationComponent department1 = new Department("计算机科学与技术", "。。。");
        OrganizationComponent department2 = new Department("软件工程", "。。。");
        OrganizationComponent department3 = new Department("单机片", "。。。");


        OrganizationComponent department4 = new Department("金融专业", "。。。");


        // 添加
        university.add(college1);
        university.add(college2);

        college1.add(department1);
        college1.add(department2);
        college1.add(department3);
        college2.add(department4);

        university.print();
    }
}

外观模式

外观模式就是将创建对象或者完成某个步骤时过程中需要很多对象的配合,这个时候就可以在写一个类来完成这些工作,降低耦合

外观模式又必须遵守迪米特法则又叫最少知道原则,就是说把创建对象这个任务交给一个新的类。这个类就是外观模式

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

实际案例

组件一个家庭影院:DVD、投影仪、自动屏幕、爆米花机。

过程为:开爆米花机、放下屏幕、开投影仪、开DVD。

上图所示,完成对象创建的话直接交给HomeTheaterFacade完成

/**
 * DVD
 * @author HiWin10
 * @date 2020/8/19
 */
public class DVDPlayer {
    // 使用单例模式,饿汉式
    private static final DVDPlayer DVD_PLAYER = new DVDPlayer();

    public static DVDPlayer getDvdPlayer() {
        return DVD_PLAYER;
    }

    public void on() {
        System.out.println("DVD 开机");
    }

    public void off() {
        System.out.println("DVD 关机");
    }

    public void play() {
        System.out.println("DVD is playing ");
    }

    public void pause() {
        System.out.println("DVD pause...");
    }
}

/**
 * 爆米花
 * @author HiWin10
 * @date 2020/8/19
 */
public class Popcorn {
    private static Popcorn popcorn = new Popcorn();
    public Popcorn getPopcorn() {
        return popcorn;
    }

    public void on() {
        System.out.println("popcorn on");
    }

    public void off() {
        System.out.println("popcorn off");
    }

    public void pop() {
        System.out.println("popcorn is poping");
    }

}

/**
 * 投影机
 * @author HiWin10
 * @date 2020/8/19
 */
public class Projector {
    private static Projector projector = new Projector();
    public Projector getProjector() {
        return projector;
    }

    public void on() {
        System.out.println("projector on");
    }
    public void off() {
        System.out.println("projector off");

    }
    public void focus() {
        System.out.println("projector is Projector");

    }
}


/**
 * 投影机
 * @author HiWin10
 * @date 2020/8/19
 */
public class Screen {
    private static Screen screen = new Screen();
    public Screen getProjectorScreen() {
        return screen;
    }

    public void up() {
        System.out.println("projector up");
    }
    public void down() {
        System.out.println("projector down");

    }
}

/**
 * 外观模式,主要目的是完成迪米特法则 最少知道原则
 * 就是把很多对象放在一个类中,让这个类完成很多对象的创建,我们只需要调用这个创建对象的类即可
 * @author HiWin10
 * @date 2020/8/19
 */
public class HomeTheaterFacade {
    // 创建各个子系统对象
    private DVDPlayer dvdPlayer;
    private Popcorn popcorn;
    private Projector projector;
    private Screen screen;

    public HomeTheaterFacade(DVDPlayer dvdPlayer, Popcorn popcorn, Projector projector, Screen screen) {
        this.dvdPlayer = dvdPlayer;
        this.popcorn = popcorn;
        this.projector = projector;
        this.screen = screen;
    }

    // 统一开启方法
    public void ready() {
        dvdPlayer.on();
        popcorn.on();
        projector.on();
        screen.up();
    }

    // 统一关闭方法
    public void end() {
        dvdPlayer.off();
        popcorn.off();
        projector.off();
        screen.down();
    }
}


/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(new DVDPlayer(), new Popcorn(), new Projector(), new Screen());
        homeTheaterFacade.ready();
        System.out.println("-------------关闭");
        homeTheaterFacade.end();
    }
}

享元模式

需求

一个棋牌上有很多棋子,棋子的颜色以及状态都是相同的,位置是不同的。如果每个棋子都是单独对象的话,那么会造成内存浪费。这个时候就可以使用享元模式。

相同点:颜色、形状、大小

不同点:位置

上面分析的是棋子很多都是一样的,对于一样的棋子没有必要重新创建对象

概念

  1. 享元模式:运用共享技术有效地支持大量细粒度的对象
  2. 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接池对象中,有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,那么就创建一个
  3. 享元模式能够解决重复对象内存浪费问题,当系统中有大量相似对象,需要缓冲池时。不需宗师创建新对象,可以从缓冲池中拿,避免内存浪费,提高效率
  4. 享元模式典型技术应用就是池技术了,String常量池、数据库连接池、缓冲池、等都是享元模式。

  • FlyweightFactory享元工厂类
    • 创建并管理享元对象,享元池一半设计成键值对
  • FlyWeight抽象享元类
    • 是抽象的享元角色,他是产品的抽象类,同时定义了对象的外部状态和内部状态接口或实现
  • ConcreteFlyWeight具体享元类
    • 为内部状态提供成员变量进行存储
  • UnsharedConcreteFlyWeight非共享享元类
    • 不能被共享的子类可以设计为非共享享元类
/**
 * 抽象的享元角色,提供访问对象内部状态以及修改外部状态
 * @author HiWin10
 * @date 2020/8/19
 */
public interface FlyWeight {
    void setColor(String color);
    String getColor();
    void display(Coordinate c);
}


/**
 * 相同信息
 * 具体享元对象为内部状态提供成员变量进行存储
 * @author HiWin10
 * @date 2020/8/19
 */
public class ConcreteFlyWeight implements FlyWeight {
    private String color;

    public ConcreteFlyWeight(String color) {
        this.color = color;
    }

    @Override
    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String getColor() {
        return this.color;
    }

    @Override
    public void display(Coordinate c) {
        System.out.println("棋子颜色: " + color);
        System.out.println("棋子位置:" + c.getX() + "--" + c.getY());
    }
}


/**
 * 享元工厂类,用于 创建/获取 享元对象,可以说是池
 * @author HiWin10
 * @date 2020/8/19
 */
public class FlyWeigthFactory {
    private static Map<String, FlyWeight> map = new HashMap<>();

    public static FlyWeight getFlyWeight(String color) {
        if (map.get(color) != null){
            return map.get(color);
        }else {
            ConcreteFlyWeight weight = new ConcreteFlyWeight(color);
            map.put(color, weight);
            return weight;
        }
    }

}


/**
 * 不同信息-此案例里就是棋子位置
 * @author HiWin10
 * @date 2020/8/19
 */
public class Coordinate {
    int x, y;

    public Coordinate(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
}

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        FlyWeight black = FlyWeigthFactory.getFlyWeight("黑棋子");
        FlyWeight black2 = FlyWeigthFactory.getFlyWeight("黑棋子");

        System.out.println(black);
        System.out.println(black2);

        // ----- 不同点
        black.display(new Coordinate(10 ,20));
        black2.display(new Coordinate(30 ,20));  // 其实是一个对象
    }
}

享元模式应用场景

享元模式由于其共享的特性,可以在任何“池”中操作,比如:线程池、数据库连接池

String类的设计也是享元模式

优点

  • 极大减少内存中对象的数量
  • 相同或相似对象的内存只存一分,极大的节省资源,提高性能
  • 外部状态相对独立,不影响内部状态

缺点

  • 模式比较复杂,使程序逻辑负责化
  • 为了节省内存,共享了内存状态,分离外部状态,而读取外部状态运行时间变长。用时间换取空间

享元模式中的内部状态:指对象共享出来的详细(也就是说相似对象的相同点),存储在享元对象内部且不会随环境的改变而改变

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

代理模式

静态代理

  1. 静态代理:被代理和代理类都实现相同接口,这个模式有点适配器的味道

ITeacherDao:是接口,有teach()方法需要实现

TeacherDao:是ITeacherDao的实现类

TeacherDaoProxy:是静态代理类

/**
 * 静态代理模式:
 * @author HiWin10
 * @date 2020/8/19
 */
public interface ITeacherDao {
    public void teach(); // 授课方法
}

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("小新老师正在授课");
    }
}

/**
 * 静态代理生成工场
 * @author HiWin10
 * @date 2020/8/19
 */
public class TeacherDaoProxy implements ITeacherDao {
    private ITeacherDao obj;

    public TeacherDaoProxy(ITeacherDao obj) {
        this.obj = obj;
    }

    @Override
    public void teach() {
        System.out.println("开始代理");
        obj.teach();
        System.out.println("代理结束");
    }
}

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        ITeacherDao teacher = new TeacherDao();

        ITeacherDao teacherProxy = new TeacherDaoProxy(teacher);
        teacherProxy.teach();

    }
}

动态代理

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

JDK代理对象生成的API

  1. 代理类包在java.lang.reflect.Proxy
  2. JDK实现代理需要newProxyInstance方法
/**
 * 接口代理接口
 * @author HiWin10
 * @date 2020/8/19
 */
public interface ITeacherDao {
    public void teach();
}


/**
 * 普通类,实现了ITeacherDao接口
 * @author HiWin10
 * @date 2020/8/19
 */
public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
        System.out.println("老师正在授课");
    }
}

/**
 * 动态代理类工厂
 * @author HiWin10
 * @date 2020/8/19
 */
public class FactoryProxy<T> {
    private T obj;

    public FactoryProxy(T obj) {
        this.obj = obj;
    }

    public T getObjProxy() {
        T proxy = (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("开始动态代理");
                Object returnValue = method.invoke(obj, args);
                System.out.println("结束动态代理");
                return returnValue;
            }
        });

        return proxy;
    }

}

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        ITeacherDao teacher = new TeacherDao();

        ITeacherDao objProxy = new  FactoryProxy<>(teacher).getObjProxy();
        objProxy.teach();
    }
}

Cglib代理

  1. 静态代理和JDK代理都需要目标对象实现一个接口,但是有时候目标对象是没有实现接口的,这个时候要想代理目标对象需要Cglib代理
  2. Cglib是在内存中构建一个子类对象从而实现对目标对象功能扩展。
  3. Cglib底层是使用字节码处理框架ASM来转换字节码并生成新的类

使用

  1. 导入maven坐标

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
  2. 要目标实现MethodInterceptor接口

代码演示

/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class TeacherDao {
    public void teach(){
        System.out.println("老师正在授课");
    }
}

/**
 * Cglib动态代理生成工厂类
 * @author HiWin10
 * @date 2020/8/19
 */
public class FactoryCglib<T> implements MethodInterceptor {
    private T obj;

    public FactoryCglib(T obj) {
        this.obj = obj;
    }

    public T getProxyInstance() {
        // 1. 创建工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(this);
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置代理");
        Object returnValue = method.invoke(obj, objects);
        System.out.println("后置代理");
        return returnValue;
    }
}
/**
 * @author HiWin10
 * @date 2020/8/19
 */
public class Client {
    public static void main(String[] args) {
        TeacherDao teacherDao  = new TeacherDao();
        FactoryCglib<TeacherDao> factoryCglib = new FactoryCglib<>(teacherDao);
        TeacherDao proxyInstance = factoryCglib.getProxyInstance();
        proxyInstance.teach();
    }

    // 另外一种使用方式(内部类使用方式)
    @Test
    public void test02() {
//        new MethodInterceptor

        TeacherDao teacherDao = new TeacherDao();


        TeacherDao proxy = (TeacherDao) new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("开始代理");
                Object returnValue = method.invoke(teacherDao, objects);
                System.out.println("结束代理");
                return returnValue;
            }

            public Object get() {
                // 1. 创建工具类
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(teacherDao.getClass());
                enhancer.setCallback(this);
                return enhancer.create();
            }
        }.get();
        proxy.teach();

    }
}

行为型模式

模板方法模式

  1. 模板方法在一个抽象类公开定义执行他的方法模板,他的子类可以按需求重写方法实现,但调用将以抽象类中定义方式进行
  2. 简单的说模板方法模式定义算法骨架,而将一些步骤延迟到子类中,是得子类可以不改变一个算法结构,可以重定义该算法某些特定步骤

应用举例

  1. 制作豆浆如下步骤 : 选材-> 添加配料 -> 浸泡->放到豆浆机打碎
  • 我们都知道豆浆有很多口味,那么多种口味所不同的制作步骤也就在添加配料这一步骤中。

  • 大致流程是一样的,不一样的是添加配料,所以可以讲添加配料抽取为接口。

/**
 * 模板方法模式,抽象类
 * @author HiWin10
 * @date 2020/8/20
 * 豆浆制作过程
 * make() 编译 制作
 * select() 采集新鲜黄豆
 * add() 模板方法 添加配料
 * soak() 浸泡黄豆 和配料
 * beat() 开始制作 放入豆浆机
 */
public abstract class SoyMilk {
    public final void make() {
        this.select();
        // 判断该方法是否要执行 可在实现类中重写getFlag()方法来决定
        if (this.getFlag()) {
            add();
        }
        soak();
        beat();
    }

    public void select() {
        System.out.println(" 采集新鲜黄豆 ");
    }

    public abstract void add();

    public void soak() {
        System.out.println(" 浸泡黄豆 和配料 ");
    }

    public void beat() {
        System.out.println(" 开始制作 放入豆浆机 ");
    }

    public boolean getFlag() {
        return true;
    }
}

/**
 * @author HiWin10
 * @date 2020/8/20
 */
public class RedSoyMilk extends SoyMilk {
    @Override
    public void add() {
        System.out.println("红豆豆浆");
    }

    @Override
    public boolean getFlag() {
        return false;
    }
}

/**
 * @author HiWin10
 * @date 2020/8/20
 */
public class BlackBeanSoyaMikl extends SoyMilk {
    @Override
    public void add() {
        System.out.println(" 开始放入配料  黑豆豆浆");
    }
}


/**
 * @author HiWin10
 * @date 2020/8/20
 */
public class Client {
    public static void main(String[] args) {
        SoyMilk blackSoy = new BlackBeanSoyaMikl();
        blackSoy.make();

        System.out.println("---钩子方法");

        SoyMilk redSoy = new RedSoyMilk();
        redSoy.make();
    }
}

命令模式

长官给士兵下达命令,如果是简单的命令,士兵可直接执行长官的命令,但是如果是有点负责或很多的命令,可以在长官和士兵中间建立一个抽象层,来完成不同类型的命令。

代码中具体

Command:命令模式,需要执行的命令,可以是接口或抽象类

MyCommand:命令的具体实现,具体到执行哪些命令

Receiver:命令执行者,Command的执行应该包含命令的执行者

Invoke:命令发出者,应该包含Command命令模式,因为要发出命令

/**
 * 命令模式-命令
 * @author HiWin10
 * @date 2020/8/20
 */
public interface Command {
    /**
     * 这个方法是一个返回结果为空的方法
     * 实际项目中,可以根据需求设计多个不同的方法
     */
    void execute();
}


/**
 * 命令执行者-士兵
 * @author HiWin10
 * @date 2020/8/20
 */
public class Receiver {
    public void action() {
        System.out.println("士兵执行了命令");
    }
}


/**
 * 命令模式的具体类
 * @author HiWin10
 * @date 2020/8/20
 */
public class MyCommand implements Command {
    // 包含命令执行者引用
    private Receiver receiver;

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

    @Override
    public void execute() {
        // 可以在前后完成自定义需求
        receiver.action();
    }
}


/**
 * 命令发出者/长官
 * @author HiWin10
 * @date 2020/8/20
 */
public class Invoke {
    // 发送命令
    private Command command;

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

    public void call() {
        System.out.println("call 发出者发出命令");
    }
}

/**
 * @author HiWin10
 * @date 2020/8/20
 */
public class Client {
    public static void main(String[] args) {
        Command command = new MyCommand(new Receiver());
        Invoke invoke = new Invoke(command);
        invoke.call();
    }
}

访问者模式

概念

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

Visitor:是抽象访问者,定义了不同Element访问的行为。

ConcreteVisitor具体访问者的实现,

ObjectStructure:多个Element用来让被访问者(Element)接待访问者(Visitor)

Element:元素,被访问者,定义一个accept方法,接受一个访问者对象

ConcreteElement:被访问者的具体实现

场景举例

将观众分为男人和女人,对歌手进行评价,当看完歌手表演后,得到观众对歌手不同的评价,目前评价有两种(成功,失败)。

数据操作:Action:访问者抽象类,定义两种数据访问操作成功和失败

数据结构:Person:两种数据结构男生和女生

// 数据结构
/**
 * 被访问者接口
 * @author HiWin10
 * @date 2020/8/22
 */
public interface Person {
    /**
     * 提供方法,让访问者可以访问
     * @param action
     */
    public void accept(Action action);
}

/**
 * 男生被访问者
 * @author HiWin10
 * @date 2020/8/22
 */
public class Man implements Person {
    @Override
    public void accept(Action action) {
        action.getManResult(this);
    }
}

/**
 * 女生被访问者
 * @author HiWin10
 * @date 2020/8/22
 */
public class WoMan implements Person {
    @Override
    public void accept(Action action) {
        action.getWoManResult(this);
    }
}

// 数据操作
/**
 * 访问者模式的抽象类
 * @author HiWin10
 * @date 2020/8/22
 */
public interface Action {
    /**
     * 获取男生的评价
     */
    public void getManResult(Person person);

    /**
     * 获取女生的评价
     */
    public void getWoManResult(Person person);
}

/**
 * 购票成功的访问者
 * @author HiWin10
 * @date 2020/8/22
 */
public class Success implements Action {
    @Override
    public void getManResult(Person person) {
        System.out.println("男生投票成功~!");
    }

    @Override
    public void getWoManResult(Person person) {
        System.out.println("女人投票成功~!");
    }
}


/**
 * 投票-失败的访问者
 * @author HiWin10
 * @date 2020/8/22
 */
public class Fail implements Action {
    @Override
    public void getManResult(Person person) {
        System.out.println("男生投票失败");
    }

    @Override
    public void getWoManResult(Person person) {
        System.out.println("女生投票失败");
    }
}
/**
 * 数据结构,管理多人投票选择
 * @author HiWin10
 * @date 2020/8/22
 */
public class ObjectStructure {
    private List<Person> list = new ArrayList<>();

    public void add(Person person) {
        list.add(person);
    }

    public void remove(Person person) {
        list.remove(person);
    }

    // 显示测评情况
    public void printAlldisplay(Action action){
        for (Person person : list) {
            person.accept(action);
        }
    }
}

/**
 * @author HiWin10
 * @date 2020/8/22
 */
public class Client {
    public static void main(String[] args) {
        ObjectStructure structure = new ObjectStructure();
        structure.add(new WoMan());
        structure.add(new Man());

        structure.printAlldisplay(new Success());

        System.out.printf("改变访问者");

        structure.printAlldisplay(new Fail());
    }
}

迭代器模式

场景

  • 提供一种可以遍历聚合对象的方式,又称为:游标模式
  • 聚合对象:存储数据
  • 迭代器:遍历数据

迭代器接口

/**
 * 定义迭代器接口
 */
public interface MyIterator {
    void  first();      // 将游标指向第一个元素
    void next();        // 将游标指向下一个元素
    boolean hasNext();  // 判断是否存在下一个元素

    boolean isFirst();
    boolean isLast();

    Object getCurrentObj();  // 获取当前游标指向的对象
}

定义聚合类

并且定义内部类实现迭代器接口

/**
 * 自定义聚合类
 */
public class MyConcreteAggregate {
    private List<Object> list = new ArrayList<>();

    public void addObject(Object o){
        list.add(o);
    }

    public void removeObject(Object o){
        this.list.remove(o);
    }

    public List<Object> getList() {
        return list;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    // 提供获取迭代器方法
    public MyIterator createMyIterator(){
        return new ConcreteInterator();
    }

    // 使用内部类定义迭代器,可以直接使用外部类的属性
    private class ConcreteInterator implements MyIterator{

        private int count;  // 记录游标,用于记录遍历时的位置

        @Override
        public void first() {
            this.count=0;
        }

        @Override
        public void next() {
            if (count < list.size()){
                count++;
            }
        }

        @Override
        public boolean hasNext() {
            if (count < list.size()){
                return true;
            }else return false;
        }

        @Override
        public boolean isFirst() {
            return count==0?true:false;
        }

        @Override
        public boolean isLast() {
            return count==(list.size()-1)?true:false;
        }

        @Override
        public Object getCurrentObj() {
            return list.get(count);
        }
    }
}

测试

public class Dome1 {
    public static void main(String[] args) {
        MyConcreteAggregate list = new MyConcreteAggregate();
        list.addObject("小白");
        list.addObject("小新");
        list.addObject("小红");

        MyIterator myIterator = list.createMyIterator();
        while (myIterator.hasNext()){
            System.out.println(myIterator.getCurrentObj());
            myIterator.next();
        }
    }
}

常见场景

JDK内置迭代器(List/Set)

观察者模式

场景引入

场景

  1. 聊天室程序创建。服务器创建后,ABC三个客户端连接聊天。A向服务器发送数据,服务器端聊天数据改变。我们希望将这些数据分别发送给其他客户。也就是群发效果
  2. 网站上,很多订阅了“Java主题”的新闻。当有这个主题新闻时,就会将这些新闻发给所有订阅的人。
  3. 大家一起玩CS时,服务器将每个人的方位发送给所有客户

上面这些场景,我们都可以使用观察者模式来处理。我们可以把多个订阅者、客户称之为观察者;需要同步给多个订阅者的数据封装到对象中,称之为目标

核心

  • 观察者模式主要用于1:N的通知。当一个对象(目标对象Subject或Objservable)的状态变化时,它需要及时告知一系列对象(观察者对象,Observer),令他们做出相应
  • 通知观察者的方式 观察者主要是完成推的方式
      • 每次都会把通知以广播方式发送给所有观察者,所以观察者只能被动接受
      • 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。

类结构图

/**
 * 被观察者对象
 * 目标对象 事件源 观察者模式中事件发出者
 * @author HiWin10
 * @date 2020/8/21
 */
public abstract class Subject {
    private List<Observer> list = new ArrayList<>();

    /**
     * 注册观察者
     * @param observer
     */
    public void registerObserver(Observer observer) {
        list.add(observer);
    }

    /**
     * 删除观察者
     * @param observer
     */
    public void removeObserver(Observer observer){
        list.remove(observer);
    }

    /**
     * 发送事件源 给观察者推送信息
     */
    public void notifyAllObserver(){
        for (Observer observer : list) {
            observer.update(this);
        }
    }
}

/**
 * 事件源 - 中国天气总部
 * @author HiWin10
 * @date 2020/8/21
 */
public class ConcreteSubject extends Subject {
    private String type;


    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
        super.notifyAllObserver();
    }
}

/**
 * 观察者对象 事件触发者 接受者
 * @author HiWin10
 * @date 2020/8/21
 */
public interface Observer {
    /**
     * 拉去信息  主动获取信息
     */
    public void update(Subject subject);
}

/**
 * 新浪天气网 - 让天气总部更新数据的时候 这里也自动更新
 * @author HiWin10
 * @date 2020/8/21
 */
public class XinLangObserver implements Observer {
    private String type;

    @Override
    public void update(Subject subject) {
        // 包含数据更新
        this.type = ((ConcreteSubject)subject).getType();
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}

/**
 * @author HiWin10
 * @date 2020/8/21
 */
public class Client {
    public static void main(String[] args) {
        // 先来创建观察者
        XinLangObserver xinlang1 = new XinLangObserver();
        XinLangObserver xinlang2 = new XinLangObserver();
        XinLangObserver xinlang3 = new XinLangObserver();

        ConcreteSubject subject = new ConcreteSubject();
        // 添加观察者
        subject.registerObserver(xinlang1);
        subject.registerObserver(xinlang2);
        subject.registerObserver(xinlang3);

        // 天气总部更新天气,然后自动推送信息
        subject.setType("晴天");
        // 获取观察者的天气
        System.out.println("新浪1天气 " + xinlang1.getType());
        System.out.println("新浪2天气 " + xinlang2.getType());
        System.out.println("新浪3天气 " + xinlang3.getType());

    }
}

中介者模式

多个功能模块里,每个模块互相调用,这样的话

  1. 多个模块会形成网状图
  2. 虽然灵活,但是逻辑非常乱,如果是大型项目逻辑会变得更复杂

那么解决办法就是每个模块在互相调用的时候都让一个对象来完成调用,这个对象就是中介者对象。

场景

  • 假如没有总经理,下面有三个部门:财务部、市场部、研发部。财务部要发工资,让大家核对公司需要跟市场部和研发部都通气;市场部要接个新项目,需要研发部处理技术、需要财务部出资金。市场部跟各个部门打交道。虽然只有三个部门,但是关系非常乱

  • 实际上,公司都有部门经理。各个部门有什么事情都要通报到总经理这里,总经理再通知各个相关部门

  • 这就是一个典型的“中介者模式”总经理起到一个中介、协调作用

核心

  • 如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对多关系,将导致关系及其负责,这些对象成为“同事对象”
  • 我们可以引入一个中介者对象,使各个同事对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星形结构。
/**
 * 中介者接口
 * @author HiWin10
 * @date 2020/8/21
 */
public interface Mediator {
    /**
     * 同事接口,也是注册部门
     * @param dname
     * @param department
     */
    void register(String dname, Department department);

    /**
     * 向同事(部门)发送请求
     * @param dname
     */
    void command(String dname);
}


/**
 * 中介者实现
 * 经理
 * @author HiWin10
 * @date 2020/8/21
 */
public class President implements Mediator {
    private Map<String, Department> map = new HashMap<>();

    @Override
    public void register(String dname, Department department) {
        map.put(dname, department);
    }

    @Override
    public void command(String dname) {
        map.get(dname).selfAction();
    }
}


/**
 * 案例中的部门接口
 * @author HiWin10
 * @date 2020/8/21
 */
public interface Department {
    /**
     * 做本部门的该做的事情
     */
    void selfAction();

    /**
     * 向中介者对象/总经理发出申请,该案例就是向其他部门寻求帮助
     */
    void outAction();
}


/**
 * 研发部
 * @author HiWin10
 * @date 2020/8/21
 */
public class Development implements Department {
    private Mediator mediator;

    public Development(Mediator mediator) {
        this.mediator = mediator;
        this.mediator.register("研发部development", this);
    }

    @Override
    public void selfAction() {
        System.out.println("专心科研,开发项目!");
    }

    @Override
    public void outAction() {
        System.out.println("会报工作,没钱了,需要财务部支持");
        this.mediator.command("财务部Finacial");
    }
}


/**
 * 财务部
 * @author HiWin10
 * @date 2020/8/21
 */
public class Finacial implements Department {
    private Mediator mediator;

    public Finacial(Mediator mediator) {
        this.mediator = mediator;
        this.mediator.register("财务部Finacial", this);
    }

    @Override
    public void selfAction() {
        System.out.println("财富部 数钱");
    }

    @Override
    public void outAction() {
        System.out.println("汇报工作!钱太多了,怎么话~?");
    }
}

/**
 * @author HiWin10
 * @date 2020/8/21
 */
public class Market implements Department {
    private Mediator mediator;

    public Market(Mediator mediator) {
        this.mediator = mediator;
        // 注册该部门,同事
        mediator.register("市场部Market", this);
    }

    @Override
    public void selfAction() {
        System.out.println("跑去接项目!");
    }

    @Override
    public void outAction() {
        System.out.println("汇报工作项目承若的进度,需要资金支持!");
        mediator.command("财务部Finacial");
    }
}

备忘录模式

非常简单,就是对一个对象的内容进行备份,并且可以恢复过来。

基本概念

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

Emp:正常对象,是一个要备份的对象

EmpMemento:备忘录对象,Emp对象的备份,与Emp对象的属性一致

CareTaker:备忘录对象的管理者,可用于管理多个备忘录

/**
 * 员工类
 * @author HiWin10
 * @date 2020/8/21
 */
public class Emp {
    private String name;
    private int age;
    private double salary;

    /**
     * 生成备忘录对象,(生成备份对象)
     * @return
     */
    public EmpMemento empMemento() {
        return new EmpMemento(this);
    }

    /**
     * 根据备份对象恢复恢复对象
     * @param empMemento 备忘录对象
     */
    public void recovery(EmpMemento empMemento){
        this.name = empMemento.getName();
        this.age = empMemento.getAge();
        this.salary = empMemento.getSalary();
    }

    public Emp(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    // 省略/getter/setter方法
}

/**
 * 备忘录类,和Emp类型属性一致,用于就是备份Emp对象
 * @author HiWin10
 * @date 2020/8/21
 */
public class EmpMemento {
    private String name;
    private int age;
    private double salary;

    public EmpMemento(Emp copy) {
        this.name = copy.getName();
        this.age = copy.getAge();
        this.salary = copy.getSalary();
    }
}

/**
 * 负责人类,用于管理备忘录对象
 * @author HiWin10
 * @date 2020/8/21
 */
public class CareTaker {
    private EmpMemento empMemento;

    // 也可以管理多个备忘录对象(多个历史记录)
    // private List<EmpMemento> list = new ArrayList<>();

    public EmpMemento getEmpMemento() {
        return empMemento;
    }

    public void setEmpMemento(EmpMemento empMemento) {
        this.empMemento = empMemento;
    }
}

/**
 * @author HiWin10
 * @date 2020/8/21
 */
public class Client {
    public static void main(String[] args) {
        // 创建正常对象
        Emp xiaohong = new Emp("小红", 20, 900D);

        // 创建备忘录管理者对象
        CareTaker careTaker = new CareTaker();
        // 备份对象
        careTaker.setEmpMemento(xiaohong.empMemento());

        xiaohong.setName("小白");
        xiaohong.setAge(5);
        System.out.println("修改后的对象 " + xiaohong);

        // 开始恢复对象
        xiaohong.recovery(careTaker.getEmpMemento());
        System.out.println("恢复后的对象 " + xiaohong);
    }
}

解释器模式

状态模式

在一个完整的事件里面会有很多不同的状态,当然不同状态对应不同的行为,比如说信号灯:红灯对应停,绿灯对应行,黄灯对应警示。

状态模式

  1. 状态模式:它主要用来解决多种状态转换时,需要对外输出不同行为的问题。状态和行为一一对应,状态之间可以相互转换
  2. 状态模式符合“开闭原则”,容易增删状态
  3. 应用场景:当一个事件或者对象有很多状态,状态之间会互相转换,对不同状态又有不同行为可以考虑使用状态模式

代码示例

以解决房间状态为例

先来分析酒店房间都有几种状态:

类图

State:状态的抽象层,有不同状态不同行为的方法handle()

FreeState:空房间状态

BookedState:预定状态

CheckInState:已入住状态

Context:上下文类,环境类,用来切换事务的状态

/**
 * 状态的接口
 * @author HiWin10
 * @date 2020/8/21
 */
public interface State {
    /**
     * 不同状态的具体行为
     */
    void handle();
}

/**
 * 房间-空 状态
 * @author HiWin10
 * @date 2020/8/21
 */
public class FreeState implements State {
    @Override
    public void handle() {
        System.out.println("该房间为空,没有人预定!");
    }
}


/**
 * 房间已经被预定状态
 * @author HiWin10
 * @date 2020/8/21
 */
public class BookedState implements State {
    @Override
    public void handle() {
        System.out.println("房间已预定!别人不能定了");
    }
}

/**
 * 房间已被入住-状态
 * @author HiWin10
 * @date 2020/8/21
 */
public class CheckInState implements State {
    @Override
    public void handle() {
        System.out.println("此房间已经被入住了,请勿打扰!");
    }
}

/**
 * 环境类
 * @author HiWin10
 * @date 2020/8/21
 */
public class Context {
    /**
     * 包含状态引用,用于切换状态
     */
    private State state;

    public void setState(State state) {
        this.state = state;
        System.out.println("状态已经被修改");
        // 直接执行转台
        state.handle();
    }
}

/**
 * @author HiWin10
 * @date 2020/8/21
 */
public class Client {
    public static void main(String[] args) {
        // 状态各种状态
        State free = new FreeState();  // 空房间-状态
        State booked = new BookedState();  // 预定房间-状态
        State checkin = new CheckInState();  // 入住房间-状态

        // 创建上下文对象 环境类
        Context context = new Context();
        context.setState(free);
        // .... 处理了一些代码 房间已被预定
        context.setState(booked);
        // .... 处理了一些代码 房间已被入住
        context.setState(checkin);
    }
}

策略者模式

  1. 策略模式中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
  2. 这算法体现了如下设计原则:
    • 把变化的代码从不变的代码中分离出来
    • 针对接口编程而不是具体类
    • 多用组合/聚合,少用继承(客户通过组合/聚合方式选择要使用的策略)

案例上讲

案例:商城里面有会员,当会员购买产品的时候会根据会员的等级来给会员具体打折情况

案例:某个商场人员接到单后报价策略(CRM系统中常见问题)。报价策略很复杂,可以简单作如下分类:

  • 普通客户小批量报价
  • 普通客户大批量报价
  • 老客户小批量报价
  • 老客户大批量报价

具体选用哪个报价策略,这个需要根据实际情况来确定

Strategy:策略模式接口,下面是不同的策略

NewCustomerFewStrategy:其中的一个策略,普通客户策略:普通客户小批量不打折

Context:上下文对象,用来切换不同策略的使用

/**
 * 策略模式,
 * 案例中的算法族,用来获取价格
 * @author HiWin10
 * @date 2020/8/22
 */
public interface Strategy {
    /**
     * 通过价格根据不同客户算出不同的批发价格
     * @param standardPrice
     * @return
     */
    public double gerPrice(Double standardPrice);
}

/**
 * 普通客户策略:普通客户小批量不打折
 * @author HiWin10
 * @date 2020/8/22
 */
public class NewCustomerFewStrategy implements Strategy {
    @Override
    public double gerPrice(Double standardPrice) {
        System.out.println("普通客户小批量的->不打折,原价");
        return standardPrice;
    }
}


/**
 * 普通客户策略:普通客户大批量的 打九折
 * @author HiWin10
 * @date 2020/8/22
 */
public class NewCustomerManyStrategy implements Strategy {
    @Override
    public double gerPrice(Double standardPrice) {
        System.out.println("普通客户,大批量的,打九折");
        return standardPrice * 0.9;
    }
}

/**
 * 老客户策略:老客户小批量的 打八五折
 * @author HiWin10
 * @date 2020/8/22
 */
public class OldCustomerFewStrategy implements Strategy {

    @Override
    public double gerPrice(Double standardPrice) {
        System.out.println("老客户,小批量的,打八五折");
        return standardPrice * 0.85;
    }
}


/**
 * 老客户策略:老客户大批量的 打八折
 * @author HiWin10
 * @date 2020/8/22
 */
public class OldCustomerManyStrategy implements Strategy {

    @Override
    public double gerPrice(Double standardPrice) {
        System.out.println("老客户,大批量的,打八折");
        return standardPrice * 0.8;
    }
}


/**
 * 上下文类,环境类
 * @author HiWin10
 * @date 2020/8/22
 */
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void printPrice(double s){
        double result = strategy.gerPrice(s);
        System.out.println("您的报价为:" + result);
    }
}


/**
 * @author HiWin10
 * @date 2020/8/22
 */
public class Client {
    public static void main(String[] args) {
        Context context = new Context(new NewCustomerManyStrategy());
        context.printPrice(1000);

        // 有一个客户来,或者说该客户升级了
        context.setStrategy(new OldCustomerManyStrategy());
        context.printPrice(1000);
    }
}

职责链模式

就像一个单向链表,一个节点解决不了问题,抛给下一个节点去解决

概念

  1. 职责链模式:为请求创建了一个接口对象的链,这种模式对请求的发送者和接受者进行解耦
  2. 职责链模式通常每个接受者都包含对下一个接受者引用,如果当前对象不能处理问题,那么把他抛给下一个接受者。

Handler:抽象的处理者,定义了处理请求接口,同时包含另一个Handler

ConcreteHandlerA,B:是具体的处理者,处理它自己负责的请求,可以访问它后续的处理者,如果后续有多个处理者,那么就是职责链表示

Request:具体的请求,处理者要处理的请求

公司里面,请求条批阅过程

  • 如果请求天数小于3天,主任审批
  • 如果请求天数大于等于3天,小于10天,经历审批
  • 如果大于10天,小于 30天,总经理审批
  • 如果大于等于30天,提示拒绝
/**
 * @author HiWin10
 * @date 2020/8/22
 */
public abstract class Leader {
    /**
     * 领导姓名
     */
    protected String name;
    /**
     * 该领导上级
     */
    private Leader nextLeader;

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

    /**
     * 设置下一个处理者
     * @param nextLeader
     */
    public void setLeader(Leader nextLeader) {
        this.nextLeader = nextLeader;
    }

    /**
     * 每个任务节点要处理的任务,交给具体角色实现
     * @param empRequest
     */
    public abstract void handleRequest(EmpRequest empRequest);

    /**
     * 如果该角色处理不了问题,就抛给下一个角色
     * @param empRequest
     */
    public void nextNode(EmpRequest empRequest) {
        if (null != this.nextLeader){
            this.nextLeader.handleRequest(empRequest);
        } else {
            throw new RuntimeException("没有设置下一个节点,下一个节点目前为空(责任链还没有搭建)");
        }
    }

}

/**
 * 责任链上的角色-主任
 * 员工请假小于3天,主任可以直接批准
 * @author HiWin10
 * @date 2020/8/22
 */
public class Director extends Leader {
    public Director(String name) {
        super(name);
    }

    @Override
    public void handleRequest(EmpRequest empRequest) {
        if (empRequest.getDays() <= 3)
            System.out.println("我是 " + super.name + "主任 ,我批准了你的请假 ");
        else
            this.nextNode(empRequest);
    }
}


/**
 * 责任链上的角色-经理
 * 员工请假大于3天小于10天,经理可以直接批准
 * @author HiWin10
 * @date 2020/8/22
 */
public class Manager extends Leader {
    public Manager(String name) {
        super(name);
    }

    @Override
    public void handleRequest(EmpRequest empRequest) {
        if (empRequest.getDays() < 10)
            System.out.println("我是 " + name + "经理,我批准了你的请假");
        else
            this.nextNode(empRequest);
    }
}

/**
 * 责任链上的角色-总经理
 * 员工请假大于10天,总经理经理可以直接批准
 * @author HiWin10
 * @date 2020/8/22
 */
public class General extends Leader {
    public General(String name) {
        super(name);
    }

    @Override
    public void handleRequest(EmpRequest empRequest) {
        int temp = empRequest.getDays();
        if (temp >= 10 && temp <= 30){
            // 总经理可批准
            System.out.println("我是" + name + "总经理,批注你了你的请假");
        } else
            System.out.println("你不用来了");
    }
}


/**
 * 员工请求类
 * @author HiWin10
 * @date 2020/8/22
 */
public class EmpRequest {
    /**
     * 请假员工姓名
     */
    private String name;
    /**
     * 请假天数
     */
    private Integer days;

    public EmpRequest(String name, Integer days) {
        this.name = name;
        this.days = days;
    }

    public String getName() {
        return name;
    }

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

    public Integer getDays() {
        return days;
    }

    public void setDays(Integer days) {
        this.days = days;
    }
}


/**
 * @author HiWin10
 * @date 2020/8/22
 */
public class Client {
    public static void main(String[] args) {
        // 创建角色,并搭建责任链
        Leader director = new Director("王");
        Leader manager = new Manager("李");
        Leader general = new General("林");
        director.setLeader(manager);
        manager.setLeader(general);

        // 创建员工消息
        EmpRequest empRequest = new EmpRequest("小红", 10);
        // 提交请求
        director.handleRequest(empRequest);
    }
}
posted @ 2020-08-22 23:27  蜡笔没有大象  阅读(215)  评论(0)    收藏  举报