Java设计模式——代理模式

大家好,我是会冒泡的可乐。

最近会冒泡的可乐的女神要生日了,所以我决定通过代购买个拽鞋当生日礼物(这半个月又白忙活了😭),在下

单完的一瞬间,突然我发现,代购和我们Java中的静态代理模式很像啊,我顾不上买鞋的心痛,马上码了这篇文

章。

我先放一张静态代理的结构图:

image-20220413163633038

上图可以看到,一共有三个角色:

  1. 主题角色(Subject):可以是抽象类也可以是接口,是一个最普通的业务类型定义;
  2. 真实主题角色(RealSubject):被代理的角色,是业务逻辑的具体执行者;
  3. 代理角色(Proxy):它负责对真实主题角色的应用,并且在真实主题角色处理理完毕前后做预处理和善后处理工作;

现在不太理解不急,我们往下看。

一、静态代理

1. 代购

好啦,正式开始讲代购啦,咳咳。

我们都知道,一般女生都喜欢名牌包包,但名牌包包有个特点,国外的款式与价格都比国内有优势,所以以前很多多金的妹子都要亲自跑到国外的包包商店买。但是自己去买,不仅费飞机票,还费时间,因此聪明的中国人看到商机,衍生出了一个新的职业:代购。

有了代购,妹子不需要亲自去买了,只需要找代购买了(这里很关键,妹子是找的代购小哥买的,但实际上代购小哥还是需要去到包包商店买,毕竟代购小哥自己没货啊!因此最终提供买包服务的不是代购小哥,而是包包商店!)

我们使用代理模式将这个代购的场景用代码描述出来。

我们首先创建一个包包商店的接口(所有包包商店都需要符合这个包包商店接口的规范),接口里面定义了个buyBag的方法:

public interface IBagShop {
void buyBag(String num);
}

接着定义一个包包商店的实现类,这个包包商店类实现了包包商店接口,因此提供了真实的买包服务:

public class BagShop implements IBagShop {
@Override
public void buyBag(String money) {
System.out.println("买了一个价值"+money+"限量款包包!");
}
}

最后定义我们的代购小哥,也要实现包包商店接口(毕竟也是需要提供买包服务给妹子的),且需要引用包包商店类,因为他最终还是需要自己去到包包商店买包包:

public class PurchasingAgent implements IBagShop {
private IBagShop bagShop;
public PurchasingAgent(IBagShop bagShop) {
super();
this.bagShop = bagShop;
}
@Override
public void buyBag(String num) {
// 调用包包商店的买服务
bagShop.buyBag(num);
}
}

三个主要类就完成啦,是不是很简单?没有?那我再给你们看下面这张图,你们就一目了然了:

MESA Monitor

最后我们再写个“妹子”类测测吧:

public class Girl {
public static void main(String[] args) {
IBagShop bagShop = new BagShop();
PurchasingAgent purchasingAgent = new PurchasingAgent(bagShop);
purchasingAgent.buyBag("10万");
}
}

MESA Monitor

这个妹子成功买到这么贵的包包了!

2. 优点

那代理模式有啥优点呢?

  1. 由代理对象来间接访问目标对象,避免了直接访问目标对象给系统带来复杂杂性;
  2. 通过代理对象对原有对象增强。

第一点我们就不解释了,主要看第二点。

代购小哥尝到了甜头以后,就想吸引更多的妹子来找他买包,于是决定提供售前与售后服务,售前是提供与包包商店的服务员砍价的服务,售后则是附送一些小礼品服务,妹子们看到代购小哥的服务这么好,于是也介绍其他姐妹们来找代购买东西了。你看,这就是代购小哥对包包商店业务的增强。

那如何在代码上体现呢?因为是代购小哥提供的服务,因此我们找到代购PruchasingAgent类,在其buy方法中做修改即可:

public class PurchasingAgent implements IBagShop {
private IBagShop bagShop;
public PurchasingAgent(IBagShop bagShop) {
super();
this.bagShop = bagShop;
}
@Override
public void buyBag(String num) {
// 售前服务:砍价
System.out.println("代购砍了50%的价格,好厉害啊!");
// 调用包包商店的买服务
bagShop.buyBag(num);
// 售后服务:送小礼品
System.out.println("代购送了价值5元的小礼品,真抠");
}
}

再次启动“妹子”类测试,测试结果为:

MESA Monitor

从上面例子可以看到,代购小哥通过增加售前售后服务增强了包包商店的买包服务业务,因此可以看到静态代理最大的优点就是:通过代理对象对原有业务进行增强。

3. 缺点

静态方法不满足设计模式原则中的开闭原则(设计模式一共有六大原则,而开闭原则是最重要的一个!纳尼,怪不得会有动态代理)。

那啥是开闭原则?程序对外扩展开放,修改关闭,换句话说,要增加新的功能时,我们可以增加新的模块代码来实现新的需求,但不能修改原有的代码来实现新需求。我们就继续通过代购来看下代理模式如果破坏了开闭原则。

经过一段时间,有妹子问代购小哥是否可以在他这里买鞋子,代购小哥虽然没有,但是代购小哥知道鞋子商店在哪啊,于是就接了这单,此后,代购小哥不仅仅代购包包,还代购鞋子了。

为了增加代购小哥的业务,我们如何修改代码呢?

首先我们需要定义一个新的鞋子商店接口,里面定义了一个购买鞋子的方法,然后再建立个实现了鞋子商店接口的鞋子商店类,并实现购买鞋子的方法,这里还是满足开闭原则的,毕竟是新建类和接口嘛。然后最头疼的地方来了,代理类PurchaingAgent需实现鞋子商店接口,且需要引入鞋子商店类,并实现购买鞋子的方法,这些都对源代码进行大量的改动,如果以后代购小哥还想扩展业务,比如代购吃的,代购奶粉,那就要继续改动,这样就违背开闭原则了。

4. 总结

最后,我再来总结下:

其实,计算机中的许多思想也是通过生活而来,特别是设计模式,我们可以在生活中找到许多映射。静态代理其实就是代理类通过引用被代理对象去执行方法,当我们需要对该方法前后进行其他处理时,我们只需要在代理类中进行更改就行。

二、动态代理

1.什么是动态代理

动态代理简单来说就是在程序执行过程中,创建代理对象,通过代理对象执行方法,给目标类的方法增加额外的功

能,也叫做功能增强。

2.jdk动态代理

  • 首先我们需要有一个目标类,在目标类的基础上通过动态代理实现功能增强

  • 创建InvocationHandler接口的实现类,在这个类中实现invoke方法,在invoke方法中实现给目标类的方法

    增强功能

  • 通过JDK中的Proxy创建代理,通过代理调用目标类中的方法,实现功能增强

3.代码实现

创建一个接口:

public interface SomeService {
void doSome();
void doOther();
}

目标类实现这个接口:

public class SomeServiceImpl implements SomeService {
@Override
public void doSome() {
System.out.println("执行doSome");
}
@Override
public void doOther() {
System.out.println("执行doOther");
}
}

创建一个工具类,里面是增强的功能:

public class ServiceTools {
public static void doLog(){
System.out.println("方法的执行时间:" + new Date());
}
public static void doTrans(){
System.out.println("方法执行完毕,提交事务");
}
}

创建InvocationHandler接口的实现类

public class MyInvocationHandler implements InvocationHandler {
//目标对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
//通过代理对象执行方法时,会调用执行这个invoke()方法
/**
*
* @param proxy 代理类实例
* @param method 被代理的方法
* @param args 方法的参数数组
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行MyInvocationHandler中的invoke方法");
//打印被调用的目标类中的方法名
System.out.println(method.getName());
Object res = null;
//此方法是通过动态代理实现的增强的功能
ServiceTools.doLog();
//执行目标类方法,通过Method实现
res = method.invoke(target, args);
//此方法是通过动态代理实现的增强的功能
ServiceTools.doTrans();
//返回目标方法的执行结果
return res;
}
}

通过Proxy创建代理

public class MyApp {
public static void main(String[] args) {
//创建目标对象
SomeService target = new SomeServiceImpl();
//创建InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(target);
//使用Proxy创建代理
SomeService proxy = (SomeService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
handler
);
//通过代理执行方法,会调用handle中的invoke()方法
proxy.doSome();
}
}

运行结果如下:

我是我,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!

posted @   colaer  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示