几种常见设计模式初步了解

设计模式


**前记:**学习的时候遇到许多专有名词,如单例,工厂等等,出于好奇就先了解了几个常见的设计模式。总的来说,学完后只能 得其形而不得其意 ,要想真正了解里面的思想,需要在平时学习的时候思考。


一、概述

GoF(Gang of Foru 四人帮)23:是一种思维,一种态度,一种进步。

推荐书籍:《设计模式:可复用面向对象软件的基础》

1、设计模式基本要素
  • 模式名称:方便记忆
  • 问题:应用的环境,即什么时候使用设计模式
  • 解决方案:提供解决问题的抽象描述,而不是具体的解决方式
  • 效果:优缺点(如时间空间复杂度的平衡)
2、模式分类
  • 创建型模式

    单例,工厂,抽象工厂,建造者,原型 模式

  • 结构型模式

    适配器,桥接,装饰,组合,外观,享元,代理 模式

  • 行为型模式

    模板方法,命令,迭代器,观察者,中介者,备忘录,解释器,状态,策略,职责,访问者 模式

3、面向对象七大原则
  1. 开闭原则

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

    如新增模块时,不会影响原来的代码

  2. 里氏替换原则

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

    不要重写父类的方法**(?)**。

  3. 依赖倒置原则

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

    抽象不依赖细节,细节依赖抽象。

  4. 单一职责原则

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

    比如一个类的职责过多,当另一个类只需要其中一个功能时,不得不将所有的功能都交给这另一个类,从而冗余。

  5. 接口隔离原则

    要为各个类建立它们需要的专用接口。

  6. 迪米特原则(记忆为ABC原则)

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

    如A类和B类有关系,B类和C类有关系。不要让A类和C类直接”交流“。不恰当使用缺点:多了”中介类“,系统复杂了。

  7. 合成利用原则

    尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

二、单例模式

博文:https://www.cnblogs.com/youngdeng/p/12857446.html

**作用:**保证每一个类只有一个实例,并且提供一个访问该实例的全局访问点

应用场景:

  • Windows的任务管理器
  • Window的回收站
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 网站的计数器一般也会采用单例模式,可以保证同步
  • 数据库连接池的设计一般也是单例模式
  • Servelet也是单例
  • Spring的Bean默认也是单例
1、饿汉式
//饿汉式
public class Single {
    
    //出现的问题:在类加载的时候,
    //即使这个单例对象没有被使用,也会被分配空间,
    //造成空间浪费
    int[] a1 = new int[1000];
    int[] a2 = new int[1000];
    int[] a3 = new int[1000];
    
    private Single() {
        
    }
    
    private static Single single = new Single();
    
    public Single getInstance() {
        return single;
    }
}
2、懒汉式

改进一

在多线程下会出现非单例的情况出现。

//懒汉式
public class Single {
    private Single() {
        System.out.println(Thread.currentThread().getName());
    }

    private static Single single;

    public static Single getInstance() {
        if (single == null) {
            single = new Single();
        }
        return single;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                Single.getInstance();
            }).start();
        }
    }
}
/**
Thread-1
Thread-0
Thread-2
**/

改进二

在指令重排的情况会出错。new操作的时候,不是一个原子性操作。

指令重排:

  1. 分配内存空间
  2. 执行构造方法,初始化对象
  3. 把这个对象指向空间

比如,A线程第一次执行时,正常132。当B线程进来时,如果这个时候,A恰好在3,则 if (single == null) 为false, 则B线程会直接返回,出现错误。

//懒汉式
public class Single {
    private Single() {
        System.out.println(Thread.currentThread().getName());
    }

    private static Single single;

    public static Single getInstance() {
        //第一重判断可以加,也可以不加
        //加的原因是,不用每次进来都要得到锁或者说等待其它程序释放锁,浪费资源
        if (single == null) { 
            synchronized (Single.class) {
                if (single == null) {
                    single = new Single();
                }
            }
        }
        return single;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                Single.getInstance();
            }).start();
        }
    }
}

最终改进

加上volatile防止指令重排

//懒汉式
public class Single {
    private Single() {
        System.out.println(Thread.currentThread().getName());
    }

    private static volatile Single single;

    public static Single getInstance() {
        //第一重判断可以加,也可以不加
        //加的原因是,不用每次进来都要得到锁或者说等待其它程序释放锁,浪费资源
        if (single == null) { 
            synchronized (Single.class) {
                if (single == null) {
                    single = new Single();
                }
            }
        }
        return single;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                Single.getInstance();
            }).start();
        }
    }
}
3、静态内部类
//静态内部类
public class Single {
    private Single() {
    }
    
    public Single getInstance() {
        return InnerClass.single;
    }

    static class InnerClass {
        private static final Single single = new Single();
    }
}
4、枚举(最佳)

上述的模式都可以被反射破坏单例。通过反射得到构造器,破坏单例模式

三重检测,红绿灯标识都不能很好解决。

而枚举自带防止反射创建对象的方式。new instance()方法中源码如下throw new IllegalArgumentException(“Cannot reflectively create enum objects”);。

//枚举
public enum  Single {
    INSTANCE;
    
    public Single getInstance() {
        return INSTANCE;
    }
}
三、工厂模式
简介
  1. 作用:实现了创建者和调用者的分离
  2. 核心本质:
    • 实例化对象不使用new,用工厂的方法代替
    • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。就是迪米特法则啦。
  3. 分类:
    • 简单工厂模式
    • 工厂方法模式
    • 抽象工厂模式
  4. 七大原则:
    • 开闭原则:在简单工厂模式中,要扩展就必需改变原来代码,违反了。而工厂方法则遵守。
    • 依赖倒转原则:面向接口,如本例中Car接口和CarFactory接口
    • 迪米特法则:创建者和调用者的分离就体现出这个法则。不用new方式,而用工厂这个“中间商”。
0、传统方法

如果要创建一个对象的时候初始化参数很多,工厂模式就体现出用处了。

public interface Car {
    void name();
}

public class ACar implements Car {
    @Override
    public void name() {
        System.out.println("is a ACar");
    }
}

public class BCar implements Car {
    @Override
    public void name() {
        System.out.println("is a BCar");
    }
}

public class Consumer {
    public static void main(String[] args) {
        Car aCar = new ACar();
        Car bCar = new BCar();
    }
}
1、简单工厂(静态工厂)模式
  • 一个工厂集合,生产各种产品。

  • 用来生产同一等级结构中的任意产品。对于增加新的产品,需要改变原来代码。

  • 扩展的时候,违反了开闭原则

public interface Car {
    void name();
}

public class ACar implements Car {
    @Override
    public void name() {
        System.out.println("is a ACar");
    }
}

public class BCar implements Car {
    @Override
    public void name() {
        System.out.println("is a BCar");
    }
}

//简单工厂模式
public class CarFactory {
    //方法一:
    public static Car getCar(String name) {
        if ("ACar".equals(name)) {
            return new ACar();
        } else if ("BCar".equals(name)){
            return new BCar();
        } else {
            return null;
        }
    }

    //方法二:为每一种车增加方法
    public static ACar getACar() {
        return new ACar();
    }
    public static BCar getBCar() {
        return new BCar();
    }
}
2、工厂方法模式

没有什么是加一层解决不了的。

用来生产同一等级结构中的固定产品,支持增加任意产品。

一种产品对应一种工厂。

通过Car接口和CarFactory接口,实现了横向扩展,不改变原来的任何代码。

public interface Car {
    void name();
}

public interface CarFactory {
    Car getCar();
}




public class ACar implements Car {
    @Override
    public void name() {
        System.out.println("is a ACar");
    }
}

public class ACarFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new ACar();
    }
}

public class BCar implements Car {
    @Override
    public void name() {
        System.out.println("is a BCar");
    }
}

public class BCarFactory implements CarFactory {
    @Override
    public Car getCar() {
        return new BCar();
    }
}
3、两大方法对比

从结构复杂度、代码复杂度、编程复杂度、管理上的复杂度上,简单工厂模式更胜一筹。

根据设计原则,则工厂方法模式更胜一筹。

但实际应用中,大多数用的是静态工厂模式~!

四、抽象工厂模式
1、产品族和产品等级

首先理解产品族和产品等级的概念。

可以简单理解为产品等级定义了横坐标的数量,而产品族定义了纵坐标的数量。

两个坐标x 和 y 可以确定一个唯一的,具体的产品。

如横坐标路由器,纵坐标小米品牌,可以确定是小米牌的路由器。
在这里插入图片描述

2、抽象工厂官方UML图

在这里插入图片描述

3、具体例子包结构图

在这里插入图片描述

4、具体例子UML图

从包结构中可以看出,一个抽象工厂接口中,定义了横坐标的数量,而抽象工厂的具体实现类则是确定了纵坐标的数量。

如本例中,AbstarctFactory接口中,两个方法规定了生产Phone和Router, 确定了两个横坐标。而AbstarctFactory接口的具体实现类,HuaWeiFactory和XiaoMiFactory则确定了纵坐标的数量。而纵坐标中(如小米工厂),对横坐标的具体创建(new了一个小米手机并返回),则体现了横纵坐标相交的过程,唯一确定了具体的产品。

从横坐标的关系看,如果要增加横坐标的数量,则要改变抽象工厂的代码,从而改变具体实现工厂中的方法,要改的地方很多。

从纵坐标的关系看,如果要增加纵坐标的数量,则新建一个实现了抽象工厂接口的类即可,不用动其它任何代码。

在这里插入图片描述

5、官方术语
  1. 定义:抽象工厂模式提供了一个创建一系列或者相互依赖对象的接口,无需指定它们具体的类。(即提供了一个创建纵坐标数量的模板)
  2. 适用场景:
    • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。(就普通工厂作用)
    • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
    • 提供一个产品类的库(纵坐标),所有的产品以同样的接口(横坐标)出现,从而使得客户端不依赖于具体要求的实现。
  3. 优点:
    • 具体产品在应用层的代码隔离,无需关心创建的细节
    • 将一个系列的产品统一到一块创建
  4. 缺点:
    • 规定了所有可能被创建的产品集合(即把横坐标定死),产品族中扩展新的产品困难(增加横坐标会改变大量代码,开闭原则被破坏)
    • 增加了系统的抽象性和理解难度
五、建造者模式
方式一:指挥者

由挥者决定建造的顺序及细节。

顺序何以体现?比如一个类有很多属性,其中一些属性的赋值先依赖于另外一个属性的赋值,那么此时属性的赋值就产生了先后顺序,就不能把建造一个类细节顺序交给用户了。

是建造者模式的常规用法,Director具有核心作用,指导具体构建者如何构建产品,控制调用 先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把Director和抽象建造者进行结合。

public abstract class Builder {
    abstract void buildA();
    abstract void buildB();

    abstract Product getProduct();
}

public class Worker extends Builder {
    private Product product;

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

    @Override
    void buildA() {
        product.setBuildA("buildA");
        System.out.println("进行步骤A");
    }

    @Override
    void buildB() {
        product.setBuildB("buildB");
        System.out.println("进行步骤B");

    }

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

public class Product {
    private String buildA;
    private String buildB;

    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;
    }

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

public class Director {
    Product getProduct(Builder builder) {
        builder.buildA();//由挥者决定建造的顺序及细节
        builder.buildB();
        return builder.getProduct();
    }
}

public class Client {
    public static void main(String[] args) {
        Director director = new Director();
        Product product = director.getProduct(new Worker());
        System.out.println(product);
    }
}
方式二:链式

由客户决定建造的顺序。但如果客户不DIY,则产品本身就有一个默认的属性。

通过静态内部类方式实现零件无序装配构造,这种方式使用更加灵活,更符合定义。内部有复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,并且无需改变具体的构造方式,就可以生产出不同复杂产品。

public abstract class Builder {
    abstract Builder buildA();
    abstract Builder buildB();

    abstract Product getProduct();
}

public class Worker extends Builder {
    private Product product;

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

    @Override
    Builder buildA() {
        product.setBuildA("buildA");
        System.out.println("进行步骤A");
        return this;
    }

    @Override
    Builder buildB() {
        product.setBuildB("buildB");
        System.out.println("进行步骤B");
        return this;
    }

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

public class Product {
    //有默认的属性
    private String buildA = "bulidA";
    private String buildB = "bulidB";

    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;
    }

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

public class Client {
    public static void main(String[] args) {
        Product product = new Worker()//链式编程
                .buildA()
                .buildB()
                .getProduct();
    }
}
概述
  1. 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  2. 建行者模式也属于创建型模式,它提供了一种创建对象的最佳方式
  3. 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
  4. 优点:
    • 产品的建造和表示分离,实现了解耦。使用建造者模式可以使客户端不必知道产品内部组成的细节
    • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 具体的建造者类之间是相互独立的,这有篮球系统的扩展。增加新的具体建行者无需修改原有类库的代码,符合”开闭原则“。(利益于抽象类的定义)
  5. 缺点:
    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
  6. 应用场景:
    • 需要生成的产品对象胡复杂的内部结构,这些产品对象具备共性
    • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
    • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程
  7. 建造者 与抽象工厂模式的比较
    • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
    • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建行步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象
    • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!
六、原型模式
  • prototype

  • 场景:创建一个对象的过程极其复杂。假设已经创建了A对象,又想创建另一个B对象,那么可以直接把A克隆到B,然后对B进行部分修改即可。

  • 涉及到的问题:深克隆与浅克隆。

  • 问题根源:Object中clone方法只是对栈地址进行复制,如果是基本类型,会直接复制一份;如果是引用类型,那么只是将引用复制了一份,实际上还是指向同一个对象。

  • 深克隆:

  1. 调用super.clone()方法后。对每个引用类型,自己再克隆赋值一遍
  2. 利用IO知识,进行序列化与反序列化
七、适配器模式

根据自己理解,可以理解为迪米特法则,即ABC法则。

1、概述
  1. 属于结构型模式,作用:从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
  2. 两种实现方式:
    • 继承:类适配器,单继承
    • 组合:对象适配器,常用
  3. 将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。比如下例中,电脑上网方法void net(NetToUSB netToUSB), 已经将接口写死了,不能直接通过网线上网。把NetToUSB写成USB更好理解。
  4. 角色分析:
    • 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。(电脑USB接口)
    • 需要适配的类(适配者):需要适配的类或适配者类(网线)
    • 适配器:通过包装一个需要适配的对象,把原接口转换成目标对象
  5. 对象适配器优点:
    • 一个对象适配器可以把多个不同的适配者适配到同一个目标
    • 可以适配一个适配者的子类。由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配
  6. 类适配器缺点:
    • 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
    • 在Java、C#等语言中,**类适配器模式中的目标抽象类只能为接口,不能为类,**其使用有一定的局限性
  7. 适用场景:
    • 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码
    • 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
    • 如系统2.0要升级为3.0, 可以通过适配器,包装2.0的一些类,适配到3.0的类去使用。
2、例子
//被适配的类,比如网线
public class Adaptee {

    void doSomething() {
        System.out.println("通过网线上网");
    }
}

//适配器接口
public interface NetToUSB {

    void connect();
}

//通过继承方式
public class Adapter extends Adaptee implements NetToUSB{

    @Override
    public void connect() {
        super.doSomething();
    }
}


//通过组合方式
public class Adapter implements NetToUSB{
    private Adaptee adaptee;

    @Override
    public void connect() {
        adaptee.doSomething();
    }
}


public class Computer {

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

在这里插入图片描述

经典x y 坐标。

在这里插入图片描述

在这里插入图片描述

//抽象出来的品牌接口
public interface Brand {

    void info();
}

//抽象出来的电脑类
public abstract class Computer {
    //通过属性,建立一座桥
    protected Brand brand;

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

    void info() {
        brand.info();
    }
}

public class XiaoMiBrand implements Brand {
    @Override
    public void info() {
        System.out.println("小米品牌");
    }
}

public class Desktop extends Computer {

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

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

public class Client {
    public static void main(String[] args) {
        Computer computer = new Desktop(new XiaoMiBrand());
        computer.info();
    }
}
九、静态代理

注意和适配器模式比较。

**横向开发!**代理是横向开发的经典例子!

1、概述
  1. 角色分析:
    • 抽象角色:一般会使用接口或者抽象类来解决。(根据自己理解,是为了统一代理角色和真实角色的行为)
    • 真实角色:被代理的角色
    • 代理角色:代理真实角色,代理真实角色后,做一些附属操作
    • 客户:访问代理对象的人
  2. 好处:
    • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
    • 公共业务交给了代理角色,实现了业务分工
    • 公共业务发生扩展的时候,方便集中管理。(方法多的时候,要做日志,如果直接在直接角色中做要做大量重复代码)
  3. 缺点:
    • 一个真实角色就会产生一个代理角色,代码量会翻倍,开发效率变低
2、实例一

接口:统一代理和真实角色的 行为

//抽象角色,或者说抽象事件
public interface Rent {
    void rent();
}

真实角色

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

代理

//代理角色
public class Proxy implements Rent{
    private Host host;

    public Proxy(Host host) {
        this.host = host;
    }

    @Override
    public void rent() {
        beforeRent();
        host.rent();
        afterRent();
    }

    public void beforeRent() {
        System.out.println("租房前手续");
    }

    public void afterRent() {
        System.out.println("租房后手续");
    }
}

客户

//客户角色
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}
十、动态代理

大量应用了反射!

1、概述
  1. 角色分类:动态代理和静态代理一样
  2. 动态含义:动态代理的代理类是动态生成的,不是直接先写好的
  3. 动态代理分为两大类:基于接口的动态代理和基于类的动态代理
    • 基于接口:JDK动态代理
    • 基于类: cglib
    • Java字节码实现:javasist
  4. 基于接口两个类:代理:Proxy 调用处理程序:InvocationHandler
  5. 好处:
    • 静态代理的好处都有
    • 一个动态代理类代理的是一个接口,一般对应的是一类业务
    • 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
2、例子
//抽象角色,或者说抽象事件
public interface Rent {
    void rent();

    void out();
}

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

    @Override
    public void out() {
        System.out.println("房东回收房子");
    }
}

public class MyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public MyInvocationHandler(Rent rent) {
        this.rent = rent;
    }

    //这一步可以不用写在这个类里面。
    //得到代理类的关键步骤
    Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(method.getName());
        Object invoke = method.invoke(rent, args);
        after();
        return invoke;
    }

    public void before(String name) {
        System.out.println("记录一下方法名" + name);
    }

    public void after() {
        System.out.println("记录两下");
    }
}

//客户角色
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(host);
        Rent proxy = (Rent)myInvocationHandler.getProxy();
        proxy.rent();
        proxy.out();
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Meow_Sir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值