一、适配器模式
适配器模式(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);
}
}