设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]

设计模式

设计模式七大原则、类之间关系https://blog.csdn.net/qq_42432141/article/details/107960977

23种设计模式

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

其实还有两类:并发型模式和线程池模式。用一个图片来整体描述一下:设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第1张图片

单例模式(8种)

什么是单例模式?

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

饿汉式

饿汉式特点

一加载类,就实例化对象

饿汉式(静态常量)

// 饿汉式 1
class EagerSingleton {
    // 1、构造器私有化,外部只能new
    private EagerSingleton() {
    }
    // 2、本类内部创建对象实例
    private final static EagerSingleton INSTANCE = new EagerSingleton();
    // 3、对外提供一个共有的静态方法,返回实例对象
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

饿汉式(静态代码块)

// 饿汉式 2
class EagerSingleton {
    // 构造器私有化,外部只能new
    private EagerSingleton() {
    }
    // 2、本类内部创建对象实例
    private static EagerSingleton instance;
    static { // 在静态代码块中,创建单例对象
        instance = new EagerSingleton();
    }
    // 3、对外提供一个共有的静态方法,返回实例对象
    public static EagerSingleton getInstance() {
        return instance;
    }
}

优缺点

  • 优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
  • 缺点:在类装载的时候就完成实例化,没有达到 Lazy Loading 的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
  • 这种方式基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,在单例模式中大多数都是调用 getInstance 方法, 但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 就没有达到 lazy loading 的效果

懒汉式

懒汉式特点

什么时候使用,什么时候加载

懒汉式(线程不安全)

/**
 * 懒汉式 1
 *  线程不安全
 *  在多线程下,多个进程同时进入 if (instance == null)判断语句块的时候
 *  就会创建多个实例
 */
class LazySingleton {
    private static LazySingleton instance;
    public LazySingleton() {
    }
    /**
     * 提供一个静态的共有方法,当使用到该方法的时候,采取创建instance
     * @return 返回创建的instance
     */
    public static LazySingleton getInstance(){
        if (instance == null){
            instance = new LazySingleton();
        }
        return instance;
    }
}

优缺点

  • 起到了 Lazy Loading 的效果,但是只能在单线程下使用。
  • 如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式

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

/**
 * 懒汉式 2
 *  线程安全 同步方法
 */
class LazySingleton {

    private static LazySingleton instance;

    public LazySingleton() {
    }

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

优缺点

  • 解决了线程安全问题
  • 效率太低了,每个线程在想获得类的实例时候,执行 getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接 return 就行了。方法进行同步效率太低

同步代码块(不能使用)

多个线程还是会进入if判断中,进行等待,实例化好对象后,下一个线程依旧会实例。

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

双重判断(推荐使用)

class LazySingleton {
    private static volatile LazySingleton instance;
    public LazySingleton() {
    }
    /**
     * 提供一个静态的共有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题,又解决了效率问题
     * @return 返回创建的instance
     */
    public static synchronized LazySingleton getInstance() {
        // 第一次判断
        if (instance == null) {
            synchronized (LazySingleton.class) {
                // 第二次判断
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

静态内部类

/**
 * 静态内部类
 */
class StaticSingleton {

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

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

    /**
     * 提供一个静态的共有方法,直接返回 StaticSingletonInstance.INSTANCE
     * @return 返回创建的instance
     */
    public static synchronized StaticSingleton getInstance() {
        return StaticSingletonInstance.INSTANCE;
    }
}

优缺点

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

枚举

public class SingletonTest07 {
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance == instance2);
    }
}
enum EnumSingleton {
    // 属性
    INSTANCE;
    public void sayOk() {
        System.out.println("ok~~");
    }
}

优缺点

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

工厂模式

简单工厂模式

又名:静态工厂模式

模式介绍

  • 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式

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

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

    实例:订购pizza

图解:

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第2张图片

// pizza 抽象类
public abstract class Pizza {
    // pizza 名字
    protected String name;
    /** 抽象方法 prepare
     * 准备原材料,不同的pizza 原材料不一样
     */
    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;
    }
}
// 希腊pizza
public class GreekPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println(" 给希腊pizza准备原材料 .... ");
    }
}
// 奶酪pizza
public class CheesePizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println(" 给制作奶酪pizza 准备原材料 .... ");
    }
}
// 制作pizza
public class OrderPizza {
    /**
     * 定义一个简单工厂对象
     */
    SimpleFactory simpleFactory=null;
    Pizza pizza = null;
    public OrderPizza(SimpleFactory simpleFactory) {
        setSimpleFactory(simpleFactory);
    }
    public void setSimpleFactory(SimpleFactory simpleFactory){
        // 用户输入的种类
        String orderType = "";
        // 设置简单工厂对象
        this.simpleFactory = simpleFactory;
        do {
            // 拿到用户想订购的类型
            orderType = getType();
            // 拿到制作好的pizza
            pizza = this.simpleFactory.creatPizza(orderType);
            if (pizza!=null){
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else {
                System.out.println("订购pizza 失败 ... ");
                break;
            }
        }while (true);

    }
    // 写一个方法,可以获取用户希望订购的pizza种类
    private String getType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("please input pizza type:  ");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
// 简单工厂
public class SimpleFactory {
    /**
     * 根据orderType 返回对应的Pizza对象
     * @param orderType
     * @return
     */
    public Pizza creatPizza(String orderType) {
        Pizza pizza = null;
        String greek = "greek";
        String cheese = "cheese";
        System.out.println("使用简单的工厂模式");
        if (greek.equals(orderType)) {
            pizza = new GreekPizza();
            pizza.setName("greek");
        } else if (cheese.equals(orderType)) {
            pizza = new GreekPizza();
            pizza.setName("cheese");
        }
        return pizza;
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        // 使用简单工厂模式
        new OrderPizza(new SimpleFactory());
        System.out.println("退出程序 ... ");
    }
}

问题来了! 新增pizza种类如何修改?

// 创建胡椒pizza 继承pizza类
public class PepperPizza extends Pizza{
    @Override
    public void prepare() {
        System.out.println("给胡椒pizza准备原材料 ... ");
    }
}

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第3张图片

工厂方法模式

有新的需求,北京奶酪pizza,伦敦胡椒pizza,如何实现?

模式介绍

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

图解

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第4张图片

代码实现

// pizza 抽象类
public abstract class Pizza {
    // pizza 名字
    protected String name;
    /** 抽象方法 prepare
     * 准备原材料,不同的pizza 原材料不一样
     */
    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;
    }
}
// 伦敦奶酪pizza
public class LDPepperPizza extends Pizza{
    @Override
    public void prepare() {
        setName("伦敦胡椒pizza");
        System.out.println("伦敦胡椒pizza  准备材料中");
    }
}
// 伦敦奶酪pizza
public class LDCheesePizza extends Pizza {
    @Override
    public void prepare() {
        setName("伦敦奶酪pizza");
        System.out.println("伦敦奶酪pizza  准备材料中");
    }
}
// 北京胡椒pizza
public class BJPepperPizza extends Pizza{
    @Override
    public void prepare() {
        setName("北京胡椒pizza");
        System.out.println("北京的胡椒pizza 准备材料 ... ");
    }
}
// 北京奶酪pizza
public class BJCheesePizza extends Pizza{
    @Override
    public void prepare() {
        setName("北京奶酪pizza");
        System.out.println("北京的奶酪pizza  准备材料 ... ");
    }
}
// 选择pizza
public abstract class OrderPizza {
    /**
     * 定义一个抽象方法 createPizza,让各个工厂子类自己实现
     * @param orderType
     * @return
     */
    abstract Pizza createPizza(String orderType);

    /**
     * 构造器
     */
    public OrderPizza() {
        Pizza pizza = null;
        // 订购Pizza的类型
        String orderType = null;
        do {
            orderType = getType();
            // 抽象方法 由工厂子类完成
            pizza = createPizza(orderType);
            // 输出pizza过程
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
        } while (true);
    }

    /**
     * 写一个方法,可以获取用户希望订购的pizza种类
     */
    private String getType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("please input pizza type:  ");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
// 伦敦pizza选择
public class LDOrderPizza extends OrderPizza{
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        String pepper = "pepper";
        String cheese = "cheese";
        if (cheese.equals(orderType)){
            pizza = new LDCheesePizza();
        }else if(pepper.equals(orderType)){
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}

// 北京pizza选择
public class BJOrderPizza extends OrderPizza{
    @Override
    Pizza createPizza(String orderType) {
        Pizza pizza = null;
        String pepper = "pepper";
        String cheese = "cheese";
        if (cheese.equals(orderType)){
            pizza = new BJCheesePizza();
        }else if(pepper.equals(orderType)){
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        BJOrderPizza bjOrderPizza = new BJOrderPizza();
    }
}

抽象工厂模式

基本介绍

  • 抽象工厂模式:定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类
  • 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
  • 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
  • 将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。

图解

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第5张图片

代码实现

这几个类和上面的一样

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第6张图片

public interface AbsFactory {
	// 抽象方法: 让工厂子类来具体实现
   public Pizza createPizza(String orderType);
}
public class BJFactory implements AbsFactory{
    // 北京工厂 根据type实现制作pizza
    @Override
    public Pizza createPizza(String orderType) {
        System.out.println("创建北京工厂  使用抽象模式");
        Pizza pizza = null;
        String cheese = "cheese";
        String pepper = "pepper";
        if (cheese.equals(orderType)){
            pizza = new BJCheesePizza();
        }else if (pepper.equals(orderType)){
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}
public class LDFactory implements AbsFactory{
    // 伦敦工厂 根据type实现制作pizza
    @Override
    public Pizza createPizza(String orderType) {
        System.out.println("创建伦敦工厂  使用抽象模式");
        Pizza pizza = null;
        String cheese = "cheese";
        String pepper = "pepper";
        if (cheese.equals(orderType)){
            pizza = new LDCheesePizza();
        }else if (pepper.equals(orderType)){
            pizza = new LDPepperPizza();
        }
        return pizza;
    }
}
public class OrderPizza {
    AbsFactory factory;
    // 构造器 实例化工厂的时候 设置工厂的种类
    public OrderPizza(AbsFactory factory) {
        setAbsFactory(factory);
    }
    //  实例化的时候 根据传入的工厂,去创建对应的pizza
    //  具体pizza的type 参考getType()方法
    private void setAbsFactory(AbsFactory factory) {
        Pizza pizza = null;
        String orderType = "";
        this.factory = factory;
        do {
            orderType = getType();
            // factory 可能是北京的工厂子类 也可能是伦敦的工厂子类
            pizza = factory.createPizza(orderType);
            if (pizza != null){
                // 输出pizza过程
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else {
                System.out.println("订购失败 ... ");
                break;
            }
        } while (true);
    }
    // 写一个方法,可以获取用户希望订购的pizza种类
    private String getType() {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("please input pizza type:  ");
            String str = bufferedReader.readLine();
            return str;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
public class PizzaStore {
    public static void main(String[] args) {
        new OrderPizza(new BJFactory());
    }
}

使用场景

子类特别多的话使用抽象工厂,子类很少的话使用工厂方法。

代理模式

代理模式的基本介绍

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

  2. 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象

  3. 代理模式有不同的形式, 主要有三种 静态代理动态代理(JDK 代理、接口代理)和 Cglib 代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。

静态代理

  • 代理类和被代理类必须实现相同接口或者父类
  • 代理类中需要定义成员变量 来接受被代理者

实现步骤

代理模式:
    1、创建接口
    2、创建被代理对象 实现接口
    3、创建代理对象 实现接口,创建被代理对象的引用 在对应的方法中 调用被代理对象的方法 并进行扩展
    4、实例化被代理对象 和 代理对象
    5、代理对象和被代理对象关联
    6、调用代理对象的方法

代理对象:
    方法实现
        首先先实现被代理对象的方法(创建引用)
        对方法进行扩展
// 接口
public interface Person {
    void show();
    void eat(String food);
}
// 被代理类
public class Student implements Person {
    String name;
    String sclass; // 班级
    @Override
    public void show() {
        System.out.println("我是" + sclass + "班的学生: " + name);
    }
    @Override
    public void eat(String food) {
        System.out.println("我是:" + name + "我爱吃" + food);
    }
    public Student(String name, String sclass) {
        this.name = name;
        this.sclass = sclass;
    }
}
// 被代理类
public class StudentProxy implements Person{
    // 定义一个成员变量 来记录被代理对象
    Person person;
    public StudentProxy(Person person) {
        this.person = person;
    }
    @Override
    public void show() {
        person.show();
        System.out.println("再来首歌");
    }
    @Override
    public void eat(String food) {
        person.eat(food);
        System.out.println("注意: 粒粒皆辛苦");
    }
}
public class TestProxy {
	public static void main(String[] args) {
		// 先有代理对象
		Student student = new Student("jsu", "信息");
      // 创建代理对象
      // 代理对象和被代理对象关联
      StudentProxy studentProxy = new StudentProxy(student);
      // 使用代理对象即可
      studentProxy.show();
      studentProxy.eat("芒果");
    }
}

优缺点

​ 缺点:

  • 代理类和被代理类必须实现同一个接口 局限性比较大 而且代理类很多

  • 代理类只能对指定接口类型的实现类进行控制 而且只能扩展接口中定义的方法

  • 一旦接口增加方法,目标对象与代理对象都要维护

    优点:

    在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展

动态代理

基本介绍

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

实现步骤

  • 1 创建一个代理工厂类 实现接口InvocationHandler

  • 2 定义成员变量记录 被代理对象

  • 3 通过构造方法的参数列表 关联被代理对象

  • 4 实现invoke方法 对目标对象的方法进行控制

  • 5 定义一个方法:getInstance 来创建动态创建代理对象

  • 6 定义被代理类:必须实现接口

  • 7 创建被代理对象

  • 8 创建代理工厂对象 并关联被代理对象

  • 9 调用工厂对象的getProxyInstance方法来动态获取代理对象

    // 接口
    public interface IActive {
        void show();
        void work();
    }
    
    public class Worker implements IActive {
        String name;
        Integer age;
        public Worker(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public void show(){
            System.out.println("我是一名x学生:"+name+"今年"+age+"岁");
        }
        @Override
        public void work(){
            System.out.println(name+"学生,正在学习!");
        }
    }
    
    //  动态代理工厂类
    public class ProxyFactory implements InvocationHandler {
        // 定义成员变量记录  被代理对象
        private Object object;
    
        // 通过构造方法的参数列表   关联被代理对象
        public ProxyFactory(Object object) {
            this.object = object;
        }
    
        // 实现invoke方法 对目标对象的方法进行控制   代理对象的方法每次调用的时候 就会执行invoke
        /*
         Object invoke(Object proxy, Method method, Object[] args)
            proxy   : 代理对象
            method  : 被代理对象的方法
            args    : 方法的实参
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("目标对象方法之前的 ---- 预处理的代码");
            // 调用目标对象的方法
            Object invoke = method.invoke(object, args);
            System.out.println("目标对象方法之后的 ---- 预处理的代码");
            return invoke;
        }
    
        // 定义一个方法: getProxyInstance
        // 此方法的目的 就是动态生成代理对象
        public Object getProxyInstance() {
            /*
             Proxy.newProxyInstance(loader, interfaces, h);
                loader : 代理对象的类加载器
                interfaces : 代理对象要实现的接口 -- 被代理对象实现的接口
                h : InvocationHandler的实现对象
             */
            Object proxy = Proxy.newProxyInstance(object.getClass().getClassLoader(),
                                      object.getClass().getInterfaces(),
                                      this);
            return proxy;
        }
    }
    
    // 测试类
    public static void main(String[] args) {
       // 创建被代理对象
       Worker su = new Worker("su", 28);
       // 创建工厂对象
       ProxyFactory proxyFactory = new ProxyFactory(su);
       // 调用工厂对象的getProxyInstance方法
       IActive proxyInstance = (IActive) proxyFactory.getProxyInstance();
       proxyInstance.show();
       proxyInstance.work();
    }
    

Cglib

优势

​ CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了

导包

<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>2.2.2version>

代码实现

public class ProxyFactory implements MethodInterceptor {
    // 维护一个目标对象
    private Object target;
    // 构造器 传入一个被代理的对象
    public ProxyFactory(Object target) {
        this.target = target;
    }
    // 重写	intercept 方法,会调用目标对象的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Cglib 代理模式 开始");
        Object invoke = method.invoke(target, objects);
        System.out.println("Cglib 代理模式 结束");
        return invoke;
    }

    //返回一个代理对象:	是target 对象的代理对象
    public Object getProxyInstance() {
        //1. 创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2. 设置父类
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调函数
        enhancer.setCallback(this);
        //4. 创建子类对象,即代理对象
        return enhancer.create();
    }
}
public class Teacher {
    public String teach() {
        System.out.println(" 老师授课中	, 我是 cglib 代理,不需要实现接口 ");
        return "hello";
    }
}
// 测试
public class CglibTest {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //创建目标对象
        Teacher target = new Teacher();
        //获取到代理对象,并且将目标对象传递给代理对象
        Teacher proxyInstance = (Teacher) new ProxyFactory(target).getProxyInstance();

        //执行代理对象的方法,触发 intercept  方法,从而实现 对目标对象的调用
        String res = proxyInstance.teach();
        System.out.println("res=" + res);

    }
}

原型模式

基本介绍

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

实例:克隆羊

传统方式

图解:

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第7张图片

代码实现

public class Sheep {
    private String name;
    private Integer age;
    private String color;
    public Sheep(String name, Integer age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
    // 自己生成get set toString方法
}
public class Client {
    public static void main(String[] args) {
        // 传统的方法
        Sheep sheep = new Sheep("tom", 1, "白色");
        // 克隆羊
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(),sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(),sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(),sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(),sheep.getColor());
        System.out.println(sheep);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);

    }
}

优缺点:

  • 优点是比较好理解,简单易操作。
  • 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
  • 总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活

浅拷贝

基本介绍

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。

  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值

  • 前面我们克隆羊就是浅拷贝

  • 浅拷贝是使用默认的 clone()方法来实现

    sheep = (Sheep) super.clone();

图解:

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第8张图片

代码实现

public class Sheep implements Cloneable{
    private String name;
    private Integer age;
    private String color;
    // 克隆该实例  使用默认的clone方法完成
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try{
            sheep = (Sheep)super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return sheep;
    }
}
public class Client {
    public static void main(String[] args) {
        // 传统的方法
        Sheep sheep = new Sheep("tom", 1, "白色");
        // 克隆羊
        Sheep clone1 = (Sheep) sheep.clone();
        Sheep clone2 = (Sheep) sheep.clone()
    }
}

深拷贝

基本介绍

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

代码实现

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  使用 clone 方法
    @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 {
// TODO Auto-generated method stub 
       DeepProtoType p = new DeepProtoType(); p.name = "宋江";
		 p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛");
    //方式 1  完成深拷贝
    //	DeepProtoType p2 = (DeepProtoType) p.clone();
    //
    //	System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode());
    //	System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.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());
	}
}

建造者模式

基本介绍

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

四个角色

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

图解

未使用建造者模式

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第9张图片

使用建造者模式之后
设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第10张图片

代码实现

// Product 产品角色
public class House {
    private String basic;
    private String wall;
    private String roofed;
	// 生成getter setter
}
// 抽象建造者
public abstract class AbstractHouseBuilder {
    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 AbstractHouseBuilder {
    @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 HighHouse extends AbstractHouseBuilder{
    @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 {
    AbstractHouseBuilder houseBuilder = null;
    // 构造器传入houseBuilder
    public HouseDirector(AbstractHouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }
    // 通过setter 传入houseBuilder
    public void setHouseBuilder(AbstractHouseBuilder 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 commonBuilder = new CommonHouse();
        // 准备盖房子的指挥者
        HouseDirector houseDirector = new HouseDirector(commonBuilder);
        // 完成盖房子 返回房子
        House house = houseDirector.constructHouse();
    }
}'

8.1 建造者模式的注意事项和细节

  • 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象

  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象

  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰, 也更方便使用程序来控制创建过程

  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式.

    抽象工厂模式 VS 建造者模式

    抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品

适配器模式

基本介绍

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

工作原理

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

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第11张图片

类适配器

应用实例说明

以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们的目 dst(即 目标)是 5V 直流电

图解:

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第12张图片

代码实现

// 被适配的类
public class Voltage220V {
    public int output220V(){
        int src = 220;
        System.out.println("电压为: "+ src +"V");
        return src;
    }
}
// 适配接口
public interface IVoltage5V {
    // 转换为5v
    public int output5V();
}
// 适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
    @Override
    public int output5V() {
        // 获取到220v的电压
        int src = output220V();
        // 获取到需要的电压 220/44=5V
        int dstV = src / 44;
        return dstV;
    }
}
// 目标
public class Phone {
    // 充电
    public void charging(IVoltage5V iVoltage5V){
        if (iVoltage5V.output5V() == 5){
            System.out.println("电压为5V, 开始充电");
        }else {
            System.out.println("电压不是5V,无法充电");
        }
    }
}
// 测试
public class Client {
    public static void main(String[] args) {
        System.out.println("类适配器模式");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

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

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

对象适配器

基本介绍

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

应用实例

以生活中充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们的目 dst(即目标)是 5V 直流电,使用对象适配器模式完成。

图解

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第13张图片

代码实现

// 与类适配器比较只有VoltageAdapter 不同
public class VoltageAdapter implements IVoltage5V {
    // 关联关系中的- 聚合关系
    private Voltage220V voltage220V;

    //通过构造器,传入一个Voltage220V实例
    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        // 定义需要的电压 默认为0
        int dstV = 0;
        // 判断voltage220V对象是否存在
        if (null != voltage220V) {
            // 获取到220v的电压
            int src = voltage220V.output220V();
            System.out.println("使用对象适配器进行转换");
            dstV = src / 44;
            System.out.println("适配完成,输出的电压为=" + dstV);
        }
        return dstV;
    }
}
public class Client {
    public static void main(String[] args) {
        System.out.println("对象适配器模式");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}

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

  1. 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。

根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承 src 的局限性问题,也不再要求 dst必须是接口。

  1. 使用成本更低,更灵活。

接口适配器

基本介绍

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

图解:

设计模式详解[单例模式、工厂模式、代理模式、原型模式、建造者模式、适配器模式]_第14张图片

代码实现

// 接口
public interface Interface1 {
    void m1();
    void m2();
    void m3();
    void m4();
}
public interface Interface2 {
    void t1();
    void t2();
    void t3();
    void t4();
}
public class AbstractAdapter implements Interface1 ,Interface2{
    // 默认实现
    @Override
    public void m1() {

    }
	......
}
// 测试
public class Client {
    public static void main(String[] args) {
        AbstractAdapter abstractAdapter = new AbstractAdapter() {
            // 只需要去覆盖我们需要使用的方法就好了
            @Override
            public void m1() {
                System.out.println("m1 run .... ");
            }

            @Override
            public void t2() {
                System.out.println("t2 run ....");
            }
        };

        abstractAdapter.m1();
        abstractAdapter.t2();
    }
}

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

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

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