GoF设计模式(不全)

了解设计模式

  • 设计模式 是前辈们对代码开发经验的总结, 是解决特定问题的一系列套路, 他不是语法规定, 而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
  • 1995年,GoF合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式

学习设计模式的意义

  • 设计模式的本质是面向对象设计原则的实际运用, 是对类的封装性, 继承性和多态性以及类的关联关系和组合关系的充分理解

  • 正确使用设计模式具有以下优点:

    • 可以提高程序员的思维能力, 编程能力和设计能力

    • 使程序设计更加标准化, 代码编制更加工程化, 使软件开发效率大大提高, 从而缩短软件的开发周期

    • 使设计的代码可重用性高, 可读性强, 可靠性高, 灵活性好, 可维护性强

GoF 23

  • Gof23
    • 一种思维, 一种态度, 一种进步
  • 创建型模式:
    • 单例模式, 工厂模式, 抽象工厂模式, 建造者模式, 原型模式
  • 结构型模式:
    • 适配器模式, 桥接模式, 装饰模式, 组合模式, 外观模式, 享元模式, 代理模式
  • 行为型模式:
    • 模板方法模式, 命令模式, 迭代器模式, 观察者模式, 中介者模式, 备忘录模式, 解释器模式, 状态模式, 策略模式, 职责链模式, 访问者模式

OOP 七大原则

  • **开闭原则: ** 对扩展开放, 对修改关闭
  • 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
  • **依赖倒置原则: ** 要面向接口编程, 不要面向实现编程
  • **单一职责原则: ** 控制类的粒度大小, 将对象解耦, 提高其内聚性
  • 接口隔离原则: 要为各个类建立它们需要的专用接口
  • **迪米特法则: **只与你的直接朋友交谈,不跟"陌生人"说话
  • **合成复用原则: **尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现

单例模式

饿汉式、DCL懒汉式,深究

饿汉式

package com.kuang.single;
//单例模式构造器一定要私有
// 饿汉式 单例
public class Hungry {

    int id;
    String name;

    //可能会浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];
    //构造器私有  就不能通过new来创建对象
    private Hungry() {
    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }

    public static void main(String[] args) {
        Hungry instance = getInstance();
        byte[] data1 = instance.data1;
        int id = instance.id;
        String name = instance.name;
    }
}

懒汉式

存在多线程并发模式,后面的DCL懒汉式解决并发问题

public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

DCL懒汉式

注意:synchronized 解决并发问题,但是因为lazyMan = new LazyMan();不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过volatil来解决

  • Java 语言提供了 volatile和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。
  • 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){//synchronized加锁解决多线程下的问题
                if(lazyMan == null){
                    lazyMan = new LazyMan();//不是一个原子性操作
                }
            }

        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类

//静态内部类
public class Holder {

    //构造器私有
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();

    }
}

单例不安全,反射破坏(见注释及main方法中反射破解步骤)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//单例懒汉式
//懒汉式单例

public class LazyMan {
    private static boolean qingjiang = false;//红绿等解决通过反射创建对象(反编译可以破解该方法)
    private LazyMan(){
        synchronized (LazyMan.class){
            if (qingjiang==false){
                qingjiang = true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏单例");
            }

        }
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;//volatile避免指令重排


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }

//反射!
public static void main(String[] args) throws Exception {
    //LazyMan instance = LazyMan.getInstance();
    Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");
    qingjiang.setAccessible(true);

    Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);//无视私有的构造器
    LazyMan instance1 = declaredConstructor.newInstance();
    qingjiang.set(instance1,false);
    System.out.println(instance1);
    LazyMan instance2 = declaredConstructor.newInstance();

    System.out.println(instance2);

}

    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
   /* public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }*/
}

枚举: 通过反射破解枚举发现不成功:
1、普通的反编译会欺骗开发者,说enum枚举是无参构造
2、实际enum为有参构造(见后面);
3、通过反射破解枚举会发现抛出异常
Exception in thread “main” java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)

import java.lang.reflect.Constructor;

//enmu是什么?本身也是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
        System.out.println(instance);
        System.out.println(instance2);

    }
}

通过idea和jdk自带的反编译枚举如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ySVBgy9-1640520822311)(设计模式.assets/image-20211225140612719.png)]

通过jad反编译枚举的代码如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V5M2yQUn-1640520822313)(设计模式.assets/9d6d581e4871678996b1fa4257d77a5c.png)]

发现枚举是有参构造

工厂模式

  • 作用:
    • 实现了创建者和调用者的分离
    • 详细分类
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
  • OOP七大原则
    • 开闭原则: 一个软件的实体应当对扩展开放, 对修改关闭
    • 依赖倒转原则: 要针对接口编程, 不要针对实现编程
    • 迪米特法则: 只与你直接的朋友通信, 而避免和陌生人通信
  • 核心本质
    • 实例化对象不使用new, 用工厂方法代替
    • 将选择实现类, 创建对象统一管理和控制. 从而将调用者跟我们的实现类解耦
  • 三种模式:
    • 简单工厂模式
      • 用来生产统一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
    • 工厂方法模式
      • 用来生产同一等级结构中的固定产品(支持增加任意产品)
    • 抽象工厂模式
      • 围绕一个超级工厂创建其他工厂, 该超级工厂又称为其他工厂的工厂

简单工厂模式simple

car

package com.kuang.factory.simple;

public interface Car {
    void name();
}

CarFactory

package com.kuang.factory.simple;

//静态工厂模式
//增加一个新的产品,如果你不修改代码,做不到!!

//开闭原则
public class CarFactory {

    //方法一
    public static Car getCar(String car){
        try {
            if (car.equals("五菱")){
                return new WuLing();
            }else if (car.equals("特斯拉")){
                return new Tesla();
            }else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //方法二
    public static Car getWuLing(String car){
        return new WuLing();
    }

    public static Car getTesla(String car){
        return new Tesla();
    }

    public static Car getDazhong(String car){
        return new Dazhong();
    }
}

Consumer

package com.kuang.factory.simple;

public class Consumer {
    public static void main(String[] args) {
        //接口,所有的实现类!工厂
        //1.传统方式
        //Car car = new WuLing();
        //Car car1 =  new Tesla();

        //2.使用工厂创建
         Car car = CarFactory.getCar("五菱");
         Car car1 = CarFactory.getCar("特斯拉");

        car.name();
        car1.name();
    }
}

Tesla Dazhong WuLing

package com.kuang.factory.simple;

public class Tesla implements Car{
    @Override
    public void name() {
        System.out.println("特斯拉!");
    }
}


package com.kuang.factory.simple;

public class WuLing implements Car{
    @Override
    public void name() {
        System.out.println("五菱宏光!");
    }
}



package com.kuang.factory.simple;

public class Dazhong implements Car{
    @Override
    public void name() {
        System.out.println("大众!");
    }
}

工厂方法模式factory

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UsgoGLAm-1640520822313)(设计模式.assets/image-20211225153141070.png)]

car

package com.kuang.factory.method;

public interface Car {
    void name();
}

CarFactory

package com.kuang.factory.method;

//工厂方法模式
public interface CarFactory {
    Car getCar();
}

Consumer

package com.kuang.factory.method;

import com.kuang.factory.simple.CarFactory;

public class Consumer {
    public static void main(String[] args) {
        Car car = new WuLingFactory().getCar();
        Car car1 = new TeslaFactory().getCar();

        car.name();
        car1.name();

        Car car2 = new MoBaiFactory().getCar();
        car2.name();

    }

    //简单工厂 与 工厂方法 之间的比较
    //结构复杂度: simple
    //代码复杂度: simple
    //编程复杂度: simple
    //管理上的复杂度: simple

    //根据设计原则:工厂方法模式!
    //根据实际业务:简单工厂模式!
}

一下代码省略:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VK2Ftmbm-1640520822314)(设计模式.assets/image-20211225154139380.png)]

  • 所有的车实现car接口

  • 每个carFactory实现Factory接口

  • 每一个车都有自己的工厂

每新加一个车就需要写一个工厂类 代码量会加大

//简单工厂 与 工厂方法 之间的比较
//结构复杂度: simple
//代码复杂度: simple
//编程复杂度: simple
//管理上的复杂度: simple

//根据设计原则:工厂方法模式!
//根据实际业务:简单工厂模式!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pAgaCX6y-1640520822314)(设计模式.assets/image-20211225153845611.png)]

抽象工厂模式

  • 定义︰抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
  • 适用场景:
    • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
    • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
    • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
  • 优点:
    • 具体产品在应用层的代码隔离,无需关心创建的细节
    • 将一个系列的产品统一到一起创建
  • 缺点:
    • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
    • 增加了系统的抽象性和理解难度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5paCwYyu-1640520822315)(设计模式.assets/image-20211225173256301.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nSgDi0lb-1640520822316)(设计模式.assets/5f3e9c78208fc62f83490b0fb53f4dd0.png)]

工厂模式:比如一个car工厂,那么就只生产car,如果需要生产air,就需要再建一个工厂,这时就可以使用抽象工厂。

抽象工厂模式:比如有两个工厂car和air,可以再为这两个工厂提供一个抽象工厂接口,利用这个抽象工厂接口来实现具体的car工厂或air工厂。

目的:这样的目的是为体现OCP原则,因为如果只使用工厂模式,你要生产air但是却没有air工厂,你必然会修改到car的工厂,这是不被允许的。

这时提供一个抽象工厂,只需要在抽象工厂中扩展即可,不会修改到原来的工厂,体现OCP原则。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sbZcia8U-1640520822316)(设计模式.assets/image-20211225220301940.png)]

IphoneProduct

package com.kuang.factory.abstractfactory;

//手机产品接口
public interface IphoneProduct {
    void start();
    void shutdown();
    void callup();
    void sendSMS();
}

IRouterProduct

package com.kuang.factory.abstractfactory;

//路由器产品接口
public interface IRouterProduct {
    void start();
    void shutdown();
    void openWife();
    void setting();
}

XiaomiPhone

package com.kuang.factory.abstractfactory;

//小米手机
public class XiaomiPhone implements IphoneProduct{

    @Override
    public void start() {
        System.out.println("开启小米手机");
    }

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

    @Override
    public void callup() {
        System.out.println("小米打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("小米发短信");
    }
}

XiaomiRouter

package com.kuang.factory.abstractfactory;

//小米路由器
public class XiaomiRouter implements IRouterProduct {

    @Override
    public void start() {
        System.out.println("启动小米路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭小米路由器");
    }

    @Override
    public void openWife() {
        System.out.println("打开小米wifi");
    }

    @Override
    public void setting() {
        System.out.println("小米设置");
    }
}

HuaweiPhone

package com.kuang.factory.abstractfactory;

//华为手机
public class HuaweiPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println("开启华为手机");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为手机");
    }

    @Override
    public void callup() {
        System.out.println("华为打电话");
    }

    @Override
    public void sendSMS() {
        System.out.println("华为发短信");
    }
}

HuaweiRouter

package com.kuang.factory.abstractfactory;

//华为路由器
public class HuaweiRouter implements IRouterProduct{

    @Override
    public void start() {
        System.out.println("启动华为路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为路由器");
    }

    @Override
    public void openWife() {
        System.out.println("打开华为wifi");
    }

    @Override
    public void setting() {
        System.out.println("华为设置");
    }
}

IProductFactory

package com.kuang.factory.abstractfactory;

//抽象产品工厂
//也就是说用两次抽象,解决了两个维度的问题。原来的工厂只是解决了一个维度的问题
public interface IProductFactory {
    //生产手机
    IphoneProduct iphoneProduct();

    //生成路由器
    IRouterProduct routerProduct();

    //生成笔记本

}

XiaomiFactory

package com.kuang.factory.abstractfactory;

public class XiaomiFactory implements IProductFactory{
    @Override
    public IphoneProduct iphoneProduct() {
        return new XiaomiPhone();
    }

    @Override
    public IRouterProduct routerProduct() {
        return new XiaomiRouter();
    }
}

HuaweiFactory

package com.kuang.factory.abstractfactory;


public class HuaweiFactory implements IProductFactory{
    @Override
    public IphoneProduct iphoneProduct() {
        return new HuaweiPhone();
    }

    @Override
    public IRouterProduct routerProduct() {
        return new HuaweiRouter();
    }
}

Client

package com.kuang.factory.abstractfactory;

public class Client {
    public static void main(String[] args) {
        System.out.println("=========小米系列产品=========");

        //小米工厂
        XiaomiFactory xiaomiFactory = new XiaomiFactory();

        IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
        iphoneProduct.callup();
        iphoneProduct.sendSMS();

        IRouterProduct iRouterProduct = xiaomiFactory.routerProduct();
        iRouterProduct.openWife();
        iRouterProduct.setting();


        System.out.println("=========华为系列产品=========");

        //华为工厂
        HuaweiFactory huaweiFactory = new HuaweiFactory();

        IphoneProduct iphoneProduct1 = huaweiFactory.iphoneProduct();
        iphoneProduct1.callup();
        iphoneProduct1.sendSMS();

        IRouterProduct iRouterProduct2 = huaweiFactory.routerProduct();
        iRouterProduct2.openWife();
        iRouterProduct2.setting();
    }
}

小结

  • 简单工厂模式(静态工厂模式)
    • 虽然某种程度上不符合设计原则, 但实际使用最多
  • 工厂方法模式
    • 不修改已有类的前提下, 通过增加新的工厂类实现扩展
  • 抽象工厂模式
    • 不可以增加产品, 可以增加产品族

应用场景

  • JDK中Calendar的getInstance方法
  • JDBC中的Connection对象的获取
  • Spring中IOC容器创建管理bean对象
  • 反射z中Class对象的newstance方法

建造者模式

建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。

定义︰将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

例子:

  • 工厂(建造者模式)︰负责制造汽车(组装过>程和细节在工厂内)
  • 汽车购买者(用户)∶你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、>发动机、方向盘等等)

角色分析

这里具体的Builder也就是代码中的worker

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRDxYqLq-1640520822316)(设计模式.assets/image-20211226164551374.png)]

既然是建造者模式,那么我们还是继续造房吧,其实我也想不到更简单的例子。假设造房简化为如下步骤:(1)地基(2)钢筋工程(3)铺电线(4)粉刷;“如果”要盖一座房子,首先要找一个建筑公司或工程承包商(指挥者)。承包商指挥工人(具体建造者)过来造房子(产品),最后验收。

代码实现

Builder

package com.kuang.builder.demo01;

//抽象的建造者: 方法
public abstract class Builder {

    abstract void buildA();//地基
    abstract void buildB();//钢筋工程
    abstract void buildC();//铺电线
    abstract void buildD();//粉刷

    //完工: 得到产品
    abstract Product getProduct();
}

Product

package com.kuang.builder.demo01;

//产品:房子
public class Product {

    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

    public String getBuildA() {
        return buildA;
    }

    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }

    public String getBuildB() {
        return buildB;
    }

    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }

    public String getBuildC() {
        return buildC;
    }

    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }

    public String getBuildD() {
        return buildD;
    }

    public void setBuildD(String buildD) {
        this.buildD = buildD;
    }

    @Override
    public String toString() {
        return "Product{" +
                "buildA='" + buildA + '\'' +
                ", buildB='" + buildB + '\'' +
                ", buildC='" + buildC + '\'' +
                ", buildD='" + buildD + '\'' +
                '}';
    }
}

Worker

package com.kuang.builder.demo01;

//具体的建造者:工人
public class Worker extends Builder{

    //注意!!从后面回来的人提示,这里这个操作和思想非常重要.
    //下面的不是无参构造 不是传给工人的  是工人new出来的  这一步很重要
    private Product product;

    public Worker() {
        product = new Product();
    }


    @Override
    void buildA() {
        product.setBuildA("地基");
        System.out.println("地基");
    }

    @Override
    void buildB() {
        product.setBuildB("钢筋工程");
        System.out.println("钢筋工程");
    }

    @Override
    void buildC() {
        product.setBuildC("铺电线");
        System.out.println("铺电线");
    }

    @Override
    void buildD() {
        product.setBuildD("粉刷");
        System.out.println("粉刷");
    }

    @Override
    Product getProduct() {
        return product;
    }
}

Director

package com.kuang.builder.demo01;

//指挥:核心。负责指挥构建一个工程,工程如何构建,由它决定
public class Director {

    //指挥工人按照顺序建房子
    //worker是builder的具体实现, 一个builder可能有几个不同的worker
    //具体的worker可能不一样 参数写抽象类 到时候穿那个worker就执行哪个
    public Product build(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();

        return builder.getProduct();
    }
}

Test

package com.kuang.builder.demo01;

public class Test {
    public static void main(String[] args) {
        //指挥
        Director director = new Director();
        //指挥 具体的工人完成 产品
        Product build = director.build(new Worker());
        System.out.println(build.toString());
        //很简单嘛,产品实体product;制造图纸(抽象封装制造方法)builder;操作者具体实现(继承图纸,具体实现)worker;指挥官(封装调用执行者)director
    }
}

建造者模式的意图和适用场景(拓展)

  • 模式意图

将一个复杂的构件与其表示相分离,使得同样的构件过程可以创建不同的表示

  • 使用场景
    • 需要生成的产品对象有复杂的内部结构
    • 当一些基本部件不会变, 而其组合经常变化的时候
    • 需要生成的对象内部属性本身相互依赖

建造者的参与者(拓展)

Builder

抽象建造者,给出一个抽象接口, 以规范产品对象的各个组成成分的建造. 这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建

ConcreteBuilder

实现Builder接口, 针对不同的商业逻辑, 具体化复杂对象的各部分的创建, 即具体建造者

Director

指挥者, 调用具体建造者来创建复杂对象的各个部分, 在指导者不涉及具体产品的信息, 只负责保证对象各部分完整创建或者按某种顺序创建

Product

要创建的复杂对象, 即产品角色

举例说明: 设计房屋

总结 (拓展)

  • 建造者模式的使用使得产品的内部表象可独立的变化. 使用建造者模式可以使客户端不必知道产品内部组成的细节
  • 每一个Builder都相对独立, 而与其它的Builder无关
  • 可使得对构造过程更加精细控制
  • 将构建代码和表示代码分开
  • 建造者模式的缺点在于难于应付"分步骤构建算法"的需求变动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bqzGFp5e-1640520822316)(设计模式.assets/image-20211226165417876.png)]

代码再实现

Builder

package com.kuang.builder.demo02;

//建造者
public abstract class Builder {
    //为啥返回类型是builder
    //返回具体的实现类,这是返回父类名
    //返回自己可能是为了链式操作?
    abstract Builder builderA(String msg);//汉堡
    abstract Builder builderB(String msg);//可乐
    abstract Builder builderC(String msg);//薯条
    abstract Builder builderD(String msg);//甜点

    abstract Product getProduct();
}

Product

package com.kuang.builder.demo02;

//产品:套餐
public class Product {

    private String BuildA = "汉堡";
    private String BuildB = "可乐";
    private String BuildC = "薯条";
    private String BuildD = "甜点";

    public String getBuildA() {
        return BuildA;
    }

    public void setBuildA(String buildA) {
        BuildA = buildA;
    }

    public String getBuildB() {
        return BuildB;
    }

    public void setBuildB(String buildB) {
        BuildB = buildB;
    }

    public String getBuildC() {
        return BuildC;
    }

    public void setBuildC(String buildC) {
        BuildC = buildC;
    }

    public String getBuildD() {
        return BuildD;
    }

    public void setBuildD(String buildD) {
        BuildD = buildD;
    }

    @Override
    public String toString() {
        return "Product{" +
                "BuildA='" + BuildA + '\'' +
                ", BuildB='" + BuildB + '\'' +
                ", BuildC='" + BuildC + '\'' +
                ", BuildD='" + BuildD + '\'' +
                '}';
    }
}

Worker

package com.kuang.builder.demo02;
//具体的建造者
public class Worker extends Builder{

    private Product product;

    public Worker(){
        product = new Product();
    }
    @Override
    Builder builderA(String msg) {
        product.setBuildA(msg);
        return this;
        //this是return的当前Builder吧    错!!
        //这里返回的是Worker吧
        //this指向当前的worker对象
        //this就是当前调用buidlA的worker
        //this指现在的worker 这个woker调用另外几个build函数后,该worker中product的属性就变化了。返回this 即返回了worker和它其中的product
    }

    @Override
    Builder builderB(String msg) {
        product.setBuildB(msg);
        return this;
    }

    @Override
    Builder builderC(String msg) {
        product.setBuildC(msg);
        return this;
    }

    @Override
    Builder builderD(String msg) {
        product.setBuildD(msg);
        return this;
    }

    @Override
    Product getProduct() {
        return product;
    }

}

Test

package com.kuang.builder.demo02;

//这个模式就是默认一个套餐,如果你想自定义,本身抽象方法是多态的不管你设置哪个食物都可以同时修改其他的时候,做到了随意组合
public class Test {
    public static void main(String[] args) {
        //服务员
        Worker worker = new Worker();

        //一般情况
        //Product product = worker.getProduct();


        /*
        链式编程  : 在原来的基础上,可以自由组合了,如果不组合,也有默认的套餐
        */
        Product product = worker.builderA("全家桶").builderB("雪碧")
                .getProduct();

        System.out.println(product.toString());
    }
}

运行结果

Product{BuildA='全家桶', BuildB='雪碧', BuildC='薯条', BuildD='甜点'}

优缺点

优点:

  • 产品的建造和表示分离, 实现了解耦, 使用建造者模式可以使客户端不必知道产品内部组成的细节.
  • 将复杂产品的创建步骤分解在不同的方法中, 使得创建过程更加清晰
  • 具体的建造者类之间是相互独立的, 这有利于系统的扩展. 增加新的具体建造者无需修改原有的类库的代码, 符合"开闭原则"

缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似; 如果产品之间的差异性很大, 则不适合使用建造者模式, 因此其使用范围受到一定的限制
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类实现这种变化, 导致系统变得很庞大
  • 应用场景以及与抽象工厂模式对比

应用场景以及与抽象工厂模式对比

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ts1ccNfY-1640520822317)(设计模式.assets/image-20211226165641063.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3GPQm79R-1640520822318)(设计模式.assets/image-20211226165652170.png)]

原型模式

克隆

Prototype

Cloneable接口

clone()方法

/*
1.实现一个接口   Cloneable
2.重写一个方法   clone()
*/

代码实现

Video

package com.kuang.prototype.demo01;

import java.util.Date;

//Video
/*
1.实现一个接口   Cloneable
2.重写一个方法   clone()
 */

public class Video implements Cloneable{  //无良up主,克隆别人的视频!

    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

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

Bililili

package com.kuang.prototype.demo01;

import javax.swing.*;
import java.util.Date;

/*
客户端:克隆
 */
public class Bililili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象v1
        Date date = new Date();
        Video v1 = new Video("狂神说Java",date);
        Video v2 = (Video) v1.clone();

        System.out.println("v1=>" + v1);
        System.out.println("v2=>" + v2);

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

        date.setTime(112323324);
        System.out.println("v1=>" + v1);
        System.out.println("v2=>" + v2);

    }
}


/**
 *
 Date date = new Date();
 Video v1 = new Video("狂神说Java", date);
 System.out.println("v1=>" + v1);
 System.out.println("v1=>hash:" + v1.hashCode());

 //浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,
 //那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
 //其次,如果在clone方法里面对也对引用对象进行了一次拷贝,开辟了新空间地址,这时就是深拷贝

 //v1 克隆  v2
 //Video v2 = new Video("狂神说Java",date);

 Video v2 =(Video) v1.clone(); //克隆出来的对象和原来的是一模一样的
 System.out.println("v2=>" + v2);
 System.out.println("v2=>hash:" + v2.hashCode());

 //hash值不一样是以为他们是两个对象,在内存中的存储位置不同,
 //hash值得计算是通过对象在内存中的逻辑地址进行加工变换的
 */

运行结果

v1=>Video{name=‘狂神说Java’, createTime=Thu Dec 10 23:52:48 CST 2020}
v2=>Video{name=‘狂神说Java’, createTime=Thu Dec 10 23:52:48 CST 2020}

v1=>Video{name=‘狂神说Java’, createTime=Thu Jan 01 14:08:51 CST 1970}
v2=>Video{name=‘狂神说Java’, createTime=Thu Jan 01 14:08:51 CST 1970}

原理理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I0nGkOHh-1640520822318)(设计模式.assets/image-20211226165922041.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8yMr7hJ-1640520822318)(设计模式.assets/image-20211226165914282.png)]

代码再实现

Video

package com.kuang.prototype.demo02;

import java.util.Date;

//Spring Bean : 单例模式 原型模式
//原型模式+工厂模式  ==>   new<=>原型模式   new比较麻烦 换成克隆也就是原型模式进行创建

public class Video implements Cloneable{  //无良up主,克隆别人的视频!

    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {

        Object obj = super.clone();

        //实现深克隆的方法    序列化,反序列化。(和IO流相关,问题是效率过低)
        Video v = (Video)obj;
        //将这个对象的属性也进行克隆
        v.createTime = (Date) this.createTime.clone();

        return obj;
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

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

Bililili

package com.kuang.prototype.demo02;

import java.util.Date;

/*
客户端:克隆
 */

//Spring  Bean: 单例模式,原型模式
//原型模式 + 工厂模式  ==>  new  <==>  原型模式

public class Bililili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象v1
        Date date = new Date();
        Video v1 = new Video("狂神说Java",date);
        Video v2 = (Video) v1.clone();

        System.out.println("v1=>" + v1);
        System.out.println("v2=>" + v2);

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

        date.setTime(112323324);
        System.out.println("v1=>" + v1);
        System.out.println("v2=>" + v2);

    }
}


运行结果

v1=>Video{name=‘狂神说Java’, createTime=Thu Dec 10 23:56:38 CST 2020}
v2=>Video{name=‘狂神说Java’, createTime=Thu Dec 10 23:56:38 CST 2020}

v1=>Video{name=‘狂神说Java’, createTime=Thu Jan 01 14:08:51 CST 1970}
v2=>Video{name=‘狂神说Java’, createTime=Thu Dec 10 23:56:38 CST 2020}

适配器模式

结构性模式:

  • 作用

    从程序的结构上实现松耦合, 从而可以扩大整体的类结构,用来解决更大的问题

适配器模式就像USB网线转换器

代码实现

Adaptee

package com.kuang.adapter;

//要被适配的类: 网线
public class Adaptee {

    public void request(){
        System.out.println("连接网线上网");
    }
}

NetToUsb

package com.kuang.adapter;

//接口转换器的抽象实现
//适配器模式:把没有关联的东西关联起来
public interface NetToUsb {

    //作用:处理请求,网线==》usb
    public void handleRequest();
}

Adapter

package com.kuang.adapter;

//1.继承(类适配器,单继承)
//2.组合(对象适配器:常用)

//真正的适配器 , 需要连接USB , 连接网线
public class Adapter extends Adaptee implements NetToUsb{


    @Override
    public void handleRequest() {
        super.request();  //可以上网了
    }
}

Computer

package com.kuang.adapter;


//客户端类:想上网,插不上网线
public class Computer {

    //我们的电脑需要连接上转换器才可以上网
    public void net(NetToUsb adapter){
    //上网的具体实现, 找一个转接头
        adapter.handleRequest();
    }

    public static void main(String[] args) {
        //电脑,适配器,网线

        //电脑
        Computer computer = new Computer();
        //网线
        Adaptee adaptee = new Adaptee();
        //转接器
        Adapter adapter = new Adapter();
        //Adapter2 adapter = new Adapter2(adaptee);

        computer.net(adapter);    //GUI

    }
}

//适配器继承网线,适配器便不再是适配器   就是需要什么网线,自己插进去。不是继承
//所以这里就不在继承某一个特定网线了  适配器也要兼容其他网线
//所以用类似注入的方式实现  你给我什么网线 我就用什么网线   网线区别就在Adaptee中的request方法内容不一样

Adapter2

package com.kuang.adapter;

//组合(对象适配器:常用)
public class Adapter2 implements NetToUsb{

    //适配器继承网线,适配器便不再是适配器   就是需要什么网线,自己插进去。不是继承
    //所以这里就不在继承某一个特定网线了  适配器也要兼容其他网线
    //所以用类似注入的方式实现  你给我什么网线 我就用什么网线   网线区别就在Adaptee中的request方法内容不一样
    private Adaptee adaptee;

    //有参构造 这样创建一个适配器的时候就要传入你要用的网线 就可以实现不同网线注入    热拔插式的实现上网
    public Adapter2(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void handleRequest() {
        adaptee.request();//可以上网了
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IARg8TRZ-1640520822319)(设计模式.assets/image-20211226194218963.png)]

角色分析

  • 将一个类的接口转换成客户希望的另外一个接口. Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作

角色分析

  • 目标接口: 客户所期待的接口, 目标可以是具体的或抽象的类, 可以是接口
  • 需要适配的类: 需要适配的类或适配者类
  • 适配器: 通过包装一个需要适配的对象, 把原接口转换成目标对象

原理理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FIKXQFxX-1640520822319)(设计模式.assets/image-20211226194309602.png)]

  • 对象适配器优点
    • 一个对象适配器可以把多个不同的适配者适配到同一个目标
    • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据"里氏代换原则", 适配者的子类也可通过该适配器进行适配
  • 类适配器缺点
    • 对于Java, C#等不支持多重类继承的语言, 一次最多只能适配一个适配者类,不能同时适配多个适配者,
    • 在Java, C#等语言中, 类适配器模式中的目标抽象类只能为接口, 不能为类, 其使用有一定的局限性
  • 适用场景
    • 系统需要使用一些现有的类, 而这些类的接口(如方法名)不符合系统的需求,甚至没有这些类的源代码
    • 想创建一个可以重复使用的类, 用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作

桥接模式

桥接模式是将抽象部分与它的实现部分分离, 使他们都可以独立地变化, 它是一种对象结构型模式, 又称为柄体(Handle and Body)模式或接口(Interface)模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TX8pdTFW-1640520822319)(设计模式.assets/image-20211226200430529.png)]

分析:

这个场景中有两个变化的维度: 品牌, 类型

代码实现大脑所想

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tlTYwClX-1640520822319)(设计模式.assets/image-20211226200454803.png)]

代码实现

Brand

package com.kuang.bridge;

//品牌
public interface Brand {

    void info();

}

Apple

package com.kuang.bridge;

public class Apple implements Brand{
    @Override
    public void info() {
        System.out.print("苹果");
    }
}

Lenovo

package com.kuang.bridge;

//联想品牌
public class Lenovo implements Brand{
    @Override
    public void info() {
        System.out.print("联想");
    }
}

Computer.java

package com.kuang.bridge;

//抽象的电脑类型类
//为什么使用抽象类而不使用接口
//个人见解:    //需求:类型(电脑、平板)为"主类",品牌及其它的为零件类,需要把零件类全部组装到主类上。(零件类可以有多个)
//如果主类使用接口,接口不能定义属性,从而不能通过构造方法保证类型的全部组装
public abstract class Computer {

    //组合、品牌     桥
    protected Brand brand;

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

    public void info(){
        //自带品牌
        brand.info();
    }
}

class Desktop extends Computer{

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

    @Override
    public void info() {
        super.info();
        System.out.println("台式机");
    }
}



class Laptop extends Computer{

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

    @Override
    public void info() {
        super.info();
        System.out.println("笔记本");
    }
}
//适配是没关系的俩个类,桥接是有关系的只是各自发挥,品牌可以作为电脑的基本属性,通过类属性注入到电脑,进行一个组合
//适配器模式是通过别的接口完成自己完不成的功能;桥接模式是将变化的维度拆分再进行组合
//妙在了解耦,可扩展更强。品牌和电脑类型可以各自扩展,互不影响。

Test

package com.kuang.bridge;

//适配是没关系的俩个类,桥接是有关系的只是各自发挥,品牌可以作为电脑的基本属性,通过类属性注入到电脑,进行一个组合
//适配器模式是通过别的接口完成自己完不成的功能;桥接模式是将变化的维度拆分再进行组合
//妙在了解耦,可扩展更强。品牌和电脑类型可以各自扩展,互不影响。

public class Test {
    public static void main(String[] args) {
        //苹果笔记本
        Computer computer = new Laptop(new Apple());
        computer.info();

        //联想台式机
        Computer computer1 = new Desktop(new Lenovo());
        computer1.info();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ovI6VXx-1640520822319)(设计模式.assets/image-20211226200612663.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6kq1vBYq-1640520822320)(设计模式.assets/image-20211226200627635.png)]

优劣势分析

  • 好处分析:
    • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
    • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
  • 劣势分析:
    • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
    • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性.
  • 最佳实践:
    • 如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
    • 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
    • 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
  • 场景:
    • Java语言通过Java虚拟机实现了平台的无关性。
    • AWT中的Peer架构
    • JDBC驱动程序也是桥接模式的应用之一。

原理简单理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWA2t60G-1640520822321)(设计模式.assets/image-20211226200850210.png)]

代理模式 (难点)

为什么要学习代理模式?因为这就是SpringAOP的底层!【SpringAOP和SpringMVC】

代理模式的分类:

  • 静态代理
  • 动态代理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfOA8dzo-1640520822321)(设计模式.assets/image-20211201185029521.png)]

静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
  • 客户:访问代理对象的人

感觉用看电影来形容稍微容易理解一点,观众去电影院看电影,电影是剧组的,电影院里面有其他服务,这就通俗了嘛

接口(出租房子的事件)、真实角色(房东只需要出租房屋)、代理角色(中介可以增加其他业务)、客户

代码步骤:

  • 接口
//租房
public interface Rent {
    public void rent();
}


  • 真实角色

    //房东
    public class Host implements Rent{
        public void rent() {
            System.out.println("房东出租房子!");
        }
    }
    
    
    
  • 代理角色

    public class Proxy implements Rent{
        private Host host;
    
        public Proxy() {
        }
    
        public Proxy(Host host) {
            this.host = host;
        }
    
        public void rent() {
            host.rent();
            seeHouse();
            sign();
            fee();
        }
    
        //看房
        public void seeHouse(){
            System.out.println("中介带着看房子!");
        }
    
        //签合同
        public void sign(){
            System.out.println("和中介签署租赁合同!");
        }
    
        //收费用
        public void fee(){
            System.out.println("中介收取费用!");
        }
    }
    
    
    
  • 客户端访问代理角色

public class Client {
    public static void main(String[] args) {
        //房东要出租房子
        Host host = new Host();
//        host.rent();

        //代理,中介帮房东出租房子,并且代理角色一般会有一些附属操作!
        Proxy proxy = new Proxy(host);

        //不用面对房东,直接找中介租房即可!
        proxy.rent();
    }
}


代理模式的好处:

  • 可以使真实角色的操作更加存粹!不用去关注一些公共的业务
  • 公共交给了代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量会翻倍 开发效率变低

加深理解

代码步骤:

  1. 接口

    package com.kuang.demo02;
    
    
    public interface UserService {
    
        void add();
        void delete();
        void update();
        void query();
    }
    
    
  2. 真实角色

    package com.kuang.demo02;
    
    
    public class UserServiceImpl implements UserService {
    
        public void add() {
            System.out.println("增加了一个用户");
        }
    
    
        public void delete() {
            System.out.println("删除了一个用户");
        }
    
    
        public void update() {
            System.out.println("修改了一个用户");
        }
    
    
        public void query() {
            System.out.println("查询了一个用户");
        }
    }
    
    
  3. 代理角色

    package com.kuang.demo02;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    
    @Data@NoArgsConstructor@AllArgsConstructor
    public class UserServicePorxy implements UserService {
    
        private UserServiceImpl userServiceIml;
    
        public void setUserServiceIml(UserServiceImpl userServiceIml) {
            this.userServiceIml = userServiceIml;
        }
    
        public void add() {
            log("add");
            userServiceIml.add();
        }
    
    
        public void delete() {
            log("delete");
            userServiceIml.delete();
        }
    
    
        public void update() {
            log("update");
            userServiceIml.update();
        }
    
    
        public void query() {
            log("query");
            userServiceIml.query();
        }
    
        public void log(String msg){
            System.out.println("使用了" + msg + "方法");
        }
    }
    
    
  4. 客户端访问代理角色

    package com.kuang.demo02;
    
    
    public class Client {
    
        public static void main(String[] args) {
            UserServiceImpl userService = new UserServiceImpl();
            UserServicePorxy userServicePorxy = new UserServicePorxy();
            userServicePorxy.setUserServiceIml(userService);
            userServicePorxy.add();
        }
    }
    
    

聊聊AOP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l5ckgYCr-1640520822321)(设计模式.assets/image-20211201211903259.png)]

动态代理

  • 动态代理和静态代理角色一样

  • 动态代理的代理类是动态生成的,不是我们直接写好的

  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理

    • 基于接口:JDK动态代理
    • 基于类: cglib
    • java字节码实现: javasist

需要了解两个类:Proxy:代理 InvocationHandler:调用处理程序

代码步骤:

package com.kuang.demo04;

import com.kuang.demo03.Rent;

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


//稍后用这个类自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }


    //生成得到代理对象
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    //getProxy里面那个第三个参数this,就包含现在这个invoke函数,invoke是自己执的
    //proxy调用任意方法都会替换为handler的invoke方法,底层已经帮我们实现了
    //你看newProxyInstance方法里的this,就是把本类当作代理对象   所以下面重写了invoke方法
    //当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的`invoke`方法。 也就是在执行pih.getProxy()的时候,invoke被执行(分配方法?)了
    //就是这个public Object invoke,能根据接口,生成每一个对应的方法给新创建的代理类!!!
    //前面黄字的疑问解答:这里不是一个个赋予方法,是通过反射把代理类中被代理的类中的方法找出来。
    //处理代理实例并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());

        //动态代理的本质就是使用反射机制实现
        Object result = method.invoke(target, args);
        return result;
    }

    public void log(String msg) {
        System.out.println("执行了" + msg + "方法");
    }

}

package com.kuang.demo04;

import com.kuang.demo02.UserService;
import com.kuang.demo02.UserServiceImpl;


public class Client {
    public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();
        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //做三件事情
        /*
        1.代理接口   接口就是这个真实对象  因为对象会实现这个接口
        2.动态生成代理类
         */
        //设置要代理的对象
        //静态代理是代理真实对象,动态代理是代理接口
        pih.setTarget(userService);
        //动态生成代理类
        //注意这里一定要用UserService接口而不能是UserServiceImpl
        UserService proxy = (UserService) pih.getProxy();
        /*
        我强转成实现类会报cannot be cast,要强转成接口才不会报错,为什么?
        回复红字:实现(真实)类和得到的代理类,二者之间实际上没有任何关系,唯一就是他俩都实现了同一个接口,所以强转时候只能用接口来声明
        前面的红字,因为生成的代理类实现了共同的接口,代理类和被代理类(真实类)不是一个类
         */
        proxy.add();
        proxy.delete();
        proxy.query();
        proxy.update();
        //看这里  后面说不知道什么时候调用invoke方法的,代理实例调用方法时invoke方法就会被调用,可以debug试试
    }
}

动态代理的好处:(这里是JDK的动态代理 代理的接口)

  • 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
  • 公共角色就交给代理角色!实现了业务的分工!
  • 公共业务发生扩展的时候,方便集中管理!
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可!

静态代理不能是接口,你接口能直接实例化调用吗

说泛型那个 老师用的Object修饰接口 不是任何类型的接口都可以了吗

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值