设计模式——结构型

一、适配器模式

        适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,目的是兼容性,让原本接口不匹配的两个类可以协同工作。适配器模式分为三类:类适配器模式、对象适配器模式、接口适配器模式。

类适配器模式

应用实例

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

思路分析(类图)

被适配的类

package com.taoke.designPattern.adapter.classAdapter;

/**
 * 被适配的类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class Voltage220V {

    /**
     * 输出220V的电压
     *
     * @return 电压
     */
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

 目标类

package com.taoke.designPattern.adapter.classAdapter;

/**
 * 目标类
 *
 * @author taoke
 * @date 2022/7/19
 */
public interface IVoltage5V {

    /**
     * 输出5V的电压
     *
     * @return 电压
     */
    int output5V();

}

适配器类

package com.taoke.designPattern.adapter.classAdapter;

/**
 * 适配器类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

    @Override
    public int output5V() {
        //获取220V电压
        int v = output220V();
        System.out.println("使用适配器进行适配~~");
        v = v / 44;
        System.out.println("适配器适配完成");
        return v;
    }
}

手机使用适配器类

package com.taoke.designPattern.adapter.classAdapter;


/**
 * 手机使用适配器
 *
 * @author taoke
 * @date 2022/7/19
 */
public class Phone {
    /**
     * 充电
     */
    public void charging(IVoltage5V voltage5v) {
        if (voltage5v.output5V() == 5) {
            System.out.println("电压5V,可以充电");
        } else if (voltage5v.output5V() > 5) {
            System.out.println("电压大于5V,不能充电");
        }
    }

}

类适配器模式测试类

package com.taoke.designPattern.adapter.classAdapter;

/**
 * 类适配器模式测试类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class ClassAdapterTest {
    public static void main(String[] args) {
        System.out.println(" === 类适配器模式 ====");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

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

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

对象适配器模式

 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,已解决兼容性的问题。根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系。

应用实例

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

思路分析(类图)

 被适配的类

package com.taoke.designPattern.adapter.objectAdapter;

/**
 * 被适配的类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class Voltage220V {

    /**
     * 输出220V的电压
     *
     * @return 电压
     */
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

 目标类

package com.taoke.designPattern.adapter.objectAdapter;

/**
 * 目标类
 *
 * @author taoke
 * @date 2022/7/19
 */
public interface IVoltage5V {

    /**
     * 输出5V的电压
     *
     * @return 电压
     */
    int output5V();

}

适配器类

package com.taoke.designPattern.adapter.objectAdapter;

/**
 * 电源适配器
 *
 * @author taoke
 * @date 2022/7/19
 */
public class VoltageAdapter implements IVoltage5V {

    /**
     * voltage220V对象
     */
    private final Voltage220V voltage220V;

    /**
     * 通过聚合关系,传入一个voltage220V对象
     *
     * @param voltage220V 对象
     */
    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    @Override
    public int output5V() {
        //获取220V电压
        int v = voltage220V.output220V();
        System.out.println("使用适配器进行适配~~");
        v = v / 44;
        System.out.println("适配器适配完成");
        return v;
    }
}

手机使用适配器类

package com.taoke.designPattern.adapter.objectAdapter;


/**
 * 手机使用适配器
 *
 * @author taoke
 * @date 2022/7/19
 */
public class Phone {
    /**
     * 充电
     */
    public void charging(IVoltage5V voltage5v) {
        if (voltage5v.output5V() == 5) {
            System.out.println("电压5V,可以充电");
        } else if (voltage5v.output5V() > 5) {
            System.out.println("电压大于5V,不能充电");
        }
    }

}

对象适配器测试类

package com.taoke.designPattern.adapter.objectAdapter;

/**
 * 对象适配器测试类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class ObjectAdapterTest {

    public static void main(String[] args) {
        System.out.println("==对象适配器模式==");
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}

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

  • 对象适配器和类适配器算是同一种思想,只不过实现方式不同,根据合成复用原则,使用组合代替继承,所以它解决了类适配器必须继承被适配类的局限问题,也不再要求目标类必须是接口
  • 使用成本更低,更灵活

接口适配器模式

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

接口

package com.taoke.designPattern.adapter.interfaceAdapter;

/**
 * 接口
 *
 * @author taoke
 * @date 2022/7/19
 */
public interface Interface {

    void m1();

    void m2();

    void m3();

    void m4();
}

抽象类,对接口默认实现

package com.taoke.designPattern.adapter.interfaceAdapter;

/**
 * 抽象类 接口适配器
 *
 * @author taoke
 * @date 2022/7/19
 */
public abstract class AbstractAdapter implements Interface {

    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}

客户端根据需要实现某些方法

package com.taoke.designPattern.adapter.interfaceAdapter;

/**
 * 接口适配器测试类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class InterfaceAdapterTest {

    public static void main(String[] args) {
        AbstractAdapter abstractAdapter = new AbstractAdapter() {
            /**
             * 重写需要覆盖的方法
             */
            @Override
            public void m1() {
                super.m1();
            }
        };
        //调用m1方法
        abstractAdapter.m1();
    }
}

二、桥接模式

        桥接模式(Bridge)是将实现与抽象放在两个不同的类层次中,使两个层次可以独立的改变,Bridge莫斯基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责,它的特点是把抽象(Abstraction)与行为实现(Implamentation)分离开来,从而可以保持各部分的独立性以及他们的功能扩展

桥接模式的角色

Client类:桥接模式的调用者

Abstraction:维护了Implementor/ConcreteImplementor,二者是聚合关系,Abstraction充当桥接类

RefinedAbstraction: 是Abstraction抽象类的子类

Implementor:行为实现类的接口

ConcreteImplementor:行为的具体实现

桥接模式UML图

应用实例

现在对不同手机类型的不同品牌实现操作编程(比如:开机、关机、上网,打电话等),如图

 

行为接口

package com.taoke.designPattern.bridge;

/**
 * 行为接口 手机
 *
 * @author taoke
 * @date 2022/7/19
 */
public interface Phone {
    /**
     * 开机
     */
    void open();

    /**
     * 关机
     */
    void close();

    /**
     * 打电话
     */
    void call();

}

行为接口实现类

package com.taoke.designPattern.bridge;

/**
 * 直屏手机
 *
 * @author taoke
 * @date 2022/7/19
 */
public class UpRightPhone implements Phone {

    @Override
    public void open() {
        System.out.println("直立手机开机");
    }

    @Override
    public void close() {
        System.out.println("直立手机关机");
    }

    @Override
    public void call() {
        System.out.println("直立手机打电话");
    }
}

package com.taoke.designPattern.bridge;

/**
 * 折叠手机
 *
 * @author taoke
 * @date 2022/7/19
 */
public class FolderPhone implements Phone {

    @Override
    public void open() {
        System.out.println("折叠手机开机");
    }

    @Override
    public void close() {
        System.out.println("折叠手机关机");
    }

    @Override
    public void call() {
        System.out.println("折叠手机打电话");
    }
}

桥接类

package com.taoke.designPattern.bridge;

/**
 * 桥接类 手机品牌
 *
 * @author taoke
 * @date 2022/7/19
 */
public abstract class Brand {
    
    /**
     * 组合手机
     */
    private final Phone phone;

    public Brand(Phone phone) {
        this.phone = phone;
    }

    public void open() {
        phone.open();
    }

    public void close() {
        phone.close();
    }

    public void call() {
        phone.call();
    }

}

桥接子类

package com.taoke.designPattern.bridge;

/**
 * Oppo手机
 *
 * @author taoke
 * @date 2022/7/19
 */
public class Oppo extends Brand {

    public Oppo(Phone phone) {
        super(phone);
    }

    @Override
    public void open() {
        super.open();
        System.out.println("Oppo手机开机");
    }

    @Override
    public void close() {
        super.open();
        System.out.println("Oppo手机关");
    }

    @Override
    public void call() {
        super.open();
        System.out.println("Oppo手机打电话");
    }
}

package com.taoke.designPattern.bridge;

/**
 * ViVo手机
 *
 * @author taoke
 * @date 2022/7/19
 */
public class ViVo extends Brand {

    public ViVo(Phone phone) {
        super(phone);
    }

    @Override
    public void open() {
        super.open();
        System.out.println("ViVo手机开机");
    }

    @Override
    public void close() {
        super.open();
        System.out.println("ViVo手机关机");
    }

    @Override
    public void call() {
        super.open();
        System.out.println("ViVo手机打电话");
    }
}

测试类

package com.taoke.designPattern.bridge;

/**
 * 桥接模式测试类
 *
 * @author taoke
 * @date 2022/7/19
 */
public class BridgeModeTest {

    public static void main(String[] args) {
        ViVo phone1 = new ViVo(new UpRightPhone());
        phone1.open();
        phone1.close();
        phone1.call();

        System.out.println("==============");
        Oppo phone2 = new Oppo(new UpRightPhone());
        phone2.open();
        phone2.close();
        phone2.call();

        System.out.println("==============");
        ViVo phone3 = new ViVo(new FolderPhone());
        phone3.open();
        phone3.close();
        phone3.call();

        System.out.println("==============");
        Oppo phone4 = new Oppo(new FolderPhone());
        phone4.open();
        phone4.close();
        phone4.call();

    }
}

三、装饰者模式

        动态地将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式体现了开闭原则(OCP)

装饰者模式的角色

Component:主体,比如Drink

ConcreteComponent:具体的主体,比如Coffee

Decorator:装饰者,比如各种调料

装饰者模式UML图

 

应用实例

        咖啡馆订咖啡,咖啡种类有:Espresso(意大利浓咖)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)。调料有:Mik、Soy(豆浆)、Chocolate。要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便,客户可以点单品咖啡,也可以单品咖啡+调料组合。

  

 

抽象主体

package com.taoke.designPattern.decorator;

/**
 * 抽象类 饮料
 *
 * @author taoke
 * @date 2022/7/20
 */
public abstract class Drink {

    /**
     * 饮料描述
     */
    private String description;

    /**
     * 饮料价格
     */
    private float price;

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    /**
     * 饮料花费金额
     *
     * @return 花费金额
     */
    public abstract float cost();
}

具体主体(大类)

package com.taoke.designPattern.decorator;

/**
 * 饮料 咖啡
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Coffee extends Drink {

    @Override
    public float cost() {
        return super.getPrice();
    }

}

具体主体(小类)

package com.taoke.designPattern.decorator;

/**
 * 无因咖啡
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Decaf extends Coffee {

    public Decaf() {
        super.setDescription(" 无因咖啡 ");
        super.setPrice(10.0f);
    }

}

package com.taoke.designPattern.decorator;

/**
 * 意大利咖啡
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Espresso extends Coffee {

    public Espresso() {
        super.setPrice(6.0f);
        super.setDescription(" 意大利咖啡 ");
    }
}

package com.taoke.designPattern.decorator;

/**
 * 美式咖啡
 *
 * @author taoke
 * @date 2022/7/20
 */
public class LongBlack extends Coffee {

    public LongBlack() {
        setPrice(5.0f);
        setDescription(" 美式咖啡 ");
    }
}

装饰者

package com.taoke.designPattern.decorator;

/**
 * 装饰者
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Decorator extends Drink {

    private final Drink drink;

    public Decorator(Drink drink) {
        this.drink = drink;
    }

    @Override
    public float cost() {
        return super.getPrice() + drink.cost();
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " " + super.getPrice() + " && " + drink.getDescription();
    }

}

具体装饰者

package com.taoke.designPattern.decorator;

/**
 * 调料 巧克力
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Chocolate extends Decorator {

    public Chocolate(Drink drink) {
        super(drink);
        super.setPrice(3.0f);
        super.setDescription(" 巧克力 ");
    }

}

package com.taoke.designPattern.decorator;

/**
 * 调料 牛奶
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Milk extends Decorator {

    public Milk(Drink drink) {
        super(drink);
        super.setPrice(2.0f);
        super.setDescription(" 牛奶 ");
    }

}

package com.taoke.designPattern.decorator;

/**
 * 调料 豆浆
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Soy extends Decorator {

    public Soy(Drink drink) {
        super(drink);
        super.setPrice(1.5f);
        super.setDescription(" 豆浆 ");
    }

}

测试类

package com.taoke.designPattern.decorator;

/**
 * 装饰者模式测试类
 *
 * @author taoke
 * @date 2022/7/20
 */
public class DecoratorTest {

    public static void main(String[] args) {
        //装饰者模式下订单:美式咖啡
        Drink longBlack = new LongBlack();
        System.out.println("描述 :" + longBlack.getDescription() + ", 费用 : " + longBlack.cost());
        System.out.println("==========");
        //装饰者模式下订单:2份牛奶+1份巧克力的意大利咖啡
        Drink coffee = new Espresso();
        System.out.println("描述 :" + coffee.getDescription() + ", 费用 : " + coffee.cost());
        //咖啡加入1份巧克力
        coffee = new Chocolate(coffee);
        System.out.println("描述 : 加入1份巧克力," + coffee.getDescription() + ", 费用 : " + coffee.cost());
        //咖啡加入1份牛奶
        coffee = new Milk(coffee);
        System.out.println("描述 : 加入1份巧克力,1份牛奶," + coffee.getDescription() + ", 费用 : " + coffee.cost());
        //咖啡加入2份牛奶
        coffee = new Milk(coffee);
        System.out.println("描述 : 加入1份巧克力,2份牛奶," + coffee.getDescription() + ", 费用 : " + coffee.cost());
        System.out.println("==========");
        //装饰者模式下订单:1份豆浆的无因咖啡
        Drink decaf = new Decaf();
        System.out.println("描述 : " + decaf.getDescription() + ", 费用 : " + decaf.cost());
        //无因咖啡加入1份豆浆
        decaf = new Soy(decaf);
        System.out.println("描述 : 加入1份豆浆," + decaf.getDescription() + ", 费用 : " + decaf.cost());
    }
}

四、组合模式

        组合模式(Composite Pattern),又叫部分整体模式。它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象

组合模式的角色

Component:组合模式中对象声明接口,实现所有类中的接口默认行为,用于访问和管理Componet子部件,Componet可以是抽象类或者接口。

Leaf:表示叶子节点,叶子节点没有子节点

Composite:非叶子节点,用于存储子部件,在Componet接口中实现子部件的相关操作,如新增,删除

组合模式UML图

 

应用实例

展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。

抽象组织

package com.taoke.designPattern.composite;

/**
 * 抽象组织
 *
 * @author taoke
 * @date 2022/7/20
 */
public abstract class OrganizationComponent {

    /**
     * 名字
     */
    private String name;

    /**
     * 描述
     */
    private String description;

    public OrganizationComponent(String name, String description) {
        this.name = name;
        this.description = description;
    }

    protected void add(OrganizationComponent component) {
        //默认实现
        throw new UnsupportedOperationException();
    }

    protected void remove(OrganizationComponent component) {
        //默认实现
        throw new UnsupportedOperationException();
    }

    /**
     * 子类需要实现
     */
    protected abstract void print();


    public String getName() {
        return name;
    }

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

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

大学

package com.taoke.designPattern.composite;

import java.util.ArrayList;
import java.util.List;

/**
 * 大学
 *
 * @author taoke
 * @date 2022/7/20
 */
public class University extends OrganizationComponent {

    private final List<OrganizationComponent> components = new ArrayList<>();

    public University(String name, String description) {
        super(name, description);
    }

    @Override
    protected void add(OrganizationComponent component) {
        components.add(component);
    }

    @Override
    protected void remove(OrganizationComponent component) {
        components.remove(component);
    }

    @Override
    protected void print() {
        System.out.println("--------" + super.getName() + "---------");
        components.forEach(OrganizationComponent::print);
    }
}

学院

package com.taoke.designPattern.composite;

import java.util.ArrayList;
import java.util.List;

/**
 * 学院
 *
 * @author taoke
 * @date 2022/7/20
 */
public class College extends OrganizationComponent {

    private final List<OrganizationComponent> components = new ArrayList<>();

    public College(String name, String description) {
        super(name, description);
    }

    @Override
    protected void add(OrganizationComponent component) {
        components.add(component);
    }

    @Override
    protected void remove(OrganizationComponent component) {
        components.remove(component);
    }

    @Override
    protected void print() {
        System.out.println("--------" + super.getName() + "---------");
        components.forEach(OrganizationComponent::print);
    }
}

package com.taoke.designPattern.composite;

/**
 * 系
 *
 * @author taoke
 * @date 2022/7/20
 */
public class Department extends OrganizationComponent {

    public Department(String name, String description) {
        super(name, description);
    }

    @Override
    protected void print() {
        System.out.println(super.getName());
    }
}

测试类

package com.taoke.designPattern.composite;

/**
 * 组合模式测试类
 *
 * @author taoke
 * @date 2022/7/20
 */
public class CompositeTest {

    public static void main(String[] args) {
        //从大到小创建对象
        OrganizationComponent university = new University("清华大学", "顶尖大学");

        //创建学院
        OrganizationComponent computerCollege = new College("计算机学院", "顶尖学院");
        OrganizationComponent infoEngineerCollege = new College("信息工程学院", "顶尖学院");

        //创建各学院下的专业
        computerCollege.add(new Department("软件工程", "软件工程很好"));
        computerCollege.add(new Department("网络工程", "网络工程很好"));
        computerCollege.add(new Department("计算机科学与技术", "计算机科学与技术很好"));

        infoEngineerCollege.add(new Department("通信工程", "通信工程很好"));
        infoEngineerCollege.add(new Department("信息工程", "信息工程很好"));

        //将学院加到学校中
        university.add(computerCollege);
        university.add(infoEngineerCollege);

        university.print();
    }
}

五、外观模式

        外观模式(Facade),也叫过程模式。外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节,这个接口使得这一子系统更加容易使用。

外观模式的角色

  • 外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象
  • 调用者(Client):外观接口的调用者
  • 子系统的集合:指模块或者子系统,处理 Facade 对象指派的任务,是功能的实际提供者

外观模式UML图

 

六、享元模式

        享元模式(Flyweight Pattern)也叫蝇量模式运用共享技术有效地支持大量细粒度的对象。常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。享元模式能够解决重复对象的内存浪费的问题。当系统中有大量相似对象,需要缓冲池时,不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式。

享元模式角色

 Flyweight:抽象的享元角色,是抽象的产品类,同时定义出对象的外部状态和内部状态的接口和实现

ConcreteFlyweight:具体的享元角色,是具体的产品类,实现抽象角色定义的相关业务

UnsharedConcreteFlyweight:不可共享的角色,一般不会出现在享元工厂中

FlyweightFactory:享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象的方法

享元模式UML图

 

应用实例

        客户 A 做一个产品展示网站,客户 A 的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求都有些不同,有客户要求以新闻的形式发布,有客户人要求以博客的形式发布,有客户希望以微信公众号的形式发布。

抽象享元角色

package com.taoke.designPattern.flyweight;

/**
 * 抽象网站
 *
 * @author taoke
 * @date 2022/7/21
 */
public abstract class WebSite {

    /**
     * 使用网站
     *
     * @param user 用户
     */
    public abstract void use(User user);

}

具体享元角色

package com.taoke.designPattern.flyweight;

/**
 * 具体网站
 *
 * @author taoke
 * @date 2022/7/21
 */
public class ConcreteWebsite extends WebSite {

    /**
     * 共享的部分(内部状态),网站发布的类型
     */
    private final String type;

    public ConcreteWebsite(String type) {
        this.type = type;
    }

    @Override
    public void use(User user) {
        System.out.println("网站发布的形式是: " + type + ",发布者是:" + user.getName());
    }
}

享元工厂

package com.taoke.designPattern.flyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * 网站工厂类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class WebSiteFactory {

    /**
     * 集合,充当池
     */
    private final Map<String, ConcreteWebsite> pool = new HashMap<>();

    /**
     * 根据网站的类型,返回一个网站,没有就创建一个网站,并放入池中
     *
     * @param type 网站类型
     * @return 网站
     */
    public WebSite getWebsiteCategory(String type) {
        if (!pool.containsKey(type)) {
            //创建一个网站,并放入池中
            pool.put(type, new ConcreteWebsite(type));
        }
        return pool.get(type);
    }

    /**
     * 获取网站的数量
     *
     * @return 网站的数量
     */
    public int getWebSiteCount() {
        return pool.size();
    }
}

实体类

package com.taoke.designPattern.flyweight;

/**
 * 用户类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class User {

    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

测试类

package com.taoke.designPattern.flyweight;

/**
 * 享元模式测试类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class FlyweightTest {

    public static void main(String[] args) {
        //创建一个工厂类
        WebSiteFactory factory = new WebSiteFactory();
        //客户要一个新闻形式发布的网站
        WebSite webSite = factory.getWebsiteCategory("新闻");
        webSite.use(new User("Tom"));

        //客户要一个博客形式发布的网站
        WebSite webSite1 = factory.getWebsiteCategory("博客");
        webSite1.use(new User("Jack"));

        //客户要一个博客形式发布的网站
        WebSite webSite2 = factory.getWebsiteCategory("博客");
        webSite2.use(new User("King"));

        System.out.println("网站的分类一共:" + factory.getWebSiteCount() + "个");

    }

}

七、代理模式

        为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。代理模式有不同的形式,主要有两种:静态代理,动态代理,cglib代理。

应用实例

  • 定义一个接口:ITeacherDao
  • 目标对象TeacherDAO实现接口ITeacherDAO
  • 使用静态代理方式,就需要在代理对象TeacherDAOProxy中也实现ITeacherDAO
  • 调用的时候通过调用代理对象的方法来调用目标对象
  • 特别提醒:代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法

静态代理

        静态代理在使里时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承和同父类—应用实例

UML类图

 

ITeacherDao

package com.taoke.designPattern.proxy.staticProxy;

/**
 * 老师接口
 *
 * @author taoke
 * @date 2022/7/21
 */
public interface ITeacherDao {

    /**
     * 教书
     */
    void teach();
}

TeacherDao

package com.taoke.designPattern.proxy.staticProxy;

/**
 * 老师
 *
 * @author taoke
 * @date 2022/7/21
 */
public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println("老师授课中。。。");
    }
}

TeacherDaoProxy

package com.taoke.designPattern.proxy.staticProxy;

/**
 * 代理对象,静态代理
 *
 * @author taoke
 * @date 2022/7/21
 */
public class TeacherDaoProxy implements ITeacherDao {

    private final ITeacherDao target;

    public TeacherDaoProxy(ITeacherDao target) {
        this.target = target;
    }

    @Override
    public void teach() {
        System.out.println("静态代理开始。。。");
        target.teach();
        System.out.println("静态代理结束。。。");
    }
}

测试类

package com.taoke.designPattern.proxy.staticProxy;

/**
 * 静态代理测试类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class StaticProxyTest {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao target = new TeacherDao();
        //创建代理对象,并将被代理对象传入代理对象
        TeacherDaoProxy proxy = new TeacherDaoProxy(target);
        //通过代理对象,调用被代理的方法
        proxy.teach();
    }
}

动态代理

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

UML类图

ITeacherDao 

package com.taoke.designPattern.proxy.jdk;

/**
 * 老师接口
 *
 * @author taoke
 * @date 2022/7/21
 */
public interface ITeacherDao {

    void teach();

    void sayHello(String name);
}

TeacherDao

package com.taoke.designPattern.proxy.jdk;

/**
 * 实现老师接口
 *
 * @author taoke
 * @date 2022/7/21
 */
public class TeacherDao implements ITeacherDao {

    @Override
    public void teach() {
        System.out.println("老师授课中。。。");
    }

    @Override
    public void sayHello(String name) {
        System.out.println("hello " + name);
    }
}

ProxyFactory

package com.taoke.designPattern.proxy.jdk;

import java.lang.reflect.Proxy;

/**
 * 动态代理工厂
 *
 * @author taoke
 * @date 2022/7/21
 */
public class ProxyFactory {

    /**
     * 被代理对象
     */
    private final Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    /**
     * 给目标对象生成一个代理对象
     *
     * @return 代理对象
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), (proxy, method, args) -> {
                    System.out.println("jdk动态代理开始。。。");
                    //反射调用目标方法
                    Object invoke = method.invoke(target, args);
                    System.out.println("动态代理结束。。。");
                    return invoke;
                });
    }

}

测试类

package com.taoke.designPattern.proxy.jdk;

/**
 * jdk动态代理测试类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class JDKProxyTest {

    public static void main(String[] args) {
        //创建目标对象
        TeacherDao target = new TeacherDao();
        //给目标对象创建代理对象
        ITeacherDao teacherDao = (ITeacherDao) new ProxyFactory(target).getProxyInstance();
        //proxyInstance=class com.sun.proxy.$Proxy0 内存中生成了代理对象
        System.out.println("proxyInstance=" + teacherDao.getClass());
        //通过生成的代理对象调用被代理的方法
        teacherDao.teach();
        teacherDao.sayHello("Tom");
    }
}

Cglib代理

  • 静态代理和 JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理——这就是 Cglib 代理
  • Cglib 代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将 Cglib 代理归属到动态代理。
  • Cglib 是一个强大的高性能的代码生成包,它可以在运行期扩展 java 类与实现 java 接口。它广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截
  • 在 AOP 编程中如何选择代理模式:目标对象需要实现接口,用 JDK 代理,目标对象不需要实现接口,用 Cglib 代理
  • Cglib 包的底层是通过使用字节码处理框架 ASM 来转换字节码并生成新的类



UML类图

 

 

TeacherDao

package com.taoke.designPattern.proxy.cglib;

/**
 * 被代理的对象
 *
 * @author taoke
 * @date 2022/7/21
 */
public class TeacherDao {

    public String teach() {
        System.out.println("老师授课中。。。我是cglib代理,不需要实现接口");
        return "hello";
    }
}

ProxyFactory

package com.taoke.designPattern.proxy.cglib;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 动态代理工厂
 *
 * @author taoke
 * @date 2022/7/21
 */
public class ProxyFactory implements MethodInterceptor {

    /**
     * 目标对象
     */
    private final Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib代理开始。。。");
        Object invoke = method.invoke(target, args);
        System.out.println("动态代理结束。。。");
        return invoke;
    }

    /**
     * 创建target的代理对象
     *
     * @return 代理对象
     */
    public Object getProxyInstance() {
        //1、创建一个工具类
        Enhancer enhancer = new Enhancer();
        //2、设置父类
        enhancer.setSuperclass(target.getClass());
        //3、设置回调函数
        enhancer.setCallback(this);
        //4、创建代理对象
        return enhancer.create();
    }
}

测试类

package com.taoke.designPattern.proxy.cglib;

/**
 * cglib动态代理测试类
 *
 * @author taoke
 * @date 2022/7/21
 */
public class CglibProxyTest {
    public static void main(String[] args) {
        //创建目标对象
        TeacherDao target = new TeacherDao();
        //创建代理对象
        TeacherDao proxy = (TeacherDao) new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法,触发intercept方法,从而实现崔目标对象的调用
        String teach = proxy.teach();
        System.out.println("方法返回结果:" + teach);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值