结构型模式

适配器模式(Adapter)

例如我们给手机充电,插座的固定电压是220v,而我们的手机能承受的电压只有5v,这个时候我们需要用到我们的充电器将220v的电压转换成5v,而这个时候手机充电器就起到啦适配器的作用;

适配器模式就是将一个类的接口转换成客户希望的另外一个接口,模式最大的作用还是将原本不兼容的接口融合在一起工作

模式中的角色

  1. 目标接口(Target):客户所期待的接口,,也目标可以是具体的或抽象的类可以是接口。(5V的电源 )
  2. 需要适配的类(Adaptee):需要适配的类或适配者类(220V的电源 )
  3. 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。(电源适配器 )

分类

  • 类适配器模式 Adapter 类,通过继承 source 类,实现 Destination 类接口,完成 source->Destination 的适配。

  • 对象适配器模式 将 Adapter 类作修改,不是继承 source 类,而是持有 source 类的实例,以解决兼容性的问题。 即:持有 source 类,实现 Destination 类接口,完成source->Destination 的适配

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

 适配器模式分类三种体现形式思想都大同小异,这里就只介绍最常用的 对象适配器模式;

案例:

在使用适配器前:(案例中还是220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电)

package com.javaxl.design.adapter.before;

/**
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 15:51
 *
 * 220V的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}


public class Phone {
//    充电
    public void charge(Voltage220V voltage220V){
        double voltage = voltage220V.getVoltage() / 40;
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}


public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        还一种方案:新增5V的一个Voltage5V,Voltage的电压可以被手机使用
//        但是这违背现实生活现象,现实生活中只有220V的电源,其他的电源都是通过适配得来的
        phone.charge(new Voltage220V());
    }
}

使用适配器模式后:

package com.javaxl.design.adapter.after;

/**
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 15:51
 *
 * 220V的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}

/**
 * 目标接口
 */
interface Voltage5V{
    double getVoltage();
}

/**
 * 适配器:里面封装了source源到destination目标的过程
 */
class VoltageAdapter implements  Voltage5V{
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public double getVoltage() {
        return voltage220V.getVoltage() / 40;
    }
}


package com.javaxl.design.adapter.after;


public class Phone {
//    充电
    public void charge(Voltage5V voltage5V){
        double voltage = voltage5V.getVoltage();
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}


public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        VoltageAdapter适配器对Voltage220V这个不能直接使用的电源适配后就可以使用了
        phone.charge(new VoltageAdapter(new Voltage220V()));
    }
}

桥接模式(Bridge)

概念:桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化

案例

需求:手机(型号 + 品牌)操作问题;

完成手机各品牌各型号的项目设计;

列如:折叠式的华为、小米、Vivo,直立式的华为、小米、Vivo,旋转式的、滑盖的...

要求该项目设计易于扩展

首先我们们最容易想到的方案应该如下:

上图是常见的需求设计方案,非常不便于维护,手机的型号与品牌耦合度太高,当要新增滑盖(Slide)式的手机时,对应的品牌手机也要新增;  

接下来我们来看看桥接模式是如何设计的?

 桥接模式将分为两个模块,抽象部分(款式)和实现部分(品牌),抽象类(款式)中定义啦一个实现类(品牌),如Folded继承拉这个抽象类,并添加有参(Implement 品牌)的构造方法,这样品牌就不会受限制

package com.javaxl.design.bridge;

/**
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 17:29
 * <p>
 * 手机型号
 */
public abstract class Abstraction {
    protected Implementor implementor;

    public abstract void call();
}

class Folded extends Abstraction {
    private String name = "折叠式";

    Folded(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Upright extends Abstraction {
    private String name = "直立式";

    Upright(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Slide extends Abstraction {
    private String name = "滑盖式";

    Slide(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}


package com.javaxl.design.bridge;

/**
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 17:29
 * 手机品牌
 */
public interface Implementor {
    String getName();
}

class HW implements Implementor{
    private String name = "华为";

    @Override
    public String getName() {
        return name;
    }
}

class Mi implements Implementor{
    private String name = "小米";

    @Override
    public String getName() {
        return name;
    }
}

class Vivo implements Implementor{
    private String name = "Vivo";

    @Override
    public String getName() {
        return name;
    }
}


public class Client {
    public static void main(String[] args) {
        Folded folded = new Folded(new HW());
        folded.call();

        Upright upright = new Upright(new Mi());
        upright.call();

        Slide slide = new Slide(new Vivo());
        slide.call();
    }
}

从结果可以看出来:

使用桥接模式,对该项目进行设计,型号或品牌的扩展,都不会影响另一方;

即手机型号扩展,手机品牌不受影响;手机品牌的上市退市,不会影响到手机型号;

实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本

  • 注意: 1、桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程 2、桥接模式要求正确识别出系统中两个独立变化的维度(抽象、和实现),因此其使用范围有一定的局限性,即需要有这样的应用场景。

  • 应用

    JDBC的Driver驱动类

 装饰者模式(Decorator)

简介:装饰者模式一般用于对原有功能进行增强/装饰 ,动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,降低啦耦合性

案例:单体咖啡与调味组合的饮料计价项目

常规的设计:

Drink===》饮品

        Juice===》果汁

        .......

        Coffee===》咖啡

                ChinaCoffee===》中式咖啡

                        ASeasoningChinaCoffee===》被A调料修饰的中式咖啡

                        BSeasoningChinaCoffee===》被B调料修饰的中式咖啡

                         ......

               XxxCoffee===》其它咖啡

                        ASeasoningXxxCoffee===》被A调料修饰的其它咖啡

                        BSeasoningXxxCoffee===》被B调料修饰的其它咖啡

                        ......

代码如下:

package com.javaxl.design.decorator.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:27
 *
 * 饮料包括单体咖啡+调料
 */
public abstract class Drink {
    protected double price;
    protected int n;
    protected DrinkSeasoning seasoning;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee{
    ChinaCoffee(double price,int n){
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price*this.n+this.seasoning.getPrice();
    }
}


package com.javaxl.design.decorator.before;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:32
 *
 * 调料的抽象接口
 */
public interface DrinkSeasoning {
    public abstract double getPrice();
}

/**
 * A类调料
 */
class ADrinkSeasoning implements  DrinkSeasoning{
    protected double price;
    protected int n;
    ADrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}


/**
 * B类调料
 */
class BDrinkSeasoning implements  DrinkSeasoning{
    private double price;
    protected int n;
    BDrinkSeasoning(double price,int n){
        this.price = price;
        this.n = n;
    }
    @Override
    public double getPrice() {
        return this.price*this.n;
    }
}


public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
        ADrinkSeasoning aDrinkSeasoning = new ADrinkSeasoning(2,2);
        chinaCoffee.seasoning = aDrinkSeasoning;
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+chinaCoffee.getPrice());
    }
}

       思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
       思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
这时,我们要在原来的基础上,追加一个调料,在当前的设计模式下,我们是无法达到以上两个要求的,当我们采用啦装饰者模式,就能达到

package com.javaxl.design.decorator.after;

import com.javaxl.design.decorator.before.DrinkSeasoning;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:27
 * <p>
 * 饮料包括单体咖啡+调料
 */
public abstract class Drink {
    protected double price;
    protected int n;

    public abstract double getPrice();
}

/**
 * 单体咖啡
 */
abstract class Coffee extends Drink {
}

/**
 * 单体果汁
 */
abstract class Juice extends Drink {
}

class ChinaCoffee extends Coffee {
    ChinaCoffee(double price, int n) {
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n;
    }
}


package com.javaxl.design.decorator.after;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 22:26
 */
public class DecoratorDrink extends Drink {
    private Drink drink;

    public DecoratorDrink(Drink drink, double price, int n) {
        this.drink = drink;
        this.price = price;
        this.n = n;
    }

    @Override
    public double getPrice() {
        return this.price * this.n + drink.getPrice();
    }
}

class ADecoratorDrink extends DecoratorDrink {
    public ADecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}

class BDecoratorDrink extends DecoratorDrink {
    public BDecoratorDrink(Drink drink, double price, int n) {
        super(drink, price, n);
    }
}


package com.javaxl.design.decorator.after;


/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-22 18:50
 */
public class Client {
    public static void main(String[] args) {
        ChinaCoffee chinaCoffee = new ChinaCoffee(6,1);
//        假定A类调料2元一份,B类调料3元一份
        Drink order = new ADecoratorDrink(chinaCoffee, 2, 2);
        System.out.println("中式咖啡1份+A调料2份,最终价格为:"+order.getPrice());

//        思考1:如果我要下单中式咖啡1份+A调料3份+B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new ADecoratorDrink(order,2,1);
        System.out.println("式咖啡1份+A调料3份,最终价格为:"+order.getPrice());
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料2份,最终价格为:"+order.getPrice());

//        思考2:在原有的咖啡订单下,追加B调料2份,计算出最终的价格,那代码该怎么改动呢?
        order = new BDecoratorDrink(order,3,2);
        System.out.println("式咖啡1份+A调料3份+B调料4份,最终价格为:"+order.getPrice());
    }
}

 

外观模式(Facade)

简介: 为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

  Facade(外观角色):在客户端可以调用它的方法,在外观角色中可以知道相关的(一个或者多个)子系统的功能和责任;在正常情况下,它将所有从客户端发来的请求委派到相应的子系统去,传递给相应的子系统对象处理。
SubSystem(子系统角色):在软件系统中可以有一个或者多个子系统角色,每一个子系统可以不是一个单独的类,而是一个类的集合,它实现子系统的功能;每一个子系统都可以被客户端直接调用,或者被外观角色调用,它处理由外观类传过来的请求;子系统并不知道外观的存在,对于子系统而言,外观角色仅仅是另外一个客户端而已。


特点:

  • 屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性

  • 对客户端与子系统的耦合关系 - 解耦,让子系统内部的模块更易维护和扩展

  • 当系统需要进行分层设计时,可以考虑使用 Facade 模式

案例:

需求:组装一个家庭影院;

电脑打开、投影仪打开、音箱打开、灯光调暗、零食拿出来,电影开始;

零食收起来、灯光调亮、音箱关闭、投影仪关闭、电脑关闭,电影结束;

package com.javaxl.design.facade.after;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-23 16:29
 *
 * 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
 */
public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponentE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}


public class ComponentFacade {
    ComponentA componentA =new ComponentA();
    ComponentB componentB = new ComponentB();
    ComponentC componentC = new ComponentC();
    ComponentD componentD = new ComponentD();
    ComponentE componenE = new ComponentE();
    public void on(){
        componentA.on();
        componentB.on();
        componentC.on();
        componentD.off();
        componenE.on();
        System.out.println("电影开始了...");
    }

    public void off(){
        componenE.off();
        componentD.on();
        componentC.off();
        componentB.off();
        componentA.off();
        System.out.println("电影结束了...");
    }
}


public class Client {
    public static void main(String[] args) {
        ComponentFacade componentFacade = new ComponentFacade();
        componentFacade.on();
        System.out.println();
        componentFacade.off();
    }
}

从上面代码可以看出:

客户端调用依赖了所有的子系统(ABCDE),如果该需求反复出现,对于客户端调用而言,就不是很方便了;

另一方面,此需求完成只需要依赖各个子系统的其中一部分功能,其它功能客户端用不上,依照迪米特法则我们也应该解耦客户端与各个子系统的关系;

package com.javaxl.design.facade.after;

/**
 * @author 小李飞刀
 * @site www.javaxl.com
 * @company
 * @create  2020-02-23 16:29
 *
 * 电脑(故意写两个用不上的功能,依次体现外观模式的优点)
 */
public class ComponentA {
    public void m1(){
        System.out.println("电脑功能一...");
    }
    public void m2(){
        System.out.println("电脑功能二...");
    }

    public void on(){
        System.out.println("电脑打开...");
    }

    public void off(){
        System.out.println("电脑关闭...");
    }
}

//投影仪
class ComponentB {
    public void on(){
        System.out.println("投影仪打开...");
    }

    public void off(){
        System.out.println("投影仪关闭...");
    }
}

//音箱
class ComponentC {
    public void on(){
        System.out.println("音箱打开...");
    }

    public void off(){
        System.out.println("音箱关闭...");
    }
}

//、灯光
class ComponentD {
    public void on(){
        System.out.println("灯光调亮...");
    }

    public void off(){
        System.out.println("灯光调暗...");
    }
}

//零食
class ComponentE {
    public void on(){
        System.out.println("零食拿出来...");
    }

    public void off(){
        System.out.println("零食收起来...");
    }
}


public class ComponentFacade {
    ComponentA componentA =new ComponentA();
    ComponentB componentB = new ComponentB();
    ComponentC componentC = new ComponentC();
    ComponentD componentD = new ComponentD();
    ComponentE componenE = new ComponentE();
    public void on(){
        componentA.on();
        componentB.on();
        componentC.on();
        componentD.off();
        componenE.on();
        System.out.println("电影开始了...");
    }

    public void off(){
        componenE.off();
        componentD.on();
        componentC.off();
        componentB.off();
        componentA.off();
        System.out.println("电影结束了...");
    }
}


public class Client {
    public static void main(String[] args) {
        ComponentFacade componentFacade = new ComponentFacade();
        componentFacade.on();
        System.out.println();
        componentFacade.off();
    }
}

代理模式(Proxy)

简介:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问

 代理模式包含如下三个角色:

     Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。

    Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中的操作。

    RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

结构:

分类:

        静态代理
        动态代理

                 jdk代理
                Cglib代理

    静态代理

    角色

        接口 ITeacherDao
        目标对象 TeacherDAO
        代理类 TeacherDAOProxy

    细节

    代理对象与目标对象要实现相同的接口 调用的时候通过调用代理对象的方法来调用目标对象

    /** 目标类
     */
    public class TeacherDAO implements ITeacherDao {
        public void teach() {
            System.out.println("老师传授知识");
        }
    }
     
    //目标接口
    interface ITeacherDao {
        void teach();
    }
     
    //代理类
    class TeacherDAOProxy implements ITeacherDao {
        private ITeacherDao teacherDAO;
     
        public TeacherDAOProxy(ITeacherDao teacherDAO) {
            this.teacherDAO = teacherDAO;
        }
     
        @Override
        public void teach() {
            System.out.println("老师正式授课前的准备工作,如学生全部签到...");
            teacherDAO.teach();
            System.out.println("老师结束授课,如下课铃声响起...");
        }
    }
     
     
    public class Client {
        public static void main(String[] args) {
            TeacherDAOProxy proxy = new TeacherDAOProxy(new TeacherDAO());
            proxy.teach();
        }
    }

    动态代理jdk代理 ​

    角色 ​

        接口 ​ ITeacherDao ​
        目标对象 ​ TeacherDAO ​
        代理类 ​ TeacherDAOProxy ​

    细节 ​

    不需要实现接口,但是目标对象要实现接 ​ 代理类口,否则不能用动态代理 ​ 代理对象的生成,是利用 JDK 的 API,动态的在内存中构建代理对象所在包:java.lang.reflect.Proxy

  

  /**
     * 目标接口
     */
    interface ITeacherDao {
        String teach();
     
        ITeacherDao sleep(int minutes);
    }
     
    class TeacherDao implements ITeacherDao{
        @Override
        public String teach() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            return sdf.format(new Date())+":老师传授知识";
        }
     
        @Override
        public ITeacherDao sleep(int minutes) {
            System.out.println("老师睡了" + minutes + "分钟");
            return this;
        }
     
    }
     
    //真实代理类的外衣
    class TeacherDaoProxy{
        private ITeacherDao target;
     
        public TeacherDaoProxy(ITeacherDao target) {
            this.target = target;
        }
     
        public Object xxx(){
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            Object obj = null;
                            String methodName = method.getName();
                            System.out.println("目标方法" + methodName + ":jdk代理开始...");
                            System.out.println("真实代理对象:"+proxy.getClass());
                            System.out.println("目标对象:"+target.getClass());
                            if("sleep".equals(methodName)){
    //                            method.invoke(target, args);
    //                            obj = proxy;
                                obj = method.invoke(target, args);
                            }else {
    //                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                                obj = method.invoke(target, args);
                            }
                            System.out.println("目标方法" + methodName + ":jdk代理结束...");
                            return obj;
                        }
                    });
        }
    }
     
     
    public class Client {
        public static void main(String[] args) {
            TeacherDaoProxy proxy = new TeacherDaoProxy(new TeacherDao());
            ITeacherDao ins = (ITeacherDao) proxy.xxx();
            System.out.println("===========代理类实例被使用   begin=============");
            System.out.println(ins);
            System.out.println("===========代理类实例被使用   end=============");
            System.out.println(ins.teach());
    //        System.out.println(proxy.execute());
            System.out.println("===========代理类实例被使用   begin=============");
            ins.sleep(10);
            System.out.println("===========代理类实例被使用   end=============");
            ins.sleep(20).sleep(60);
        }
    }

注意:java.lang.reflect.InvocationHandler.invoke第一个参数proxy的使用场景,链式编程中会用到;

    Cglib代理 ​

    角色 ​

        接口 ​ ITeacherDao ​
        目标对象 ​ TeacherDAO ​
        代理类 ​ TeacherDAOProxy

    细节 ​

        目标对象与代理对象都不需要实现接口 ​
        Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展 ​ Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口.它广泛的被许多 AOP 的框架使用 ​
        Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类 ​

    注意:

        需要引入 cglib 的 jar 文件
        在内存中动态构建子类,注意代理的类不能为 final,否则报错java.lang.IllegalArgumentException:
        目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.

  

  import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
     
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.text.SimpleDateFormat;
    import java.util.Date;
     
    class TeacherDao {
        public String teach() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            return sdf.format(new Date()) + ":老师传授知识";
        }
     
        public TeacherDao sleep(int minutes) {
            System.out.println("老师睡了" + minutes + "分钟");
            return this;
        }
     
    }
     
    //真实代理类的外衣
    class TeacherDaoProxy implements MethodInterceptor {
        private Object target;
     
        public TeacherDaoProxy(Object target) {
            this.target = target;
        }
     
        //返回一个代理对象:    是 target  对象的代理对象
        public Object getProxyInstance() {
            //1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2. 设置父类
            enhancer.setSuperclass(target.getClass());
            //3. 设置回调函数
            enhancer.setCallback(this);
            //4. 创建子类对象,即代理对象
            return enhancer.create();
        }
     
        /**
         * @param proxyIns  由CGLib动态生成的代理类实例
         * @param method    上文中实体类所调用的被代理的方法引用
         * @param args      参数值列表
         * @param methodProxy   生成的代理类对方法的代理引用
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object proxyIns, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            String methodName = method.getName();
            Object res;
            System.out.println("目标方法" + methodName + ":cglib代理开始...");
            System.out.println("真实代理对象:" + proxyIns.getClass());
            System.out.println("目标对象:" + target.getClass());
            if ("sleep".equals(methodName)) {
    //                            method.invoke(target, args);
    //                            obj = proxy;
                res = method.invoke(target, args);
    //            res = methodProxy.invokeSuper(proxyIns,args);
                res = proxyIns;
            } else {
    //                        proxy是真实代理类,method是目标方法,args是目标方法携带的参数
                res = method.invoke(target, args);
            }
            System.out.println("目标方法" + methodName + ":cglib代理结束...");
            return res;
        }
    }
     
    public class Client {
        public static void main(String[] args) {
            TeacherDao proxy = (TeacherDao) new TeacherDaoProxy(new TeacherDao()).getProxyInstance();
            proxy.sleep(111).sleep(222);
        }
    }

总结

            代理模式是常用的结构型设计模式之一,它为对象的间接访问提供了一个解决方案,可以对对象的访问进行控制。代理模式类型较多,其中远程代理、虚拟代理、保护代理等在软件开发中应用非常广泛。

     

    模式优点

           代理模式的共同优点如下:

        能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
        客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

    模式缺点
           代理模式的主要缺点如下:

        由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢,例如保护代理。
        实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂。
 

weixin028基于微信小程序小说阅读器设计+ssm后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值