软件工程 | 设计模式Java版

631 阅读21分钟

前置知识

面向对象(oo) => 功能模块[设计模式+算法(数据结构)] => 框架[多种设计模式] => 架构[服务器集群]

高内聚低耦合:重用性、可读性、可扩展性、可靠性

设计原则:

  • 单一职责原则(Principle of Single Responsibility)- 交通工具例子
  • 接口隔离原则(Interface Segregation Principle)- A-I12-B/C-I13-D例子
  • 依赖倒转原则(Dependence Inversion Principle)- 接口方法/构造方法/setter方法传递 Person-IReceiver-Email/Weinxin例子和电视问题
  • 里氏替换原则(Liskov Substitution Principle)- 解决子类修改父类方法问题-原有继承去掉(依赖/聚合/组合)
  • 开放封闭原则(Open Close Principle)- 扩展开放(提供方),修改关闭(使用方)-GF.DrawShap/abstract Shape.draw();/yuan sanjiao例子
  • 迪米特法则(最少知道原则)(Demeter Principle)- A依赖B(依赖关系B不能是局部变量)
  • 合成复用原则 - 使用合成/聚合的方式,而不是使用继承
//依赖倒转案例
//第一种方式:接口传递
//开关的接口
interface IOpenAndClose {
    public void opoen(ITV tv);//抽象方法,接收接口
}

interface ITV {//ITV接口

    public void play();
}

//实现接口
class OpenAndColse implements IOpenAndClose {
    @Override
    public void opoen(ITV tv) {
        tv.play();
    }
}
//方式二:构造方法传递
interface IOpenAndClose {
    public void open();//抽象方法
}

interface ITV {//ITV接口

    public void play();
}

class OpenAndClose implements IOpenAndClose {
    public ITV tv;//成员

    public OpenAndClose(ITV tv) {//构造方法
        this.tv = tv;
    }

    @Override
    public void open() {
        this.tv.play();
    }
}
//方式三:setter方法传递
interface IOpenAndClose {
    public void open();//抽象方法
}

interface ITV {//ITV接口

    public void play();
}

class OpenAndClose implements IOpenAndClose {
    private ITV tv;

    public void setTv(ITV tv) {
        this.tv = tv;
    }

    @Override
    public void open() {
        this.tv.play();
    }
}

UML类图——Unified modeling language UML(统一建模语言),是一种用于软件系统分析和设计的语言工具。【依赖、泛化(继承)、实现、关联、聚合与组合UML类图.png

设计模式

分类.png

单例模式

1)饿汉式(静态常量)

步骤如下:
1)构造器私有化(防止new)
2)类的内部创建对象
3)向外暴露一个静态的公共方法。getInstance
4)代码实现

public class SingletonTest1 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//饿汉式(静态常量)
class Singleton {

    //1、构造器私有化,外部不能new
    private Singleton() {}

    //2、本类内部创建对象实例
    private final static Singleton instance = new Singleton();

    //3、提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}

在类装载的时候就完成实例化,避免线程同步问题。没有达到Lazy Loading的效果,会造成内存浪费。
注意类加载时机:new一个对象、访问类的静态成员、调用类的静态方法、给类的静态成员赋值、程序的主类、反射

2)饿汉式(静态代码块)

public class SingletonTest2 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//饿汉式(静态代码块)
class Singleton {

    //1、构造器私有化,外部不能new
    private Singleton() {}

    //2、静态代码块
    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    //3、提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance() {
        return instance;
    }
}

3)懒汉式(线程不安全)

public class SingletonTest3 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//懒汉式(线程不安全)
class Singleton {

    private static Singleton instance;
    
    private Singleton() {}

    //提供一个静态的公有方法,当使用到该方法时,才去创建instance
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

起到了Lazy Loading的效果,但是只能在单线程下使用。【不建议使用】

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

public class SingletonTest4 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//懒汉式(线程安全,同步方法)
class Singleton {

    private static Singleton instance;

    private Singleton() {}

    //提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

加锁synchronized,方法进行同步效率太低。【不推荐使用】

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

public class SingletonTest5 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//懒汉式(线程安全,同步代码块)
class Singleton {

    private static Singleton instance;

    private Singleton() {}

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

这种同步并不能起到线程同步的作用。【不能使用】

6)双重检查

public class SingletonTest6 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//双重检查
class Singleton {

    private static volatile Singleton instance;

    private Singleton() {}

    //提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
    public static synchronized Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile和synchronized,线程安全;延迟加载;效率较高。【推荐使用】

7)静态内部类

public class SingletonTest7 {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

//静态内部类
class Singleton {

    private Singleton() {}

    //写一个静态内部类,该类中有一个静态属性Singleton
    private static class singleInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    //提供一个静态的公有方法,直接返回对象
    public static synchronized Singleton getInstance() {
        return singleInstance.INSTANCE;
    }
}

采用了类装载的机制来保证初始化实例时只有一个线程。静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。【推荐使用】

8)枚举

public class SingletonTest8 {
    public static void main(String[] args) {
        Singleton instance = Singleton.INSTANCE;
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance == instance2);
        instance.sayOK();
    }
}

//使用枚举,可以实现单例
enum Singleton {
    INSTANCE;//属性
    public void sayOK() {
        System.out.println("ok");
    }
}

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。【推荐使用】

单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或义件的对象(比如数据源、session工厂等)

工厂模式

在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。

简单工厂模式

定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)。 简单工厂模式.png

//把Pizza类做成抽象类
public abstract class Pizza {
    private String name;//名字

    //准备原材料,不同的披萨不一样,因此,我们做成抽象方法
    public abstract void prepare();

    public void bake() {
        System.out.println(name + " baking");
    }

    public void cut() {
        System.out.println(name + " cutting");
    }

    public void box() {
        System.out.println(name + " boxing");
    }

    public void setName(String name) {
        this.name = name;
    }
}
// GreekPizza CheessPizza ... 披萨种类
public class GreekPizza extends Pizza {
    @Override
    public void prepare() {
        System.out.println("给GreekPizza准备原材料");
    }
}
public class OrderPizza {
    //构造方法
    public OrderPizza() {
        Pizza pizza = null;
        String orderType;//订购披萨的类型
        do {
            orderType = getType();
            if (orderType.equals("greek")) {
                pizza = new GreekPizza();
                pizza.setName("GreekPizza");
            } else if (orderType.equals("cheess")) {
                pizza = new CheessPizza();
                pizza.setName("CheessPizza");
            } else {
                break;
            }
            //输出pizza制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    //写一个方法,可以获取客户希望订购的披萨种类
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
//相当于客户端
public class OrderStore {
    public static void main(String[] args) {
        OrderPizza orderPizza = new OrderPizza();
    }
}

改进的思路分析

  • 分析:修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有多处。
  • 思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可,其它有创建到Pizza对象的代码就不需要修改了(使用简单工厂模式)。
//简单工厂类
public class SimpleFactory {
    //简单工厂模式,也叫静态工厂模式,可以用static修饰该方法
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("greek")) {
            pizza = new GreekPizza();
            pizza.setName("GreekPizza");
        } else if (orderType.equals("cheess")) {
            pizza = new CheessPizza();
            pizza.setName("CheessPizza");
        }
        //输出pizza制作过程
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
public class OrderPizza2 {
    //定义一个简单工厂对象
    SimpleFactory simpleFactory;

    //构造器
    public OrderPizza2(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    Pizza pizza = null;

    public void setFactory(SimpleFactory simpleFactory) {
        String orderType = "";//用户输入的
        this.simpleFactory = simpleFactory;//设置简单工厂对象
        do {
            orderType = getType();
            pizza = this.simpleFactory.createPizza(orderType);
            //输出pizza制作过程
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            } else {
                System.out.println("订购失败");
                break;
            }
        } while (true);
    }

    //写一个方法,可以获取客户希望订购的披萨种类
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
//相当于客户端
public class OrderStore {    
  public static void main(String[] args) {        
    OrderPizza2 orderPizza = new OrderPizza2(new SimpleFactory());    
  }
}

工厂方法模式

工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类中具体实现。
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。 工厂方法模式.png

public class BJCheessPizza extends Pizza {
    @Override
    public void prepare() {
        setName("北京的CheessPizza");
        System.out.println("  北京的CheessPizza 准备原材料");
    }
}
public class BJPepperPizza extends Pizza {...}
public class LDCheessPizza extends Pizza {...}
public class LDPepperPizza extends Pizza {...}
public abstract class OrderPizza {
    //构造方法
    public OrderPizza() {
        Pizza pizza = null;
        String orderType;//订购披萨的类型
        do {
            orderType = getType();
            //调用方法
            pizza = creatPizza(orderType); //让子类的抽象方法实现
            //输出pizza制作过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    //定义一个抽象方法,让各个工厂子类自己实现
    abstract Pizza creatPizza(String orderType);

    //写一个方法,可以获取客户希望订购的披萨种类
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
public class BJOrderPizza  extends OrderPizza {
    @Override
    Pizza creatPizza(String orderType) {
        Pizza pizza=null;
        if (orderType.equals("Cheess")){
            pizza=new BJCheessPizza();  
        }else if(orderType.equals("Pepper")){
            pizza=new BJPepperPizza();
        }
        return pizza;
    }
}
public class LDOrderPizza extends OrderPizza {
    pizza = new LDCheessPizza();
    pizza = new LDPepperPizza();
}
//披萨店
public class PizzaStore {
    public static void main(String[] args) {
        new BJOrderPizza();//创建北京各种口味的披萨
        new LDOrderPizza();//创建伦敦各种口味的披萨
    }
}

抽象工厂模式

抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类。

抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)

将工厂抽象成两层,AbsFactory(抽象工厂)和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。 抽象工厂模式.png

//一个抽象工厂模式的抽象层(接口)
public interface AbsFactory {
    //让下面的工厂子类来具体实现
    Pizza createPizza(String orderType);
}
//这是一个工厂子类
public class BJFactory implements AbsFactory {

    @Override
    public Pizza createPizza(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("Cheess")) {
            pizza = new BJCheessPizza();
        }
        if (orderType.equals("Pepper")) {
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}

public class LDFactory implements AbsFactory {
...
    pizza=new LDCheessPizza();
    pizza=new LDPepperPizza();
...
}
public class OrderPizza {
    AbsFactory factory;

    //构造器
    public OrderPizza(AbsFactory factory) {
        setFactory(factory);
    }

    private void setFactory(AbsFactory factory) {
        Pizza pizza = null;
        String orderType = "";//用户输入
        this.factory = factory;
        do {
            orderType = getType();
            //factory可能是北京的工厂子类,也可能是伦敦的工厂子类
            pizza = factory.createPizza(orderType);
            if (pizza != null) {
                pizza.prepare();
                pizza.bake();
                pizza.box();
                pizza.cut();
            } else {
                System.out.println("订购失败");
                break;
            }
        } while (true);
    }

    //写一个方法,可以获取客户希望订购的披萨种类
    private String getType() {
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = null;
            str = strin.readLine();
            return str;
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
        //new OrderPizza(new LDFactory());
    }
}

原型模式

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

1)原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2)原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象,无需知道如何创建的细节
3)工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()

public class Sheep implements Cloneable {

    private String name;
    private int age;
    private String color;

    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    //get、set方法

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

    //克隆该实例,使用默认的clone方法来完成
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }
}
public class Client {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("tom", 1, "白色");
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep)sheep.clone();
        //......
        System.out.println(sheep);
        System.out.println(sheep2);
        System.out.println(sheep3);
    }
}

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

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

public class DeepCloneableTarget implements Serializable, Cloneable {

	private static final long serialVersionUID = 1L;

	private String cloneName;

	private String cloneClass;

	public DeepCloneableTarget(String cloneName, String cloneClass) {
		this.cloneName = cloneName;
		this.cloneClass = cloneClass;
	}

	//因为该类的属性都是String,因此我们这里使用默认的clone完成即可
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}
public class DeepProtoType implements Serializable, Cloneable {

    public String name;
    public DeepCloneableTarget deepCloneableTarget;

    public DeepProtoType() {
        super();
    }

    //深拷贝-方式1
    @Override
    protected Object clone() throws CloneNotSupportedException {

        Object deep = null;
        //完成对属性为基本数据类型和String的克隆
        deep = super.clone();
        //对属性为引用类型的单独处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();

        return deepProtoType;
    }

    //深拷贝-方式2
    public Object deepClone() {

        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //把当前这个对象以对象流的方式输出

            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType copyObj = (DeepProtoType) ois.readObject();

            return copyObj;

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}
public class Client {

    public static void main(String[] args) throws Exception {

        DeepProtoType p = new DeepProtoType();
        p.name = "宋江";
        p.deepCloneableTarget = new DeepCloneableTarget("小明", "学生类");

        //测试,方式1
        DeepProtoType p1 = (DeepProtoType) p.clone();

        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p1.name=" + p.name + "p2.deepCloneableTarget=" + p1.deepCloneableTarget.hashCode());

        //测试,方式2
        DeepProtoType p2 = (DeepProtoType) p.deepClone();

        System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
        System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode());

    }
}

建造者模式

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

建造者模式的四个角色
1)Product (产品角色):一个具体的产品对象。
2)Builder(抽象建造者):创建一个Product对象的各个部件指定的接口。
3)ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
4) Director(指挥者)::构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。

建造者模式.png

//产品-->Product
public class House {
    private String baise;
    private String wall;
    private String roofed;
    ...getter/setter
}
//抽象的建造者
public abstract class HouseBuilder {

    protected House house = new House();

    //将建造的流程写好,抽象的方法
    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void roofed();

    //建造房子后,将产品(房子)返回
    public House buildHouse() {
        return house;
    }
}
public class CommonHouse extends HouseBuilder {
    @Override
    public void buildBasic() {
        System.out.println("普通房子打地基 5米");
    }

    @Override
    public void buildWalls() {
        System.out.println("普通房子砌墙10cm");
    }

    @Override
    public void roofed() {
        System.out.println("普通房子屋顶");
    }
}

public class HighBuilding extends HouseBuilder {
    @Override
    public void buildBasic() {
        System.out.println("高楼的打地基100米");
    }

    @Override
    public void buildWalls() {
        System.out.println("高楼的砌墙20cm");
    }

    @Override
    public void roofed() {
        System.out.println("高楼的透明屋顶");
    }
}
//指挥者,指定制作流程
public class HouseDirector {

    HouseBuilder houseBuilder = null;

    //通过构造器传入houseBuilder
    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    //或者通过set方法传入houseBuilder
    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    //如何处理建造房子的流程?交给指挥者
    public House constructHouse() {
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.roofed();
        return houseBuilder.buildHouse();
    }
}
public class Client {
    public static void main(String[] args) {
        CommonHouse commonHouse = new CommonHouse();//盖普通房子
        //HighBuilding highBuilding = new HighBuilding();//盖高房子
        HouseDirector houseDirector = new HouseDirector(commonHouse);//创建房子的指挥者
      House house = houseDirector.constructHouse();//完成盖房,返回产品(房子)
    }
}

代理模式

代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

代理模式有不同的形式,主要有三种静态代理、动态代理(JDK代理或接口代理)和cglib代理(在内存动态的创建对象,而不需要实现接口)

静态代理

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

public interface ITeacherDao {
    void teach();//授课的方法
}
//被代理对象
public class TeacherDao implements ITeacherDao {
    @Override
    public void teach() {
       System.out.println("老师授课中...");
    }
}
 //代理对象
 public class TeacherDaoProxy implements ITeacherDao {
 
     private ITeacherDao iTeacherDao;
 
     public TeacherDaoProxy(ITeacherDao iTeacherDao){
         this.iTeacherDao=iTeacherDao;
     }
 
    @Override
    public void teach() {
        System.out.println("老师备课");
        iTeacherDao.teach();
        System.out.println("老师结束讲课");
    }
}
 public class Client {
     public static void main(String[] args) {
         //创建被代理对象(目标对象)
         TeacherDao teacherDao = new TeacherDao();
         //创建代理对象,同时将被代理对象传递给代理对象
         TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
         //通过代理对象,调用到被代理对象的方法
         teacherDaoProxy.teach();
     }
}

优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
缺点:一旦接口增加方法,目标对象与代理对象都要维护。

动态代理-JDK代理

代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理。
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。【反射机制】
代理类所在包:java.lang.reflect.Proxy。JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h)

动态代理-JDK.png

 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
 public class ProxyFactory {
 
     private Object target;//目标对象
 
     public ProxyFactory(Object target) {
        this.target = target;
    }

    //给目标对象生成一个代理对象
    public Object getProxyInstance() {

        /**
         * newProxyInstance方法参数说明
         * 第一个参数:指定当前目标对象使用的类加载器;
         * 第二个参数:目标对象实现的接口类型;
         * 第三个参数:事件处理,执行目标对象的方式时,会触发事件处理器方法
         */
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("老师备课");
                        //反射机制调用目标对象的方法
                        Object invoke = method.invoke(target, args);
                        System.out.println("老师讲完课了");
                        return invoke;
                    }
                }
        );
        
   }
}
public class Clinet {
    public static void main(String[] args) {
        // 创建目标对象
        ITeacherDao target = new TeacherDao();

        // 给目标对象 创建代理对象
        ITeacherDao proxyInstance = (ITeacherDao) new ProxyFactory(target).getProxyInstance();

        // 内存中动态生成代理对象 proxyInstance class com.sun.proxy.$Proxy0
        System.out.println("proxyInstance "+proxyInstance.getClass());

        // 通过代理对象 调用目标对象
        proxyInstance.teach();
    }
}

动态代理-CGlib代理

被代理对象不使用接口,使用方法拦截器
在AOP编程中如何选择代理模式:

  1. 目标对象需要实现接口,用JDK代理
  2. 目标对象不需要实现接口,用cglib代理

cglib包的底层是通过使用字节码处理框架ASM转换字节码并生成新的类

asm.jar
asm-commons.jar
asm-tree.jar
cglib-2.2.jar

在内存中动态构建子类,注意代理的类不能为final,否则报错java.lang.lllegalArgumentException

动态代理-cglib.png

public class TeacherDao {
    public void teach(){
        System.out.println("老师授课中...");
    }
}
 import net.sf.cglib.proxy.Enhancer;
 import net.sf.cglib.proxy.MethodInterceptor;
 import net.sf.cglib.proxy.MethodProxy;
 import java.lang.reflect.Method;
 
 public class ProxyFactory implements MethodInterceptor {
 
     private Object target;//维护一个目标对象
 
    //构造器,传入被代理对象
    public ProxyFactory(Object target){
        this.target=target;
    }

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

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("老师备课");
        Object invoke = method.invoke(target, args);
        System.out.println("老师讲完课了");
        return invoke;
    }
}
public class Client {
    public static void main(String[] args) {
        //创建目标对象
        TeacherDao teacherDao = new TeacherDao();
        //创建代理对象,返回被代理的目标对象(必须强制类型转换)
        TeacherDao proxyInstance = (TeacherDao) new ProxyFactory(teacherDao).getProxyInstance();
        proxyInstance.teach();
    }
}

代理模式的变体:防火墙代理、缓存代理、远程代理、同步代理

观察者模式

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

观察者模式.png
①subject
registerObserver() 注册
removeObserver() 移除
notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定。
②Observer
update() 接收输入更新

观察者模式UML类图.png

//接口, 让WeatherData 来实现【被观察者】 
public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}
//观察者接口,由观察者来实现
public interface Observer {
    public void update(float temperature, float pressure, float humidity);
}
//观察者的实现1
public class CurrentConditions implements Observer {
    // 温度,气压,湿度
    private float temperature;
    private float pressure;
    private float humidity;

    // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }

    // 显示
    public void display() {
        System.out.println("***Today mTemperature: " + temperature + "***");
        System.out.println("***Today mPressure: " + pressure + "***");
        System.out.println("***Today mHumidity: " + humidity + "***");
    }
}
//观察者的实现2
public class BaiduSite implements Observer {
    // 温度,气压,湿度
    private float temperature;
    private float pressure;
    private float humidity;

    // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }

    // 显示
    public void display() {
        System.out.println("===百度网站====");
        System.out.println("***百度网站 气温 : " + temperature + "***");
        System.out.println("***百度网站 气压: " + pressure + "***");
        System.out.println("***百度网站 湿度: " + humidity + "***");
    }
}
public class WeatherData implements Subject {
    private float temperatrue;
    private float pressure;
    private float humidity;

    //观察者集合
    private ArrayList<Observer> observers;

    //加入新的第三方
    public WeatherData() {
        observers = new ArrayList<Observer>();
    }

    public float getTemperature() {
        return temperatrue;
    }	
    public float getPressure() {
        return pressure;
    }
    public float getHumidity() {
        return humidity;
    }

    public void dataChange() {
        //调用 接入方的 update
        notifyObservers();
    }

    //当数据有更新时,就调用 setData
    public void setData(float temperature, float pressure, float humidity) {
        this.temperatrue = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        //调用dataChange, 将最新的信息 推送给 接入方 currentConditions
        dataChange();
    }

    //注册一个观察者
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }

    //移除一个观察者
    @Override
    public void removeObserver(Observer o) {
        if(observers.contains(o)) {
            observers.remove(o);
        }
    }

    //遍历所有的观察者,并通知
    @Override
    public void notifyObservers() {
        for(int i = 0; i < observers.size(); i++) {
            observers.get(i).update(this.temperatrue, this.pressure, this.humidity);
        }
    }
    
}
public class Client {
    public static void main(String[] args) {
        // 创建一个WeatherData
        WeatherData weatherData = new WeatherData();

        // 创建观察者
        CurrentConditions currentConditions = new CurrentConditions();
        BaiduSite baiduSite = new BaiduSite();

        // 注册到weatherData
        weatherData.registerObserver(currentConditions);
        weatherData.registerObserver(baiduSite);

        // 测试
        System.out.println("通知各个注册的观察者, 看看信息");
        weatherData.setData(10f, 100f, 30.3f);

        weatherData.removeObserver(currentConditions);

        // 测试
        System.out.println();
        System.out.println("通知各个注册的观察者, 看看信息");
        weatherData.setData(10f, 100f, 30.3f);
    }
}
//结果
通知各个注册的观察者, 看看信息
***Today mTemperature: 10.0***
***Today mPressure: 100.0***
***Today mHumidity: 30.3***
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***

通知各个注册的观察者, 看看信息
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***

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

参考资料

尚硅谷Java设计模式(图解+框架源码剖析)
设计模式的七大原则详解
java深拷贝与浅拷贝
设计模式-观察者模式