23种设计模式

储备知识

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

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

设计模式的基本要素
  • 模式名称
  • 问题
  • 解决方案
  • 效果
23种设计模式

在这里插入图片描述

  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
  • 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  • 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  • 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
  • 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
  • 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
  • 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
  • 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
  • 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
  • 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
  • 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  • 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
  • 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
  • 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
  • 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
  • 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
  • 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
  • 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
  • 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

OOP七大原则

1.开闭原则

对扩展开放,对修改关闭。

当需求发生变化时,在不修改软件实体源代码的情况下,可以进行功能扩展,使其满足新需求。

2.里氏替换原则

继承必须确保超类所拥有的性质在子类中仍然成立。

通俗来讲就是,子类可以扩展父类的功能,但不能改变父类原有的功能,即不可以重写父类的方法

如果通过重写父类方法扩展新功能,写起来可能会很简单,但这会使整个继承体系的可复用性变得很差,特别是运用多态比较频繁时,程序很有可能会运行错误。

典型案例,“正方形不是长方形”,“鸵鸟不是鸟”。

3.依赖倒置原则

要面向接口编程,不要面向实现编程。

原始定义为:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象

依赖倒置原则是实现开闭原则重要途径之一。

4.单一职责原则

控制类的粒度大小,将对象解耦,提高其内聚性。

职责指类变化的原因,一个类应当有且只有一个引起它变化的原因,若有多个则该类需要拆分。如果一个对象承担了太多职责,至少会存在以下两个缺点:

一、当一个职责变化时,可能会削弱或抑制该类实现其他职责的能力。

二、当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。

单一职责同样也适用于方法。一个方法应该尽可能做好一件事情,如果一个方法处理的事情太多,其颗粒度会变得很粗,不利于重用。

5.接口隔离原则

为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:

单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

6.迪米特法则

只与你的直接朋友交谈,不跟“陌生人”说话

简单来说就是,如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

但过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

7.合成复用原则

软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。组合是“has a”关系,继承是“is a”关系。

组合就是A类的对象是B类的成员变量,相当于 A类是B类对象的一个属性。
例如,Dog类和Animal类应该使用继承关系,因为狗是动物;Person类和Head类应该使用组合关系,因为头是组成人体的一部分,即人有头。

如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

创建型模式

1.单例模式(不允许new对象)

构造器私有,别人无法new对象,以保证构造器内只有一个对象

1.1饿汉式

直接加载所有对象——可能造成资源浪费(没用到却已经申请了内存)

//饿汉式单例
public class Hungry {
   //浪费内存
    private byte[] data1=new byte[1024*1024];
    private byte[] data2=new byte[1024*1024];
    
    private  Hungry(){
    }
    private final static  Hungry HUNGRY=new Hungry();
    public static  Hungry getInstance(){
        return HUNGRY;
    }

1.2枚举和懒汉式——DCL懒汉式

双重检测锁模式 ——DCL懒汉式

代码:
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"ok");
    }

    private  static LazyMan Lazyman;
//双重检测锁模式
    public static LazyMan getLazyman(){
        //加锁来解决并发问题
        //双重if检测
         if(Lazyman==null){
            synchronized (LazyMan.class){
                if(Lazyman==null){
                    Lazyman=new LazyMan();//不是一个原子操作
                    //1.分配内存空间
                    //2.执行构造方法,初始化对象
                    //3.把这个对象指向这个空间
                }
            }
        }
        return Lazyman;
    }
    //单线程下单例OK
    //多线程并发测试代码
    public static void main(String[] args){
        for (int i=0;i<10;i++){
            new Thread(()->{
                LazyMan.getLazyman();
            }).start();
        }
    }

**产生问题及解决:
问题1: Lazyman=new LazyMan(); 不是一个原子操作
  • 1.分配内存空间
  • 2.执行构造方法,初始化对象
  • 3.把这个对象指向这个空间
    多线程时可能发生指令重排问题,执行顺序可能不是123而是312等

解决方案:加volatile

private volatile static LazyMan Lazyman;
问题2:暴力反射可以破坏私有性而新建对象

测试代码:输出两个不同的值,说明破坏了单例模式

 //反射!!
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
       LazyMan instance=LazyMan.getInstance();
        Constructor<single.LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        single.LazyMan instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);  //输出两个不同的值,说明破坏了单例模式
    }
问题3:使用两次反射来破坏私有性而新建对象

测试代码:

single.LazyMan instance = declaredConstructor.newInstance();
single.LazyMan instance1 = declaredConstructor.newInstance();

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
      // LazyMan instance=LazyMan.getInstance();
        Constructor<single.LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        single.LazyMan instance = declaredConstructor.newInstance();
        single.LazyMan instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);
    }

解决方案:红绿灯(设置标志位)

  • 对wodebianliang保密即可
  • 若被反编译得到变量名就不安全了
 private static  boolean wodebianliang=false;
    private LazyMan(){
        synchronized(LazyMan.class){
            if(wodebianliang==false){
                wodebianliang=true;
            }else 
                throw new RuntimeException("不要试图用反射破坏异常");
        }
    }
问题4:标志位被破解时,需要利用源码的枚举特性来防止反射

枚举的源码中只有有参构造,没有无参构造

NoSuchMethodException: single.EnumSingle.()

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1= EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
       // NoSuchMethodException: single.EnumSingle.<init>()
        //枚举的源码中只有有参构造,没有无参构造
        System.out.println(instance1);
        System.out.println(instance2);

使用有参构造方法后,抛出正确的异常

IllegalArgumentException: Cannot reflectively create enum objects

 Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
得出结论:反射不能破坏枚举!!

1.3 静态内部类

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

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

}

2.工厂模式(创建者和调用者分离)

满足:开闭原则,依赖倒转原则,迪米特法则

  • 工厂模式:
    • 核心本质:实例化对象不使用new,用工厂方法代替

    • 将选择实现类,创建对象通过管理和控制,从而实现调用者和实现类解耦。

    • 三种模式

      • 简单工厂模式
        • 用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
      • 工厂方法模式
        • 用来生产同一等级结构中的固定产品(支持增加任意产品)
      • 抽象工厂模式
        • 围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。不能增加产品,可以增加产品族。

2.1简单工厂模式

需要修改factory才能实现新增对象 “dazhong”
工厂文件代码:

//静态工厂模式--需要修改factory才能实现新增对象 “dazhong”
//开闭原则无法满足
public class CarFactory {
    //方法一
    public static Car getCar(String car){
        if(car.equals("wuling")){
            return new WuLing();
        }else if(car.equals("tesila")){
            return new Tesila();
        }else  return null;

    }
    //方法二
    public static Car getWuling(){return new WuLing();}
    public static Car getTesila(){return new Tesila();}

调用工厂文件的代码:

public class Consumer {
    public static void main(String[] args) {
        //接口,实现类
        WuLing car1 = new WuLing();
        Tesila car2 = new Tesila();
        //使用工厂创建
        Car car = CarFactory.getCar("wuling");

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

2.2工厂方法模式

多了一个CarFactory接口和多个继承该接口的Factory类
在这里插入图片描述
CarFactory接口代码:

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

TesilaFactory类实现CarFactory接口:

public class TesilaFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Tesila();
    }
}

调用工厂文件的代码:

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

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

对比:

结构复杂度 simple
代码复杂度 simple
编程复杂度 simple
管理上的复杂度 simple

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

3抽象工厂模式

3.1定义:抽象工厂模式提供了一个创建一系列相关或互相依赖对象的接口,无需指定他们具体的类

在这里插入图片描述

3.2结构变化

在这里插入图片描述

  • 多了抽象接口IProductFactory(超级工厂),HuaWeiFactory和XiaoMiFactory都继承该接口
//抽象产品工厂
public interface IProductFactory {
    //生产手机
    IphoneProduct iphoneProduct();
    //生产路由器
    IRouterProduct irouterProduct();
}
public class HuaWeiFactory implements IProductFactory{
    @Override
    public IphoneProduct iphoneProduct() {
        return new HuaWeiPhone();
    }

    @Override
    public IRouterProduct irouterProduct() {
        return new HuaWeiRouter();
    }
}
  • 测试文件:先new小米工厂,再调用手机工厂
    在这里插入图片描述

4.建造者模式(用于复杂对象的建造和表示分离)

提供创建对象的最佳模式

4.1结构:

在这里插入图片描述
worker是抽象类builder的实现类
director是builder的指导

在这里插入图片描述

4.2代码:

Builder:抽象类,是抽象的建造者。具体实现有对应实体类负责
//抽象的建造者:方法
public abstract class Builder {
    abstract void buildA();//地基
    abstract void buildB();//钢筋
    abstract void buildC();//电线
    abstract void buildD();//粉刷
    //完工:得到产品
    abstract Product getProduct();
}
Director:指挥者,指挥抽象建造者进行建造。返回产品。决定构建顺序。
//指挥:核心,负责指挥构建一个工程
public class Director {
    //指挥工人安装顺序建房子
    public Product build(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();

        return builder.getProduct();
    }
}

Worker(ConCreteBuilder): 抽象类Builder的实体类,new Product();是实际建造者。重写抽象类中的get和set方法。调用product中方法实现构建。
//具体的建造者:工人
public class Worker extends Builder{
    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;
    }
}

Product:具体的产品。为Worder提供构建产品的get和set等方法。
//具体的产品:房子
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 + '\'' +
                '}';
    }
}

4.3 测试

public class Test {
    public static void main(String[] args) {
        //指挥
        Director director = new Director();
        //指挥具体工人完成产品
        Product build = director.build(new Worker());
        System.out.println(build.toString());
    }

}

4.4静态内部类方式(不需要Director)

在这里插入图片描述

静态内部类使用场景一般是当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候会考虑采用静态内部类的设计

静态内部类调用外部类的构造函数,来构造外部类,静态内部类可以被单独初始化

4.4.1Builder
//抽象的建造者:方法
public abstract class Builder {
    //传递参数String msg,实现用户自定义
    abstract Builder buildA(String msg);//汉堡
    abstract Builder buildB(String msg);//可乐
    abstract Builder buildC(String msg);//薯条
    abstract Builder buildD(String msg);//炸鸡
    //完工:得到产品
    abstract Product getProduct();
}
4.4.2Product
//具体的产品:套餐
public class Product {
    private String buildA="汉堡";
    private String buildB="可乐";
    private String buildC="薯条";
    private String buildD="炸鸡";

//既有默认值,又设置set方法,以实现用户点套餐和自定义两种功能需求
    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 + '\'' +
                '}';
    }
}

4.4.3Worker
//具体的建造者:工人
public class Worker extends Builder {
    private Product product;
    public Worker(){
        product=new Product();   //注意:工人负责创建产品
    }

   // Builder是worker的静态内部类
    @Override
    Builder buildA(String msg) {
        product.setBuildA(msg);
        return this;
    }

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

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

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

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

4.5应用场景及与抽象工厂模式的比较

在这里插入图片描述

5.原型模式

应用:

Spring中的bean 可以用单例模式或者原型模式创建
原型模式通常和工厂模式合作使用 : new工厂的操作用原型模式来替换

浅拷贝和深拷贝

在这里插入图片描述

浅拷贝

拷贝设置

/*
1.实现一个接口 Cloneable
2.重写一个方法 clone
*/
//Video
public class Video implements Cloneable{    //克隆别人的视频的up
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    public Video(){
    }
    //其他省略

测试:

public class Bilibili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象 v1
        Date date=new Date();
        Video v1=new Video("狂神说JAVA",date);
        System.out.println("v1"+v1);
        System.out.println("v1=>hash:"+v1.hashCode());

        //v1 克隆出 v2
        //克隆出的对象和原对象一模一样
       Video v2= (Video) v1.clone();
        System.out.println("v2"+v1);
        System.out.println("v2=>hash:"+v1.hashCode());
        v2.setName("Clone 狂神说JAVA");
        System.out.println(v2);

    }

深拷贝
  • 序列化和反序列化
  • 改造克隆方法
 @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        //实现深克隆
        Video v=(Video) obj;
        //将对象的属性也进行克隆
        v.createTime=(Date) this.createTime.clone();
        return  obj;
    }

结构型模式

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

6.适配器模式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.需要适配的类:Adaptee 网线

//要被适配的类:   网线
public class Adaptee {
    //实现它的功能 : 上网
    public void request(){
        System.out.println("连接网线上网了");
    }
}

2.目标接口(客户期待的接口):Computer 电脑

//客户端:想上网,插不上网线
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 adapter2 = new Adapter2(adaptee);

        computer.net(adapter);
        computer.net(adapter2);
    }
}

3.适配器Adapter 网线—电脑转换连接接口

3.1适配器接口:NetToUSB
//接口转换器的抽象实现~
public interface NetToUSB {
    //作用:处理请求, 网线=>USB
    public void handleRequest();
}

3.2真正的适配器
3.2.1 继承(类适配器,单继承)
//真正的适配器,  需要连接USB,  需要连接网线
public class Adapter extends Adaptee implements NetToUSB{
    @Override
    public void handleRequest() {
        super.request(); //可以上网了
    }
}
3.3.2 组合(对象适配器:常用)
//不再继承Adaptee,因为转接口需要能接任意网线
public class Adapter2 implements NetToUSB{
    private Adaptee adaptee;  //把网线组合进来
    public Adapter2(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void handleRequest() {
        adaptee.request();
    }
}

7.桥接模式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.1 品牌

接口
//品牌
public interface Brand {
     void info();

}
具体品牌(实现类)
public class Apple implements Brand {
    @Override
    public void info() {
        System.out.println("苹果");
    }
}
public class Lenovo implements Brand{
    @Override
    public void info() {
        System.out.println("联想");
    }
}

7.2 电脑类型(自带品牌属性) 组合

抽象类
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("笔记本");
    }
}

8.代理模式

  • 代理模式是SpringAOP的底层
  • 代理模式分为静态代理动态代理

8.1静态代理模式

角色分析:
  • 抽象角色:一般用抽象类或接口来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后我们一般会进行一些附属操作
  • 客户:访问代理对象的人
代码步骤:

1.接口

public interface Rent {
    public void rent();
}

2.真实角色

//房东
public class Host implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

3.代理角色

public class Proxy implements Rent{
    private Host host;
    public Proxy(){}
    public Proxy(Host host){
        this.host=host;
    }

    @Override
    public void rent() {
        host.rent();
    }
    //看房
    public void seeHouse(){
        System.out.println("中介带你看房子");
    }
    //签合同
    public void hetong(){
        System.out.println("中介和你签合同");
    }

}

4.客户端访问代理角色

    public static void main(String[] args) {
        //房东要租房子
        Host host=new Host();
        //中介(代理角色)代理房东租房子,  同时也有一些附属操作
        Proxy proxy = new Proxy(host);
        //你不用找房东,直接找中介租房
        proxy.rent();
        proxy.seeHouse();
        proxy.hetong();
    }
}
静态代理模式的优缺点:
  • 优点:
    • 可以使真实角色的操作更纯粹,不用关注一些公共业务
    • 公共业务就交给代理角色,实现了业务分工
    • 公共业务发生扩展时,方便集中管理
  • 缺点:
    • 一个真实角色对应一个代理角色,代码量翻倍,开发效率低

加深理解

接口
public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

真实角色实现接口
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("进行了增加");
    }

    @Override
    public void delete() {
        System.out.println("进行了删除");
    }

    @Override
    public void update() {
        System.out.println("进行了修改");
    }

    @Override
    public void query() {
        System.out.println("进行了查询");
    }
}
代理角色实现接口,聚合真实角色,并增加附属功能
public class UserServiceProxy implements UserService{
    private  UserServiceImpl userService;

    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }

    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

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

}

客户端调用代理角色
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.delete();
    }
}

在这里插入图片描述

8.2动态代理模式

  • 动态代理的底层是反射
  • 动态代理和静态代理的角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口–JDK动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javasist

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

真实角色和接口同上
代理角色
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//用这个类,自动生产代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private  Rent rent;
    public void setRent(Rent rent){
        this.rent=rent;
    }

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


    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理的本质就是使用反射机制实现
        seeHouse();
        Object result=method.invoke(rent,args);
        fare();
        return null;
    }
    public void seeHouse(){
        System.out.println("中介带你看房子");
    }
    public void fare(){
        System.out.println("中介收费");}
}

客户端
  public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色:现在没有
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们要调用的接口对象
        pih.setRent(host);

        Rent proxy = (Rent)pih.getProxy();  //这里的proxy就是动态生成的,我们没有写它
        proxy.rent();

    }

在这里插入图片描述

进一步抽象

代理角色(固定代码,以后可以直接用)

//用这个类,自动生产代理类
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);
    }

    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());  //method反射
        Object result=method.invoke(target,args);
        return null;
    }


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

}

客户端
public static void main(String[] args) {
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();

        //代理角色,不存在,要生成
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
       //设置要代理的对象
        pih.setTarget(userService);
        //动态生成代理类
        UserService proxy =(UserService) pih.getProxy();

        //执行操作
        proxy.delete();
    }
优点:
  • 可以使真实角色的操作更纯粹,不用关注一些公共业务
  • 公共业务就交给代理角色,实现了业务分工
  • 公共业务发生扩展时,方便集中管理
  • 一个动态代理类代理的是一个接口,一般就是对应的一类业务
  • 一个动态代理可以代理多个类,只要是实现了同一个接口
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值