设计模式

大部分代码来自菜鸟教程,这是学习笔记,详细学习可到菜鸟教程-设计模式,或购买书籍。

设计模式的目的

  1. 代码可用性
  2. 可读性
  3. 可扩展性
  4. 可靠性
  5. 使程序呈现高内聚、低耦合

设计模式的七大原则

就是在编程时,需要遵循的原则

1. 单一职责原则

基本介绍

降低类的复杂度,一个类只负责一项职责。

2. 接口隔离原则

客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。

3. 依赖倒转原则

  1. 高层模块不应该依赖于底层模块,二者都应该依赖其抽象。
  2. 抽象不应该依赖细节,细节应该依赖抽象。
  3. 依赖倒置的中心思想是面向接口编程。
  4. 抽象指的是接口或抽象类,细节就是具体的实现类。
  5. 使用接口或抽象类的目的就是制定好规范,而不涉及任何具体的操作。

4. 里氏替换原则

  1. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类方法。

5. 开闭原则

  1. 开闭原则是编程中最基础、最重要的设计原则。
  2. 一个软件实体,如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。(就是在修改业务代码后,使用的代码不用进行修改)
  3. 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现
  4. 编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则。

6. 迪米特原则

  1. 一个对象应该对其他对象保持最少的了解。
  2. 类与类的关系越密切,耦合度就越大。
  3. 对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供public方法,不对外泄露信息。
  4. 陌生的类最好不要以局部变量的形式出现在类的内部。

7. 合成复用原则

尽量使用合成(set)/聚合(new一个成员变量)的方式,而不是使用继承。(就是使用new成员变量的方式或者使用set)

类之间的关系

依赖关系

只要在类中使用到了对方,两个类之间就存在依赖关系。

泛化关系

泛化关系实际上就是继承关系,它是依赖关系的特例。

实现关系

实现关系实际上就是A类实现B接口,它是依赖关系的特例。

关联关系

  1. 关联关系就是类与类之间的联系,它是依赖关系的特例。
  2. 可以是双向关系或单向关系。(一对一、一对多、多对多)

聚合关系

  1. 实体类之间可以分离的就是聚合关系(比如电脑类中包含一个鼠标类和键盘类,但是他们可以分离的)
  2. 聚合关系表示的是整体和部分的关系,整体和部分可以分开。聚合关系是关联关系的特例。

组合关系

实体类之间不能进行分离,如在Person类中IdCard idCard = new IdCard();,这样Idcard类就和Person同时创建、销毁。

设计模式的分类

  1. 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
  2. 结构性模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
  3. 行为模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式

(1)单例模式

一、饿汉式

  1. 优点:基于classloader机制避免了多线程的同步问题。线程安全的。
  2. 缺点:类加载时就初始化,浪费内存。
/**
 * 饿汉式
 * 优点:线程安全
 * 缺点:类加载就初始化,浪费内存
 */
public class Single01{
    private static Single01 single = new Single01();

    // 私有化构造器
    private Single01() {
        
    }

    // 提供一个外界的访问方法
    public static Single01 getInstance() {
        return single;
    }
}

二、懒汉式:线程不安全的

  1. 优点:实现懒加载。
/**
 * 懒汉式:线程不安全的
 * 优点:实现了懒加载
 */
public class Single02 {
    private static Single02 single;

    public Single02() {}

    public static Single02 getInstance() {
        if(single == null)
            single = new Single02();

        return single;
    }
}

1. 模拟多线程,进行测试

  1. 使用Thread.sleep(200),增大获取不同实例的机会。
/**
 * 懒汉式:线程不安全的
 * 优点:实现了懒加载
 */
public class Single02 {
    private static Single02 single;

    public Single02() {}

    public static Single02 getInstance() {
        // 多线程环境下
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        if(single == null)
            single = new Single02();

        return single;
    }
}

  1. 模拟多线程
public class Test02 {
    public static void main(String[] args) {
        // 模拟多线程测试
        new Thread("t1") {
            @Override
            public void run() {
                Single02 single1 = Single02.getInstance();
                // Single02@10f33626
                System.out.println(single1);
            }
        }.start();

        new Thread("t2") {
            @Override
            public void run() {
                Single02 single2 = Single02.getInstance();
                // Single02@25c8063f
                System.out.println(single2);
            }
        }.start();
    }
}

  1. 获取实例不同。

三、懒汉式:线程安全

  1. 加入synchronized同步代码块,保证线程安全。
  2. 但是如果频繁调用getInstance()方法,效率低。
/**
 * 懒汉式:线程安全
 */
public class Single03 {
    private static Single03 single;

    private Single03() {}

    public static Single03 getInstance() {
        // 加锁
        synchronized (Single03.class) {
            if(single == null)
                single = new Single03();

            return single;
        }
    }
}

四:懒汉式:双重检查锁(double-cheched-locking)

  1. 双重锁,在多线程情况下能保持高性能。
/**
 * 双重检查锁
 */
public class Single04 {
    private static Single04 single;

    private Single04() {}

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

1. 方式二:将对象声明为volatile

  1. 将对象声明为volatitle后,重排序在多线程环境中将会被禁止。
/**
 * 双重检查锁
 */
public class Single04 {
    private static volatitle Single04 single;

    private Single04() {}

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

2. 使用反射和序列化的方式破坏单例模式

  1. 私有化构造器的都可以通过反射对单例模式进行破坏。
public class Test01 {
    public static void main(String[] args) {
        // 测试饿汉式
        Single01 single1 = Single01.getInstance();
        Single01 single2 = Single01.getInstance();

        // true
        System.out.println(single1 == single2);

        // 通过反射破除单例模式
        try {
            Class<Single01> aClass = Single01.class;
            Constructor<Single01> constructor = aClass.getDeclaredConstructor();
            constructor.setAccessible(true);

			// 通过反射回去一个实例
            Single01 single01 = constructor.newInstance();

            // false
            System.out.println(single01 == single2);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
  1. 对象进行序列化,反序列化的时候,会创建一个新的对象。
public class Test01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 测试饿汉式
        Single01 single1 = Single01.getInstance();
        Single01 single2 = Single01.getInstance();

        // true
        System.out.println(single1 == single2);
        
        // 通过反序列化破解单例:要实现Serializable接口
        // 通过反序列化把对象写出到文件中
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/test.txt"));
        oos.writeObject(single1);
        // 写出完成刷新
        oos.flush();
        oos.close();

        // 将序列化后的对象写回内存
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/test.txt"));
        // 获得序列化后的对象
        Single01 obj = (Single01) ois.readObject();
        // false
        System.out.println(single1 == obj);
    }
}

五、静态内部类

  1. 静态内部类,在外部被装载的时候,single对象不一定被初始化。
  2. 因为SingleHolder没有被主动调用。只有在调用getInstance()方法时,才会显式的调用SingleHolder类,从而实例化single。
/**
 * 静态内部类
 */
public class Single05 {
    private Single05() {}

    // 使用静态内部类
    private static class SingleHolder {
        private static Single05 single = new Single05();
    }

    public static Single05 getInstance() {
        return SingleHolder.single;
    }
}

六、枚举

  1. 实现单例模式的最佳方式
  2. 避免了多线程同步的问题。
  3. 自动支持序列化机制,防止反序列化重新创建新的对象。
/**
 * 使用枚举的方式
 */
public enum Single06 {
    INSTANCE;

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

(2)工厂模式

  1. 让其子类实现接口,返回一个具体的产品。
    在这里插入图片描述
    第一步:创建一个接口
/**
 * 形状接口
 */
public interface Shape {
    void draw();
}

第二步:创建接口的实现类

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("circle");
    }
}
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("square");
    }
}
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("rectangle");
    }
}

第三步:创建工厂类

/**
 * 工厂类
 */
public class ShapeFactory {
    public static Shape getShape(String shapeName) {
        if(shapeName == null)
            return null;

        if("circle".equals(shapeName)) {
            return new Circle();
        } else if ("square".equals(shapeName)) {
            return new Square();
        } else if ("rectangle".equals(shapeName)) {
            return new Rectangle();
        }

        return null;
    }
}

第四步:创建Demo进行测试

public class FactoryDemo {
    public static void main(String[] args) {
    	// 使用工厂获取具体的类型
        Shape circle = ShapeFactory.getShape("circle");
        circle.draw();
    }
}

(3) 抽象工厂模式

  1. 抽象工厂模式(Abstract Factory Pattern),抽象工厂可以生产一个产品族,比如一个工厂可以生产手机和耳机。

在这里插入图片描述
第一步:创建手机和耳机的抽象类

/**
 * 手机抽象类
 */
public abstract class Phone {
    abstract void name();
}
/**
 * 耳机抽象类
 */
public abstract class HeadSet {
    abstract void play();
}

第二步:创建华为手机和小米手机

/**
 * 华为手机
 */
public class HuaweiPhone extends Phone{
    @Override
    void name() {
        System.out.println("华为手机");
    }
}
/**
 * 小米手机
 */
public class XiaoMiPhone extends Phone{
    @Override
    void name() {
        System.out.println("小米手机");
    }
}

第三步:创建华为耳机和小米耳机

/**
 * 华为耳机
 */
public class HuaweiHeadSet extends HeadSet{
    @Override
    void play() {
        System.out.println("华为耳机:播放");
    }
}
/**
 * 小米耳机
 */
public class XiaoMiHeadSet extends HeadSet{
    @Override
    void play() {
        System.out.println("小米耳机:播放");
    }
}

第四步:创建一个抽象工厂,能够生产产品族手机和耳机

/**
 * 抽象工厂:创建产品族
 */
public interface AbstractFactory{
    Phone getPhone();
    HeadSet getHeadSet();
}

第五步:创建华为工厂和小米工厂,实现抽象工厂

/**
 * 华为工厂创建华为的产品组
 */
public class HuaWeiFactory implements AbstractFactory{
    @Override
    public Phone getPhone() {
        return new HuaweiPhone();
    }

    @Override
    public HeadSet getHeadSet() {
        return new HuaweiHeadSet();
    }
}

/**
 * 小米工厂:生成小米产品组
 */
public class XiaoMiFactory implements AbstractFactory{
    @Override
    public Phone getPhone() {
        return new XiaoMiPhone();
    }

    @Override
    public HeadSet getHeadSet() {
        return new XiaoMiHeadSet();
    }
}

第六步:创建Demo类进行测试

public class Demo {
    public static void main(String[] args) {
        // 创建一个华为工厂
        AbstractFactory huaWeiFactory = new HuaWeiFactory();
        Phone huaWeiPhone = huaWeiFactory.getPhone();
        HeadSet huaWeiHeadSet = huaWeiFactory.getHeadSet();
        huaWeiPhone.name();
        huaWeiHeadSet.play();
    }
}

(4)建造者模式

  1. 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂对象。
  2. 适用于:一些基础部件不会经常改变,而其组合经常变化时。
  3. 实现案例:我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。然后我们创建一个 Meal 类,带有 Item 的 ArrayList 和一个通过结合 Item 来创建不同类Meal 对象的 MealBuilder。BuilderPatternDemo 类使用 MealBuilder 来创建一个 Meal。
    在这里插入图片描述
    第一步:创建一个商品项接口和食物包装接口
/**
 * 商品项接口
 */
public interface Item {
    String name();
    // 包装
    Packing pacing();
    float price();
}
/**
 * 包装接口
 */
public interface Packing {
    String pack();
}

第二步:创建Packing包装接口的实现类

/**
 * 包装纸:实现包装接口
 */
public class Wrapper implements Packing {
    @Override
    public String pack() {
        return "wrapper";
    }
}
/**
 * 包装瓶:实现包装接口
 */
public class Bottle implements Packing {
    @Override
    public String pack() {
        return "bottle";
    }
}

第三步:创建Item商品接口的抽象类Burger和ColdDrink。

/**
 * 汉堡类抽象类:实现商品接口
 */
public abstract class Burger implements Item{
    // 汉堡使用包装纸进行包装
    @Override
    public Packing pacing() {
        return new Wrapper();
    }
}

/**
 * 冷饮抽象类:实现item接口
 */
public abstract class ColdDrink implements Item{
    // 冷饮使用瓶子包装
    @Override
    public Packing pacing() {
        return new Bottle();
    }
}

第四步:创建抽象类Burger和ColdDrink的实现类

/**
 * 蔬菜汉堡:继承汉堡类
 */
public class VegBurger extends Burger {
    @Override
    public String name() {
        return "vegBurger";
    }

    @Override
    public float price() {
        return 25.0f;
    }
}
/**
 * 鸡肉汉堡:继承汉堡类
 */
public class ChickenBurger extends Burger{
    @Override
    public String name() {
        return "chickenBurger";
    }

    @Override
    public float price() {
        return 35.0f;
    }
}
/**
 * 可口可乐:继承可乐类
 */
public class Coke extends ColdDrink {
    @Override
    public String name() {
        return "cock";
    }

    @Override
    public float price() {
        return 10.0f;
    }
}
/**
 * 百事可乐:继承冷饮类
 */
public class Pepsi extends ColdDrink{
    @Override
    public String name() {
        return "pepsi";
    }

    @Override
    public float price() {
        return 15.0f;
    }
}

第五步:创建Meal商品类

/**
 * 一顿餐的组合
 */
public class Meal {
    private List<Item> items = new ArrayList<>();

    // 添加组合
    public void addItem(Item item) {
        items.add(item);
    }

    // 计算商品总价
    public float getCost() {
        float cost = 0.0f;
        for(Item item : items) {
            cost += item.price();
        }

        return cost;
    }

    // 查看一餐的所有信息
    public void showItems() {
        for(Item item : items) {
            System.out.println("商品名:" + item.name());
            System.out.println("商品包装:" + item.pacing().pack());
            System.out.println("商品总价:" + item.price());
        }
    }
}

第六步:创建MealBuilder类,实际的创建者builder类负责创建Meal对象。

/**
 * 实际的builder类,用于创建Meal对象
 */
public class MealBuilder {
    // 创建一个蔬菜汉堡套餐
    public Meal prepareVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new VegBurger());
        meal.addItem(new Coke());

        return meal;
    }

    // 创建一个鸡肉汉堡套餐
    public Meal prepareChickenMeal() {
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());

        return meal;
    }
}

第七步:Demo进行测试

public class MealDemo {
    public static void main(String[] args) {
        // 获取创建者
        MealBuilder builder = new MealBuilder();
        // 使用创建者创建一个套餐
        Meal chickenMeal = builder.prepareChickenMeal();
        chickenMeal.showItems();
        float cost = chickenMeal.getCost();
        System.out.println(cost);
    }
}

(5)原型模式

  1. 在系统中需要创建大量相同或相似的对象。
  2. 核心思想:通过拷贝指定的原型对象,创建和该对象一样的新对象。(克隆指定对象)
  3. 需要拷贝的原型类必须实现Cloneable接口(该接口没有任何内容,一个标记接口,给实现类一个实现标记),重写Object类中的clone方法。(只有当一个类实现Cloneable接口过后,才能调用重写自Object中的clone方法,否则会抛出CloneNotSupportedException异常

1. 浅拷贝

  1. 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值给新对象
  2. 当成员变量时引入数据类型时,浅拷贝复制的是引用数据类型的地址。这种情况下,当拷贝出来的类修改引用类型的成员变量,会导致所有拷贝的类发生变化。
/**
 * 原型模式:克隆要实现Cloneable接口,重写Object中的clone方法
 */
public class Person implements Cloneable{
    private String name;
    private String job;

    public Person(String name, String job) {
        this.name = name;
        this.job = job;
    }

    public String getName() {
        return name;
    }

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

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    // 重写Object中的clone方法
    @Override
    protected Object clone() {
        Person person = null;
        try {
            // 克隆一个类
            person = (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                '}';
    }
}

测试

public class Demo {
    public static void main(String[] args) {
        // 新建一个Person类
        Person person = new Person("小张", "程序员");
        // 克隆一个Person对象
        Person clonePerson = (Person) person.clone();
        clonePerson.setName("小王");

        System.out.println(person);
        System.out.println(clonePerson);
        
		// Person{name='小张', job='程序员'}
		// Person{name='小王', job='程序员'}
    }
}

2. 深拷贝

  1. 在Person类中添加一个引用数据类型Computer(也需要重写Cloneable接口)。
public class Computer implements Cloneable {
    private String color;

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

    public String getColor() {
        return color;
    }

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

    @Override
    public String toString() {
        return "Computer{" +
                "color='" + color + '\'' +
                '}';
    }

    // 重写clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

/**
 * 原型模式:克隆要实现Cloneable接口,重写Object中的clone方法
 */
public class Person implements Cloneable {
    private String name;
    private String job;
    private Computer computer;

    public Person(String name, String job, Computer computer) {
        this.name = name;
        this.job = job;
        this.computer = computer;
    }

    // ...get、set方法

    @Override
    protected Object clone() {
        Person person = null;
        try {
            // 克隆一个对象
            person = (Person) super.clone();
            // 深拷贝引用类型
            person.computer = (Computer) this.computer.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return person;
    }

    // ...toString()
}

测试:

public class Demo {
    public static void main(String[] args) {
        // 新建一个Person类
        Person person = new Person("小张", "程序员", new Computer("黑色"));
        // 克隆一个Person对象
        Person clonePerson = (Person) person.clone();
        clonePerson.setComputer(new Computer("白色"));

        System.out.println(person);
        System.out.println(clonePerson);
    }
}

该方法有一个缺陷:需要单独处理所有要克隆的类中的引用类型。

序列化实现深拷贝(推荐)

  1. Person类和Computer类实现序列化接口Serializable接口,Computer类加入一个序列化ID。
  2. 使用序列化和反序列的方式,实现深拷贝。
public class Person implements Serializable {
	// 使用序列化的方式:对Person对象进行深拷贝
    public Person deepClone() {
        ByteArrayOutputStream bos = null;
        ByteArrayInputStream bis = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;

        try {
            // 创建字节数组流
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);

            // 序列化对象
            oos.writeObject(this);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            // 反序列化对象
            return (Person) ois.readObject();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            // 关闭流
            if(bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bis != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(oos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(ois != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return null;
    }
}

测试:

public class Demo {
    public static void main(String[] args) {
        // 新建一个Person类
        Person person = new Person("小张", "程序员", new Computer("黑色"));
        // 使用序列化的方式,进行深拷贝
        Person clonePerson = person.deepClone();
        clonePerson.setName("小李");
        clonePerson.getComputer().setColor("白色");

        System.out.println(person);
        System.out.println(clonePerson);
    }
}

(6)适配器模式

  1. 适配器模式(Adapter pattern)是作为两个不相同接口之间的桥梁。
  2. 将一个类的接口转化为一个接口。
    在这里插入图片描述
  3. 播放器AudioPlayer默认只能播放mp3类型。需要加入一个播放的适配器类,用户播放其他类型。

第一步:创建一个媒体播放接口MediaPlayer

/**
 * 播放器:默认只能播放mp3
 */
public interface MediaPlayer {
    void play(String audioType, String filename);
}

第二步:创建一个扩展媒体的播放接口AdvanceMediaPlayer,用于播放其他类型

/**
 * 扩展播放的接口:可以播放其他音频或视频
 */
public interface AdvanceMediaPlayer {
    void playOther(String audioType, String filename);
}

第三步:创建扩展媒体播放器的实现类Mp4Player

/**
 * MP4的播放器,实现扩展播放的接口
 */
public class Mp4Player implements AdvanceMediaPlayer {
    @Override
    public void playOther(String audioType, String filename) {
        System.out.println("播放" + audioType + ":" + filename);
    }
}

第四步:创建适配器类MediaAdapter,用于播放其他类型

/**
 * 播放的适配器
 */
public class MediaAdapter implements MediaPlayer{
    // 加入一个扩展播放器
    private AdvanceMediaPlayer advanceMediaPlayer;

    @Override
    public void play(String audioType, String filename) {
        if(audioType.equals("mp4")) {
            // 如果是播放其他就播放其他
            advanceMediaPlayer = new Mp4Player();
            advanceMediaPlayer.playOther("mp4", filename);
        }
    }
}

第五步:创建播放器类AudioPlayer

/**
 * 音频播放器
 */
public class AudioPlayer implements MediaPlayer{
    // 加入一个适配器
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String filename) {
        // 默认播放mp3
        if(audioType.equals("mp3")) {
            System.out.println("播放mp3:" + filename);
        } else if (audioType.equals("mp4")) {
            // 播放其他就使用一个适配器进行播放
            mediaAdapter = new MediaAdapter();
            mediaAdapter.play("mp4", filename);
        }
    }
}


(7)桥接模式

  1. 桥接(Bridge)是用于把抽象化与实现化解耦。它提供抽象化和实现化之间的桥接结构,来实现二者解耦。
  2. 一个桥接接口,使得实体类的功能能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。
    在这里插入图片描述

第一步:新建一个桥接接口DrawAPI

/**
 * 创建一个桥接接口:用于画各种不同颜色的圆
 */
public interface DrawAPI {
    void drawCircle(int radius, int x, int y);
}

第二步:创建桥接接口的实现类RedCircleBlueCircle

public class RedCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("drawing red circle [radius: " + radius + ",x: " + x + ",y: " + y + "]");
    }
}
public class BlueCircle implements DrawAPI {
    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("drawing blue circle [radius: " + radius + ",x: " + x + ",y: " + y + "]");
    }
}

第三步:创建抽象的形状Shape

/**
 * 创建一个抽象的形状类
 */
public abstract class Shape {
    DrawAPI drawAPI;

    public Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }

    public abstract void draw();
}

第四步:创建具体的形状类Circle

public class Circle extends Shape {
    private int x, y, radius;

    public Circle(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public void draw() {
        drawAPI.drawCircle(radius, x, y);
    }
}

第五步:创建Demo

public class Demo {
    public static void main(String[] args) {
        Shape redCircle = new Circle(2,2,4, new RedCircle());
        Shape blueCircle = new Circle(7,7,5, new BlueCircle());

        redCircle.draw();
        blueCircle.draw();
    }
}

(8)过滤器模式

  1. 过滤器模式(Filter Pattern)或标准模式(Criteria Pattern),允许开发人员使用不同的标准来过滤一组对象

    第一步:创建需要过滤的类Person
/**
 * 过滤的对象
 */
public class Person {
    private String name;
    private String gender;
    // 婚姻状况
    private String maritalStatus;

    public Person() {
    }

    public Person(String name, String gender, String maritalStatus) {
        this.name = name;
        this.gender = gender;
        this.maritalStatus = maritalStatus;
    }

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getMaritalStatus() {
        return maritalStatus;
    }

    public void setMaritalStatus(String maritalStatus) {
        this.maritalStatus = maritalStatus;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", maritalStatus='" + maritalStatus + '\'' +
                '}';
    }
}

第二步:创建一个过滤接口Criteria

/**
 * 创建过滤的接口
 */
public interface Criteria {
    List<Person> filter(List<Person> persons);
}

第三步:创建过滤接口的实现类:过滤男CriteriaMale、女CriteriaFemale和单身CriteriaSingle

/**
 * 男过滤器
 */
public class CriteriaMale implements Criteria {
    @Override
    public List<Person> filter(List<Person> persons) {
        List<Person> maleList = new ArrayList<>();
        // 遍历进行过滤
        for(Person p : persons) {
            String gender = p.getGender();
            if(gender.equals("male")) {
                maleList.add(p);
            }
        }
        return maleList;
    }
}

/**
 * 女过滤器
 */
public class CriteriaFemale implements Criteria {
    @Override
    public List<Person> filter(List<Person> persons) {
        List<Person> femaleList = new ArrayList<>();
        for(Person p : persons) {
            String gender = p.getGender();
            if(gender.equals("female")) {
                femaleList.add(p);
            }
        }
        return femaleList;
    }
}

/**
 * 单身过滤器
 */
public class CriteriaSingle implements Criteria{
    @Override
    public List<Person> filter(List<Person> persons) {
        List<Person> personList = new ArrayList<>();
        for(Person p : persons) {
            if(p.getMaritalStatus().equals("single")) {
                personList.add(p);
            }
        }
        return personList;
    }
}

第四步:创建组合过滤器AndCriteria、或过滤器OrCriteria

/**
 * 组合过滤器
 */
public class AndCriteria implements Criteria {
    // 使用两个过滤器,进行组合过滤
    private Criteria criteria;
    private Criteria otherCriteria;

    public AndCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }

    @Override
    public List<Person> filter(List<Person> persons) {
        // 先进行过滤
        List<Person> list = criteria.filter(persons);
        // 在进行第二次过滤
        return otherCriteria.filter(list);
    }
}

/**
 * 相当于 or
 */
public class OrCriteria implements Criteria{
    private Criteria criteria;
    private Criteria otherCriteria;

    public OrCriteria(Criteria criteria, Criteria otherCriteria) {
        this.criteria = criteria;
        this.otherCriteria = otherCriteria;
    }

    @Override
    public List<Person> filter(List<Person> persons) {
        List<Person> personList = criteria.filter(persons);
        List<Person> list = otherCriteria.filter(persons);

        for(Person p : list) {
            // 将两个过滤之后进行合并
            // 判断是否包含
            if(!personList.contains(p)) {
                personList.add(p);
            }
        }
        return personList;
    }
}

第五步:创建Demo

public class Demo {
    public static void main(String[] args) {
        Person p1 = new Person("john", "male", "single");
        Person p2 = new Person("tom", "male", "married");
        Person p3 = new Person("laura", "female", "single");
        Person p4 = new Person("bobby", "female", "married");

        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);

        // 创建过滤器
        CriteriaMale criteriaMale = new CriteriaMale();
        CriteriaFemale criteriaFemale = new CriteriaFemale();
        CriteriaSingle criteriaSingle = new CriteriaSingle();
        AndCriteria andCriteria = new AndCriteria(criteriaFemale, criteriaSingle);
        OrCriteria orCriteria = new OrCriteria(criteriaMale, criteriaSingle);

        System.out.println("========过滤男士========");
        print(criteriaMale.filter(list));

        System.out.println("========过滤女士========");
        print(criteriaFemale.filter(list));

        System.out.println("========过滤单身女士========");
        print(andCriteria.filter(list));

        System.out.println("========过滤男士或单身========");
        print(orCriteria.filter(list));
    }

    // 打印方法
    private static void print(List<Person> list) {
        for(Person p : list) {
            System.out.println(p);
        }
    }
}

(9)组合模式

  1. 组合模式(Composite Pattern),又叫部分整体模式。把一组相似的对象当作一个单一的对象。
  2. 组合模式依据树型结构来组合对象,用来表示部分以及整体的层次。
  3. 将对象组合成树型结构以表示”部分-整体“之间的关系,使得用户对单个对象和组合对象的使用具有一致性。
    在这里插入图片描述

第一步:创建一个员工对象Employee

/**
 * 员工类
 */
public class Employee {
    private String name;
    private String dept;
    private float salary;
    private List<Employee> subordinates;

    public Employee(String name, String dept, float salary) {
        this.name = name;
        this.dept = dept;
        this.salary = salary;
        subordinates = new ArrayList<>();
    }

    // 添加下属
    public void add(Employee employee) {
        subordinates.add(employee);
    }

    // 删除下属
    public void remove(Employee e) {
        subordinates.remove(e);
    }

    // 获得下属
    public List<Employee> getSubordinates() {
        return subordinates;
    }

    @Override
    public String toString() {
        return "Employee[" + "name:" + name + ", dept:" + dept + ", salary:" + salary + "]";
    }
}

public class Demo {
    public static void main(String[] args) {
        // 创建一个CEO
        Employee CEO = new Employee("john", "CEO", 10000);

        // 创建两个负责人
        Employee headSalary = new Employee("Robert", "head sales", 5000);
        Employee headMarketing = new Employee("michel", "head marketing", 5000);

        CEO.add(headSalary);
        CEO.add(headMarketing);

        // 创建四个员工
        Employee clerk1 = new Employee("laura", "marketing", 4000);
        Employee clerk2 = new Employee("bob", "marketing", 4000);

        Employee salesExecutive1 = new Employee("richard", "sales", 4500);
        Employee salesExecutive2 = new Employee("rob", "sales", 4500);

        headMarketing.add(clerk1);
        headMarketing.add(clerk2);
        headSalary.add(salesExecutive1);
        headSalary.add(salesExecutive2);

        //打印CEO
        System.out.println(CEO);
        // 遍历CEO的下属
        for(Employee head : CEO.getSubordinates()) {
            System.out.println(head);

            // 遍历负责人的下属
            for(Employee e : head.getSubordinates()) {
                System.out.println(e);
            }
        }
    }
}

(10)装饰者模式

  1. 装饰者模式(Decorator Pattern)允许向一个现有对象添加一个新的功能,同时又不改变其结构。
  2. 创建一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
  3. 一般我们扩展一个类经常使用继承方式实现,装饰者模式比子类更加灵活。
  4. 用于扩展一个类的功能,动态的增加和撤销。(可代替继承)
  5. 实现:我们将创建一个Shape接口以及实现该接口的实体类。然后创建一个实现Shape接口的抽象装饰类ShapeDecorator,并把Shape作为它的实例变量。RedShapeDecorator是实现ShapeDecorator的实体类。
    在这里插入图片描述

第一步:创建Shape接口以及该接口的实现类。

public interface Shape {
    void draw();
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Circle: draw circle");
    }
}

第二步:创建装饰者ShapeDecorator抽象类,以及它的实现子类RedShapeDecorator。

/**
 * 装饰者抽象类
 */
public abstract class ShapeDecorator implements Shape {
    protected Shape shape;

    public ShapeDecorator(Shape shape) {
        this.shape = shape;
    }

    public abstract void draw();
}

/**
 * 装饰者抽象类的实现类
 */
public class RedShapeDecorator extends ShapeDecorator {

    public RedShapeDecorator(Shape shape) {
        super(shape);
    }

    @Override
    public void draw() {
        shape.draw();
        setRed(shape);
    }

    // 增加方法
    public void setRed(Shape shape) {
        System.out.println("draw red!!!!");
    }
}

第三步:进行测试

public class Demo {
    public static void main(String[] args) {
        // 新建一个形状
        Shape circle = new Circle();
        // 创建一个装饰者类
        ShapeDecorator decorator = new RedShapeDecorator(circle);

        // 未经装饰的方法调用
        circle.draw();
        // 装饰者之后的方法调用
        decorator.draw();
    }
}

(11)外观模式

  1. 外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供一个客户端可以访问系统的接口。
  2. 降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
    在这里插入图片描述
  3. 客户端只调用外观类,内部实现细节以及复杂性进行隐藏。

第一步:创建接口以及它的实现类

public interface Shape {
    void draw();
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("circle: draw");
    }
}
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("rectangle: draw");
    }
}
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("square: draw");
    }
}

第二步:创建外观类ShapeMaker

/**
 * 外观类,实现对这些复杂类的调用
 */
public class ShapeMaker {
    private Shape circle;
    private Shape rectangle;
    private Shape square;

    public ShapeMaker() {
        circle = new Circle();
        rectangle = new Rectangle();
        square = new Square();
    }

    // 分别对方法进行调用
    public void drawCircle() {
        circle.draw();
    }

    public void drawRectangle() {
        rectangle.draw();
    }

    public void drawSquare() {
        square.draw();
    }
}

第三步:创建Demo测试

public class FacadePatternDemo {
    public static void main(String[] args) {
        // 使用外观类调用方法
        ShapeMaker shapeMaker = new ShapeMaker();

        shapeMaker.drawCircle();
        shapeMaker.drawRectangle();
        shapeMaker.drawSquare();
    }
}

(12)享元模式

  1. 享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。
  2. 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
  3. 核心思想:使用一个Map将对象存起来,使用时先向Map获取对象,获取不到在创建。

第一步:创建接口以及实现类

public interface Shape {
    void draw();
}
public class Circle implements Shape {
    private int x;
    private int y;
    private int radius;
    private String color;

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

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

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

    public void setRadius(int radius) {
        this.radius = radius;
    }

    @Override
    public void draw() {
        System.out.printf("Circle draw [color: %s, x:%d, y:%d, radius:%d]\n", color, x, y, radius);
    }
}

第二步:创建一个享元工厂,使用Map存储对象

/**
 * 享元工厂,存在该对象就直接使用,不存在就创建
 */
public class ShapeFactory {
    // 使用一个map判断对象是否已经存在
    private static final Map<String, Shape> map = new HashMap<>();

    public static Shape getCircle(String color) {
        // 尝试获取对象
        Circle circle = (Circle) map.get(color);

        if(circle == null) {
            // 等于null,说明该对象不存在
            circle = new Circle(color);
            map.put(color, circle);
            System.out.println("创建了" + color + "颜色的圆!");
        }

        return circle;
    }
}

第三步:创建测试Demo

public class FlyWeightPatternDemo {
    public static void main(String[] args) {
        // 第一次获取一个红色的圆,获取不到,将会创建
        Circle redCircle1 = (Circle) ShapeFactory.getCircle("red");
        redCircle1.setX(10);
        redCircle1.setY(20);
        redCircle1.setRadius(50);
        redCircle1.draw();

        // 第二次获取到的是第一个创建的圆
        Circle redCircle2 = (Circle) ShapeFactory.getCircle("red");
        redCircle2.draw();
    }
}

(13)代理模式

  1. 代理模式(Proxy Pattern),一个类代表另一个类的功能。
  2. 主要解决在直接访问对象时带来的问题。比如要访问的对象在远程的机器上。
  3. 如:远程代理、虚拟代理、防火墙代理。
    在这里插入图片描述

第一步:创建一个图片接口以及实现类

public interface Image {
    void display();
}
/**
 * 真实图片类
 */
public class RealImage implements Image{
    private String filename;

    public RealImage(String filename) {
        loadImage(filename);
        this.filename = filename;
    }

    @Override
    public void display() {
        System.out.println("read image display " + filename);
    }

    // 加载图片的方法
    public void loadImage(String filename) {
        System.out.println("loading " + filename);
    }
}

第二步:创建一个代理的类,访问图片时不用重复加载

/**
 * 代理图片对象:减少真实图片的加载
 */
public class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;

    public ProxyImage(String filename) {
        this.filename = filename;
    }

    @Override
    public void display() {
        if(realImage == null) {
            realImage = new RealImage(filename);
        }

        realImage.display();
    }
}

第三步:创建测试类

public class ProxyDemo {
    public static void main(String[] args) {
        // 使用代理类获取图片
        Image proxyImage = new ProxyImage("heat.jpg");
        // 第一次加载需要创建真实图片类
        proxyImage.display();

        // 第二次不需要
        proxyImage.display();
    }
}

(14)责任链模式

  1. 责任链模式(Chain Of Responsibility Pattern)为请求创建了一个接收者对象的链。
  2. 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,它会把相同的请求传给下一个接收者。
  3. 避免请求者和接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且连着这条链传递请求,直到有对象处理它为止。
  4. 我们创建抽象类 AbstractLogger,带有详细的日志记录级别。然后我们创建三种类型的记录器,都扩展了 AbstractLogger。每个记录器消息的级别是否属于自己的级别,如果是则相应地打印出来,否则将不打印并把消息传给下一个记录器。

在这里插入图片描述
第一步:创建抽象记录日志类

/**
 * 抽象的记录器类
 */
public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;

    // 责任链中的下一个元素
    protected AbstractLogger nextLogger;

    public void setNextLogger(AbstractLogger nextLogger) {
        this.nextLogger = nextLogger;
    }

    // 根据日志级别输出日志
    public void logMessage(int level, String message) {
        // 判断是否能够打印当前日志
        if(this.level <= level) {
            // 打印日志
            write(message);
            // 输出日志之后,低级别的就不再重复打印
            return;
        }

        // 向责任链下面传递
        if(nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }

    // 打印日志的抽象方法
    protected abstract void write(String message);
}

第二步:创建不同级别的日志类

/**
 * 控制台打印日志
 */
public class ConsoleLogger extends AbstractLogger{
    // 传入日志的级别
    public ConsoleLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("console logger: " + message);
    }
}

/**
 * 文件记录日志
 */
public class FileLogger extends AbstractLogger {
    public FileLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("file logger: " + message);
    }
}

/**
 * 错误记录日志
 */
public class ErrorLogger extends AbstractLogger {
    public ErrorLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("error logger: " + message);
    }
}

第三步:创建测试Demo

public class ChainPatternDemo {
    // 获得责任链方法
    public static AbstractLogger getChainOfLogger() {
        AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
        AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
        AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

        // 设置责任链
        errorLogger.setNextLogger(fileLogger);
        fileLogger.setNextLogger(consoleLogger);

        return errorLogger;
    }

    public static void main(String[] args) {
        // 获取责任链打印日志
        AbstractLogger chainOfLogger = getChainOfLogger();

        // 最低日志
        chainOfLogger.logMessage(AbstractLogger.INFO, "控制台输出!");
        chainOfLogger.logMessage(AbstractLogger.DEBUG, "debug输出!");
        // 最高级别日志
        chainOfLogger.logMessage(AbstractLogger.ERROR, "错误输出!");
    }
}

(15)命令模式

  1. 命令模式(Command Pattern),请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
  2. 将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。
  3. 命令模式结构示意图:
    在这里插入图片描述
  4. 我们首先创建作为命令的接口 Order,然后创建作为请求的 Stock 类。实体命令类 BuyStock 和 SellStock,实现了 Order 接口,将执行实际的命令处理。创建作为调用对象的类 Broker,它接受订单并能下订单。Broker 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。CommandPatternDemo 类使用 Broker 类来演示命令模式。

在这里插入图片描述

第一步:创建一个命令接口Order订单接口

public interface Order {
    void execute();
}

第二步:创建一个请求类Stock库存类

/**
 * 库存类
 */
public class Stock {
    private String name = "ABC";
    private int quantity = 10;

    // 买入库存方法
    public void buy() {
        System.out.println("stock [ name : " + name + ", quantity : " + quantity + "] bought");
    }

    // 卖出库存方法
    public void sell() {
        System.out.println("stock [ name : " + name + ", quantity : " + quantity + "] sold");
    }
}

第三步:创建Order接口的实现类BuyStock和SellStock代表不同的行为

/**
 * 买入库存
 */
public class BuyStock implements Order {
    private Stock abcStock;

    public BuyStock(Stock abcStock) {
        this.abcStock = abcStock;
    }

    @Override
    public void execute() {
        abcStock.buy();
    }
}
/**
 * 销售库存
 */
public class SellStock implements Order {
    private Stock abcStock;

    public SellStock(Stock abcStock) {
        this.abcStock = abcStock;
    }

    @Override
    public void execute() {
        abcStock.sell();
    }
}

第四步:创建一个经理类Broker对Order订单进行管理

/**
 * 经理类,下订单
 */
public class Broker {
    private List<Order> orderList = new ArrayList<>();

    // 加入一个订单
    public void takeOrder(Order order) {
        orderList.add(order);
    }

    // 执行订单方法
    public void palceOrder() {
        // 遍历订单
        for(Order o : orderList) {
            // 执行订单
            o.execute();
        }

        // 执行完之后清除所有订单
        orderList.clear();
    }
}

第五步:创建测试Demo

public class CommandPatternDemo {
    public static void main(String[] args) {
        // 创建一个经理人
        Broker broker = new Broker();
        // 创建一个仓库
        Stock stock = new Stock();

        // 加入不同的订单
        broker.takeOrder(new BuyStock(stock));
        broker.takeOrder(new SellStock(stock));

        // 执行所有的订单
        broker.palceOrder();
    }
}

(16)解释器模式

  1. 解释器模式(Interpreter Pattern),实现一个表达式接口,该接口解释一个特定的上下文。该接口解释一个特定的上下文,这种模式被用在SQL解析、符号引擎处理等。
  2. 对于一些固定文法构建一个解释句子的解释器。
  3. 我们将创建一个接口 Expression 和实现了 Expression 接口的实体类。定义作为上下文中主要解释器的 TerminalExpression 类。其他的类 OrExpression、AndExpression 用于创建组合式表达式。InterpreterPatternDemo,我们的演示类使用 Expression 类创建规则和演示表达式的解析。

在这里插入图片描述

第一步:定义一个解释器接口

/**
 * 解释器接口
 */
public interface Expression {
    boolean interpret(String context);
}

第二步:定义一个解释器接口的实现类

/**
 * 终端解释器:实现解释器接口
 */
public class TerminalExpression implements Expression{
    private String data;

    // 传入需要解释的数据
    public TerminalExpression(String data) {
        this.data = data;
    }

    @Override
    public boolean interpret(String context) {
        // 返回解释结果
        return context.contains(data);
    }
}

第三步:定义组合解释器

/**
 * 两个解释器,一个返回true就为true
 */
public class OrExpression implements Expression{
    // 定义两个解释器
    private Expression e1;
    private Expression e2;

    public OrExpression(Expression e1, Expression e2) {
        this.e1 = e1;
        this.e2 = e2;
    }

    @Override
    public boolean interpret(String context) {
        return e1.interpret(context) || e2.interpret(context);
    }
}
/**
 * 两个解释器都为true,才为true
 */
public class AndExpression implements Expression {
    private Expression e1;
    private Expression e2;

    public AndExpression(Expression e1, Expression e2) {
        this.e1 = e1;
        this.e2 = e2;
    }

    @Override
    public boolean interpret(String context) {
        return e1.interpret(context) && e2.interpret(context);
    }
}

第四步:定义测试Demo

public class InterpreterPatternDemo {
    // 定义一个解释规则:tom和jack都是男
    public static Expression getMaleExpression() {
        TerminalExpression tom = new TerminalExpression("tom");
        TerminalExpression jack = new TerminalExpression("jack");

        return new OrExpression(tom, jack);
    }

    // 定义一个规则:julie已婚
    public static Expression getMarriedExpression() {
        TerminalExpression julie = new TerminalExpression("julie");
        TerminalExpression married = new TerminalExpression("married");

        return new AndExpression(julie, married);
    }

    public static void main(String[] args) {
        Expression maleExpression = getMaleExpression();
        System.out.println("tom is male? " + maleExpression.interpret("tom"));

        Expression marriedExpression = getMarriedExpression();
        System.out.println("julie is married? " + marriedExpression.interpret("julie married"));
    }
}

(17)迭代器模式

  1. 迭代器模式(Iterator Pattern)用于顺序访问集合对象的元素,不需要知道集合对象的底层实现。
  2. 我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。IteratorPatternDemo,我们的演示类使用实体类 CommonContains 来打印 CommonContains 中存储的元素。

第一步:创建迭代器接口和容器接口

/**
 * 迭代器接口
 */
public interface Iterator<T> {
    boolean hasNext();
    T next();
}
/**
 * 容器接口
 */
public interface Container<T> {
    // 获取迭代对象
    Iterator<T> getIterator();
}

第二步:创建容器接口的实现类

/**
 * 容器的实现类:一个普通的容器
 */
public class CommonContainer<T> implements Container<T> {
    private T[] values;

    public CommonContainer(T[] values) {
        this.values = values;
    }

    @Override
    public Iterator<T> getIterator() {
        return new CommonIterator<>();
    }

    private class CommonIterator<T> implements Iterator<T> {
        // 定义一个指针
        int index;

        // 判断是否有下一个
        @Override
        public boolean hasNext() {
            return index < values.length;
        }

        // 获取下一个元素
        @Override
        public T next() {
            if(this.hasNext()) {
                return (T) values[index++];
            }
            return null;
        }
    }
}

第三步:创建Demo测试

public class IteratorPatternDemo {
    public static void main(String[] args) {
        String[] names = {"tom", "jack", "jerry"};

        // 通用容器
        Container<String> container = new CommonContainer<>(names);
        // 获得容器的迭代器
        Iterator<String> iterator = container.getIterator();
        // 获取每个元素
        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

(18)中介者模式

  1. 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。提供一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。
  2. 我们通过聊天室实例来演示中介者模式。实例中,多个用户可以向聊天室发送消息,聊天室向所有的用户显示消息。我们将创建两个类 ChatRoom 和 User。User 对象使用 ChatRoom 方法来分享他们的消息。
  3. MediatorPatternDemo,我们的演示类使用 User 对象来显示他们之间的通信。

在这里插入图片描述

第一步:创建一个User用户类

/**
 * 用户类
 */
public class User {
    private String name;

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

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

    public String getName() {
        return name;
    }

    public void showMessage(String message) {
        ChatRoom.showMessage(this, message);
    }
}

第二步:创建一个聊天中介类ChatRoom

/**
 * 创建一个聊天室作为中介类
 */
public class ChatRoom {
    public static void showMessage(User user, String message) {
        System.out.println("User[" + user.getName() + "] : " + message);
    }
}

第三步:创建Demo

public class MediatorPatternDemo {
    public static void main(String[] args) {
        User tom = new User("tom");
        User jerry = new User("jerry");
        tom.showMessage("hello jerry");
        jerry.showMessage("hi tom");
    }
}

(19)备忘录模式

  1. 备忘录模式(Memento Patern)保存一个对象的某种状态,必便在适当的时候恢复对象。
  2. 所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
  3. 应用实例: 1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。 4、IE 中的后退。 4、数据库的事务管理。

在这里插入图片描述

第一步:创建要被恢复的对象的状态

/**
 * 包含要被恢复对象的状态
 */
public class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

第二步:创建并在Memento中存储状态

/**
 * 在Memento对象中存储状态
 */
public class Originator {
    private String state;

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

    public String getState() {
        return state;
    }

    // 保存状态到Memento中
    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    // 获取Memento中的状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

第三步:从Memento中恢复对象的状态

/**
 * 从Memento中恢复对象的状态
 */
public class CareTaker {
    private List<Memento> mementoList = new ArrayList<>();

    // 添加需要恢复的状态
    public void add(Memento memento) {
        mementoList.add(memento);
    }

    // 获取状态,进行恢复
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

第四步:创建Demo

public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        CareTaker careTaker = new CareTaker();

        originator.setState("state #1");
        // 保存当前状态
        Memento memento1 = originator.saveStateToMemento();
        // 添加状态,以便恢复
        careTaker.add(memento1);

        originator.setState("state #2");
        Memento memento2 = originator.saveStateToMemento();
        careTaker.add(memento2);

        // 获取状态
        System.out.println("当前状态:" + originator.getState());
        System.out.println("第一次保存的状态:" + careTaker.get(0).getState());
        System.out.println("第二次保存的状态:" + careTaker.get(1).getState());

        // 恢复状态
        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("恢复后的当前状态:" + originator.getState());
    }
}

(20)观察者模式

  1. 当对象间存在一对多的关系时,使用观察者模式(Observer Pattern)。当一个对象被修改时,会自动通知依赖它的对象。
  2. 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  3. 观察者模式使用三个类 Subject、Observer 和 Client。Subject 对象带有绑定观察者到 Client 对象和从 Client 对象解绑观察者的方法。我们创建 Subject 类、Observer 抽象类和扩展了抽象类 Observer 的实体类。
  4. ObserverPatternDemo,我们的演示类使用 Subject 和实体类对象来演示观察者模式。

在这里插入图片描述

第一步:创建一个被观察的对象Subject类

/**
 * 创建实体类
 */
public class Subject {
    // 将所有的观察者都存放在List中,发生改变就通知它们
    private List<Observer> observers = new ArrayList<>();
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
        // 改变值的时候,就通知所有的观察者
        notifyAllObservers();
    }

    // 将观察者添加到list中
    public void attach(Observer observer) {
        observers.add(observer);
    }

    // 通知所有的观察者
    public void notifyAllObservers() {
        for (Observer o : observers) {
            // 调用观察者的update进行修改
            o.update();
        }
    }
}

第二步:创建观察者抽象类

/**
 * 观察者抽象类
 */
public abstract class Observer {
    // 需要观察的对象
    protected Subject subject;
    public abstract void update();
}

第三步:创建观察者的实现类

/**
 * 观察者实现类
 */
public class BinaryObserver extends Observer {
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
        // 转化为二进制
        System.out.println("Binary String: " + Integer.toBinaryString(subject.getState()));
    }
}
/**
 * 观察者实现类
 */
public class HexaObserver extends Observer {
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }

    @Override
    public void update() {
        // 转化为16进制
        System.out.println("Hex String: " + Integer.toHexString(subject.getState()));
    }
}

第四步:创建Demo

public class ObserverPatternDemo {
    public static void main(String[] args) {
        Subject subject = new Subject();
        // 新建两个观察者
        new BinaryObserver(subject);
        new HexaObserver(subject);

        // 改变subject的state,会通知观察者
        subject.setState(15);
        subject.setState(18);
    }
}

(21)状态模式

  1. 在状态模式中(State Pattern),类的行为是基于它的状态改变的。
  2. 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
  3. 我们将创建一个 State 接口和实现了 State 接口的实体状态类。Context 是一个带有某个状态的类。
  4. StatePatternDemo,我们的演示类使用 Context 和状态对象来演示 Context 在状态改变时的行为变化。
    在这里插入图片描述

第一步:创建一个状态接口State

/**
 * 状态接口
 */
public interface State {
    void doAction(Context context);
}

第二步:创建一个上下文类Context

public class Context {
    private State state;

    public State getState() {
        return state;
    }

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

第三步:创建State的实现类

public class StartState implements State{
    @Override
    public void doAction(Context context) {
        System.out.println("Player is in start state");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "Start State";
    }
}
public class StopState implements State {
    @Override
    public void doAction(Context context) {
        System.out.println("Player is in stop state");
        context.setState(this);
    }

    @Override
    public String toString() {
        return "stop state";
    }
}

第四步:创建Demo

public class StatePatternDemo {
    public static void main(String[] args) {
        Context context = new Context();

        // 新建一个状态
        StartState startState = new StartState();
        // 设置状态
        startState.doAction(context);
        // 获取当前状态
        State state = context.getState();
        System.out.println(state);
    }
}

(22)空对象模式

  1. 在空对象模式(Null Object Pattern)中,一个空对象取代 NULL 对象实例的检查。Null 对象不是检查空值,而是反应一个不做任何动作的关系。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。
  2. 我们将创建一个定义操作(在这里,是客户的名称)的 AbstractCustomer 抽象类,和扩展了 AbstractCustomer 类的实体类。工厂类 CustomerFactory 基于客户传递的名字来返回 RealCustomer 或 NullCustomer 对象。
  3. NullPatternDemo,我们的演示类使用 CustomerFactory 来演示空对象模式的用法。

在这里插入图片描述

第一步:创建一个抽象类

/**
 * 抽象的消费者类
 */
public abstract class AbstractCustomer {
    protected String name;
    // 是否为空
    public abstract boolean isNil();
    public abstract String getName();
}

第二步:创建实现类

/**
 * 真实的消费者类
 */
public class RealCustomer extends AbstractCustomer {
    public RealCustomer(String name) {
        this.name = name;
    }

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

    @Override
    public String getName() {
        return name;
    }
}
/**
 * 空对象的消费者类
 */
public class NullCustomer extends AbstractCustomer{
    @Override
    public boolean isNil() {
        return true;
    }

    @Override
    public String getName() {
        return "Not Available in Customer Database";
    }
}

第三步:创建工厂类

/**
 * 创建消费者的工厂类
 */
public class CustomerFactory {
    private static final String[] NAMES = {"tom","jerry","jack"};

    public static AbstractCustomer getCustomer(String name) {
        for(String s : NAMES) {
            // 如果能够找到名字就创建一个真实的对象
            if(name.equals(s)) {
                return new RealCustomer(name);
            }
        }

        return new NullCustomer();
    }
}

第四步:创建Demo

public class NullObjectPatternDemo {
    public static void main(String[] args) {
        // 能够获取到真实对象
        AbstractCustomer tom = CustomerFactory.getCustomer("tom");
        // 获取到自定义的一个空对象
        AbstractCustomer hanMeimei = CustomerFactory.getCustomer("HanMeimei");

        System.out.println(tom.getName());
        System.out.println(hanMeimei.getName());
    }
}

(23)策略模式

  1. 在策略模式(Strategy)中,一个类的行为或其算法可以在运行时更改
  2. 我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
  3. StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

在这里插入图片描述

第一步:创建一个策略接口

/**
 * 策略接口
 */
public interface Strategy {
    // 做运算的策略
    int doOperation(int num1, int num2);
}

第二步:创建接口的实现类

/**
 * 做加法的运算策略
 */
public class OperationAdd implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}
/**
 * 减法策略
 */
public class OperationSubsract implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}
/**
 * 乘法策略
 */
public class OperationMultiply implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1 * num2;
    }
}

第三步:创建一个Context类,统一调用运算

/**
 * 统一调用
 */
public class Context {
    private Strategy strategy;

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

    // 统一执行方法
    public int executStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

第四步:创建Demo

public class StrategyPatternDemo {
    public static void main(String[] args) {
        // 调用运算策略
        Context context = new Context(new OperationAdd());
        System.out.println("4 + 5 = " + context.executStrategy(4,5));

        context = new Context(new OperationMultiply());
        System.out.println("4 * 5 = " + context.executStrategy(4,5));
    }
}

(24)模板模式

  1. 模板模式(Template Pattern),一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现。但调用将以抽象类中定义的方法执行。
  2. 我们将创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
  3. VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。

在这里插入图片描述

第一步:创建一个Game抽象类

/**
 * 游戏抽象类
 */
public abstract class Game {
    abstract void init();
    abstract void start();
    abstract void end();

    public final void play() {
        // 初始化游戏
        init();
        // 开始游戏
        start();
        // 结束游戏
        end();
    }
}

第二步:创建抽象类的实现

public class BasketBall extends Game {
    @Override
    void init() {
        System.out.println("BasketBall init");
    }

    @Override
    void start() {
        System.out.println("BasketBall start");
    }

    @Override
    void end() {
        System.out.println("BasketBall end");
    }
}
public class FootBall extends Game {
    @Override
    void init() {
        System.out.println("FootBall init");
    }

    @Override
    void start() {
        System.out.println("FootBall start");
    }

    @Override
    void end() {
        System.out.println("FootBall end");
    }
}

第三步:创建Demo

public class TemplatePatternDemo {
    public static void main(String[] args) {
        Game basketball = new BasketBall();
        basketball.play();
        System.out.println();

        Game football = new FootBall();
        football.play();
    }
}

(25)访问者模式

  1. 访问者模式(Visitor Pattern),==使用一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。==根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
  2. 我们将创建一个定义接受操作的 ComputerPart 接口。Keyboard、Mouse、Monitor 和 Computer 是实现了 ComputerPart 接口的实体类。我们将定义另一个接口 ComputerPartVisitor,它定义了访问者类的操作。Computer 使用实体访问者来执行相应的动作。
  3. VisitorPatternDemo,我们的演示类使用 Computer、ComputerPartVisitor 类来演示访问者模式的用法。

在这里插入图片描述

第一步:创建一个元素接口

/**
 * 元素接口
 */
public interface ComputerPart {
    // 需要一个访问者
    void accept(ComputerPartVisitor computerPartVisitor);
}

第二步:创建访问者接口

/**
 * 访问者接口
 */
public interface ComputerPartVisitor {
    void visit(Computer computer);
    void visit(Mouse mouse);
    void visit(Keyboard keyboard);
    void visit(Monitor monitor);
}

第三步:创建元素接口的实现类

public class Monitor implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}
public class Mouse implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}
public class Keyboard implements ComputerPart {
    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        computerPartVisitor.visit(this);
    }
}
/**
 * 电脑实体类
 */
public class Computer implements ComputerPart {
    ComputerPart[] parts;

    public Computer() {
        parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
    }

    @Override
    public void accept(ComputerPartVisitor computerPartVisitor) {
        for(ComputerPart part : parts) {
            part.accept(computerPartVisitor);
        }
        computerPartVisitor.visit(this);
    }
}

第四步:创建访问者的实现类

public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
    @Override
    public void visit(Computer computer) {
        System.out.println("computer display");
    }

    @Override
    public void visit(Mouse mouse) {
        System.out.println("computer mouse");
    }

    @Override
    public void visit(Keyboard keyboard) {
        System.out.println("computer keyboard");
    }

    @Override
    public void visit(Monitor monitor) {
        System.out.println("computer monitor");
    }
}

第五步:创建Demo

public class VisitorPatternDemo {
    public static void main(String[] args) {
        ComputerPart computer = new Computer();
        computer.accept(new ComputerPartDisplayVisitor());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值