Java日记之设计模式总结(结构型)

前言 推荐看这篇文章之前先了解Java日记之设计模式初探。
结构型设计模式总共有7种
1.外观模式
2.装饰者模式
3.适配器模式
4.享元模式
5.组合模式
6.桥接模式
7.代理模式


1.外观模式

  • 定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。和外观模式相关的设计模式有中介者模式。
  • 适用场景:子系统越来越复杂,增加外观模式提供简单调用接口。构建多层系统结构,利用外观对象作为每层的入口,简化层间调用。
  • 优点:简化了调用过程,无需深入了解系统,防止带来风险。减少系统依赖,松散耦合。可以更好的划分访问层次。同时符合迪米特法则,即最少知道原则。
  • 缺点:增加子系统,扩展子系统行为容易引入风险,不符合开闭原则。
代码举例:
//物品实体类 public class PointsGift {private String name; public PointsGift(String name){ this.name = name; }public String getName() { return name; }public void setName(String name) { this.name = name; } }

//支付子系统 public class PointsPaymentService {public boolean pay(PointsGift pointsGift) { //扣减积分逻辑 System.out.println("支付" + pointsGift.getName() + "积分成功"); return true; } }//校验子系统 public class QualifyService {public boolean isAvailable(PointsGift pointsGift) { System.out.println("校验" + pointsGift.getName() + "积分资格通过,库存通过"); return true; }}//物流子系统 public class ShippingService {//返回订单号 public String shipGift(PointsGift pointsGift){ //物流系统的对接逻辑 System.out.println(pointsGift.getName()+"进入物流系统"); String shippingOrderNo = "666"; return shippingOrderNo; }}

//外观类 public class GiftExchangeService {private QualifyService qualifyService = new QualifyService(); private PointsPaymentService pointsPaymentService = new PointsPaymentService(); private ShippingService shippingService = new ShippingService(); public void giftExchange(PointsGift pointsGift) { //资格校验通过 if (qualifyService.isAvailable(pointsGift)) { //支付通过 if (pointsPaymentService.pay(pointsGift)) { //返回订单号 String shippingOrderNo = shippingService.shipGift(pointsGift); System.out.println("物流系统下单成功,订单号是:" + shippingOrderNo); } } else { //不通过 //TODO } } }//测试类 public class Test {public static void main(String[] args) { PointsGift pointsGift = new PointsGift("T桖"); //正常来说可以通过spring注入 GiftExchangeService giftExchangeService = new GiftExchangeService(); giftExchangeService.giftExchange(pointsGift); } }

我们举个例子,比如有一个积分商城,你需要去进行换购物品,这时候就需要经过积分校验-支付-进入物流这3个过程,也就是3个子系统,而外观类呢就是把这3个子系统结合在一起,我们应用层只需要和外观类交互就可以换购1个商品,而不不用去在意那3个子系统的哪个先调用和一些其中具体的逻辑,从UML图也可以看出外观模式的核心思想就是应用层不要跟子系统去相连,只要通过外观类联系就好了,当然外观类也可以改成抽象外观类,这个就需要通过具体的业务逻辑判断了。

Java日记之设计模式总结(结构型)
文章图片
外观模式运行结果
Java日记之设计模式总结(结构型)
文章图片
外观模式UML


2.装饰者模式
  • 定义:在不改变原有对象的基础之上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象的功能)。这个模式在生活中还是挺常见的,比如买个蛋糕,我想加个巧克力,慕斯之类的。根据不同的需求来装饰。装饰者模式本身也是用了继承。继承是扩展形式之一,但不是弹性设计的最佳方式。
  • 适用场景:扩展一个类的功能或给一个类附加职责。可以动态的给一个对象添加功能,这些功能也可以动态的撤销。
  • 优点:继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。通过使用不同装饰以及这些装饰类的排列组合,可以实现不同的效果。符合开闭原则。
  • 缺点:会出现更多的代码,更多的类,增加程序复杂性。动态装饰时,多层装饰时会使程序更复杂。
  • 相关设计模式:装饰者模式关注在一个对象上动态的添加方法。代理模式关注于控制对对象的访问,代理模式中的代理类可以对他的客户隐藏一个对象的具体信息。还有一个相关的就是,装饰者和适配器模式,它们都可以叫作包装模式,装饰者和被装饰者可以实现相同的接口,或者装饰者是被装饰者的子类。适配器中,适配器和被适配的具有不同的接口。当然有少数重合的接口。
代码举例:
//煎饼抽象类 public abstract class ABatterCake {public abstract String getDesc(); public abstract int cost(); }//煎饼实体类 public class BatterCake extends ABatterCake{@Override public String getDesc() { return "煎饼"; }@Override public int cost() { return 8; } }//装饰者模式抽象类 public abstract class AbstractDecorator extends ABatterCake {//持有抽象煎饼的引用 private ABatterCake aBatterCake; protected abstract void odSomething(); public AbstractDecorator(ABatterCake aBatterCake) { this.aBatterCake = aBatterCake; }@Override public String getDesc() { return aBatterCake.getDesc(); }@Override public int cost() { return aBatterCake.cost(); } }

这里举例一个场景,单位楼下有一个卖煎饼的,然后在单位的茶水间吃早餐,有时候加1个蛋或者香肠。这时候我们就需要装饰者模式了,我们首先定义1个煎饼的抽象类,还有最需要的装饰者抽象类,,我们这里持有抽象煎饼类的引用,接下来,我们该怎么给这个煎饼添加鸡蛋和香肠呢。代码如下:
//加蛋的类 public class EggDecorator extends AbstractDecorator {@Override protected void odSomething() {}public EggDecorator(ABatterCake aBatterCake) { super(aBatterCake); }@Override public String getDesc() { return super.getDesc() + "加1个煎蛋 "; }@Override public int cost() { return super.cost() + 1; } }//加香肠的类 public class SausageDecorator extends AbstractDecorator {@Override protected void odSomething() {}public SausageDecorator(ABatterCake aBatterCake) { super(aBatterCake); }@Override public String getDesc() { return super.getDesc() + "加1根香肠 "; }@Override public int cost() { return super.cost() + 2; } }//测试类 public class Test {public static void main(String[] args) { ABatterCake aBatterCake; aBatterCake = new BatterCake(); aBatterCake = new EggDecorator(aBatterCake); aBatterCake = new SausageDecorator(aBatterCake); aBatterCake = new EggDecorator(aBatterCake); System.out.println(aBatterCake.getDesc() + "销售价格:" + aBatterCake.cost()); } }

我们让香肠类和蛋糕类去继承装饰者类,因为装饰者类不是无参的构造函数,所以我们要实现构造方法。然后通过装饰者模式来返回新的对象。这样子我们需要加香肠还是煎蛋,只需要重新new一个,然后获得新的返回对象就行。我们从UML中也可以看到,煎蛋类和香肠类是同级的。

Java日记之设计模式总结(结构型)
文章图片
装饰者模式结果
Java日记之设计模式总结(结构型)
文章图片
装饰者模式UML类图


3.适配器模式
  • 定义:将一个类的接口转换成客户期望的另一个接口。使原本接口不兼容的类可以一起工作。
  • 应用场景:已经存在的类,它的方法和需求不匹配时,也就是方法结果相同或者想相似。这个设计模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不相同的情况下的解决方案。
  • 优点:能提高类的透明性和复用,现有的类但不需要改变。目标类和适配器解耦,提高程序扩展性。符合开闭原则。
  • 缺点:适配器模式编写过程中需要全面的考虑,可能会增加系统的复杂性。另外会增加系统代码可读的难度。
  • 扩展:对象适配器(符合组合复用原则,并且使用委托机制)。类适配器(使用类继承来实现的)。
适配器和外观模式都是对现有的类进行封装,外观定义了新的接口,适配器则是复用一个原有的接口,适配器是使2个已有的接口协同工作,而外观则是在系统中提供一个更方便的访问入口,这两个最大的区别就是适配力度不同,外观模式是用来适配整个子系统,外观所针对的对象力度更大。
类适配器模式 代码举例:
//被适配者 public class Adaptee {public void adapteeRequest(){ System.out.println("被适配者的方法"); } }//转换接口 public interface Target {void request(); }//适配器 public class Adapter extends Adaptee implements Target{ @Override public void request() { //添加各种逻辑代码 super.adapteeRequest(); } }//实现类 public class ConcreteTarget implements Target{@Override public void request() { System.out.println("concreteTarget目标方法"); } }//测试类 public class Test {public static void main(String[] args) { Target target = new ConcreteTarget(); target.request(); Target adapterTarget = new Adapter(); adapterTarget.request(); } }

首先建立一个被适配者的类,然后建立一个实现类ConcreteTarget,接下来我们就需要一个适配器Adapter来进行适配器,这个Adapter要继承被适配器的类和Target接口。这样我们的接口就可以通过Adapter来实现了。也就是说Target的具体实现通过Adapter已经移交给了Adaptee,这就是类适配器,这里强调的是继承,通过继承来获取被适配者的一些方法,我们可以在request()添加各种逻辑代码。

Java日记之设计模式总结(结构型)
文章图片
类适配器模式结果
Java日记之设计模式总结(结构型)
文章图片
类适配器UML


对象适配器模式
//适配器类 public class Adapter implements Target{private Adaptee adaptee= new Adaptee(); @Override public void request() { ...... adaptee.adapteeRequest(); } }

其它的代码不变,变就是适配器类,这里不通过继承,直接通过持有Adaptee的引用来进行组合。结果不变。

Java日记之设计模式总结(结构型)
文章图片
对象适配器模式UML
我们这里在举一个变压器的例子,来理解适配器设计模式。
//220V电压 public class AC220 {public int outPutAC220V() { int outPut = 220; System.out.println("输出交流电" + outPut + "V"); return outPut; } }//5V直流电 public interface DC5 {int outputDC5V(); }//适配器类 public class PowerAdapter implements DC5 {AC220 ac220 = new AC220(); @Override public int outputDC5V() { int adapterInput = ac220.outPutAC220V(); //变压器 int adapterOutput = adapterInput / 44; System.out.println("使用PowerAdapter输入AC:" + adapterInput + "V" + "输出DC:" + adapterOutput + "V"); return adapterOutput; } }//测试类 public class Test {public static void main(String[] args) { DC5 dc5 = new PowerAdapter(); dc5.outputDC5V(); } }


Java日记之设计模式总结(结构型)
文章图片
变压器运行结果
从上诉代码可以看出,220V就是一个被适配者的类,然后我们通过变压器(也就是适配器类)来适配输出AC,这样就可以进行转换了。


享元模式
  • 定义:提供了减少对象数量从而改善应用所需要的对象结构的方式。运用共享技术有效地支持大量细粒度的对象。一句话,就是减少创建对象的数量,从而减少内存的占用,提高性能。
  • 适用场景:系统底层的开发,以便解决系统的性能问题。还有系统有大量的相似对象,需要换冲刺的场景。
  • 优点:减少对象的创建,降低内存中的对象数量,降低系统的内存,同时提高了效率。还有就是减少内存之外的其他资源的占用(比如时间等,因为new一个对象需要消耗时间)。
  • 缺点:关注内部/外部状态,关注线程安全问题。是系统程序的逻辑复杂化。
  • 扩展:内部状态(是指在享元对象的内部并且不会随着环境改变而改变的共享部分,也就是说外部环境怎么变话,内部都不会变)。外部状态(那随着环境改变的状态就是外部状态,这种状态是不可以共享的状态,是记录在享元对象的外部)。通俗一点,内部状态可以认为是享元对象的属性,这个属性不会随着外部的环境变化而变化。外部状态的话就是我们在调用享元模式获取享元对象的时候,我们通过方法的参数传过来一个状态,例如说int = 0的时候是怎么样,1的时候是怎么样。
  • 相关设计模式:代理模式(需要一个代理类,需要花费资源,那么就可以用享元模式提高速度)。单例模式(容器单例就是和享元模式的一个结合)。
代码举例:
我们现在有一个场景,一个公司要求各个部门的管理者做年底总结报告,如果报告已经生成过了,就没必要就去new一个了。这里也会结合工程模式。
//经理功能接口 public interface Employee {void report(); }//经理 public class Manager implements Employee {//所在部门 private String department; //报告内容 private String reportContent; public Manager(String department) { this.department = department; }@Override public void report() { System.out.println(reportContent); }public String getReportContent() { return reportContent; }public void setReportContent(String reportContent) { this.reportContent = reportContent; } }//工厂模式 public class EmployeeFactory {//容器单例 private static final Map EMPLOYEE_MAP = new HashMap<>(); public static Employee getManager(String department) { Manager manager = (Manager) EMPLOYEE_MAP.get(department); if (manager == null) { manager = new Manager(department); System.out.print("创建部门经理:" + department); String reportContent = department + "部门汇报:此次报告的主要内容是******"; manager.setReportContent(reportContent); System.out.println(" 创建报告:" + reportContent); EMPLOYEE_MAP.put(department, manager); }return manager; } }//测试类 public class Test {private static final String[] departments = {"RD", "QA", "PM", "BD"}; public static void main(String[] args) { for (int i = 0; i < 10; i++) { //随机叫部门报告 String department = departments[(int) (Math.random() * departments.length)]; Manager manager = (Manager) EmployeeFactory.getManager(department); manager.report(); } } }

Java日记之设计模式总结(结构型)
文章图片
享元模式运行结果
从上述代码就可以看出EMPLOYEE_MAP就是享元模式的缓冲池,每次叫部门做报告的时候,如果没有做报告的就会new一个报告出来,如果之前已经报告过的,就直接从缓冲池里进行报告,不用再次创建报告。对于部门经理,也就是department,它是声明在外部的,并且manager的department这个状态依赖于外部传入的参数,所以可以认为它是外部状态,因为传入不同的department,获得的manager的department这个属性是不一样的,对于Manager这个类的实体,我们就可以认为department是外部状态,那内部状态就是比如说我直接在Manager直接声明一个String变量。
private String title = "部门经理";

这个时候,不论外部怎么传递department或者reportContent,Manager的title是不变的,它不会随着的外部状态而变化。title永远都是部门经理。


5.组合模式
  • 定义:将对象组合成树形结构以表示“部分 - 整体”的层次结构。组合模式使客户端对单个对象和组合对象保持一致的方式处理。比如说,我们有一个树形结构的菜单,菜单里面还有子菜单,在子菜单里面可能还有子菜单。

    Java日记之设计模式总结(结构型)
    文章图片
    组合模式如图
  • 适用场景:希望客户端忽略组合对象与单个对象的差异时,处理一个树形结构。
  • 优点:清楚的定义分层次的复杂对象,表示对象的全部或部分层次。让客户端忽略层次的差异,方便对整个层次结构进行控制。同时简化客户端代码。符合开闭原则。
  • 缺点:限制类型时会较会复杂。使设计变的更加抽象。
  • 相关设计模式:访问者模式(来访问组合模式的递归结构)。
代码举例:
老样子,举例一个业务场景,拿慕课网来说,这里有很多课程,也有课程目录,课程有名称和价格,例如Java课程,那这个课程就属于Java目录下,Java目录下有很多Java相关的课程,Python也一样。如果使课程的目录和课程继承同一个抽象类,例如目录组件,那么就可以把课程本身,还有由课程组合成的课程目录视为同一类对象进行操作,但是在操作上还会有一些差别,具体的差别在定制化处理。
//组合模式抽象类 public abstract class CatalogComponent {public void add(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持添加操作"); }public void remove(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持删除操作"); }public String getName(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持获取名称操作"); }public double getPrice(CatalogComponent catalogComponent){ throw new UnsupportedOperationException("不支持获取价格操作"); }public void print(){ throw new UnsupportedOperationException("不支持打印操作"); }}//课程类 public class Course extends CatalogComponent {private String name; private double price; public Course(String name, double price) { this.name = name; this.price = price; }@Override public String getName(CatalogComponent catalogComponent) { return this.name; }@Override public double getPrice(CatalogComponent catalogComponent) { return this.price; }@Override public void print() { System.out.println("Course name:" + name + "price:" + price); } }//目录类 public class CourseCatalog extends CatalogComponent {private List items = new ArrayList<>(); private String name; public CourseCatalog(String name) { this.name = name; }@Override public void add(CatalogComponent catalogComponent) { items.add(catalogComponent); }@Override public void remove(CatalogComponent catalogComponent) { items.remove(catalogComponent); }@Override public void print() { System.out.println(this.name); for (CatalogComponent catalogComponent : items){ System.out.print(" "); catalogComponent.print(); } }public String getName() { return this.name; } }//测试类 public class Test {public static void main(String[] args) { CatalogComponent linuxCourse = new Course("Linux课程", 11); CatalogComponent windowsCourse = new Course("Windows课程", 10); CatalogComponent javaCourseCatalog = new CourseCatalog("Java课程目录"); CatalogComponent mallCourse1 = new Course("Java电商课程1期", 55); CatalogComponent mallCourse2 = new Course("Java电商课程2期", 66); CatalogComponent designPattern = new Course("Java设计模式", 77); javaCourseCatalog.add(mallCourse1); javaCourseCatalog.add(mallCourse2); javaCourseCatalog.add(designPattern); //主目录 CatalogComponent imoocMainCourseCatalog = new CourseCatalog("慕课网课程主目录"); imoocMainCourseCatalog.add(linuxCourse); imoocMainCourseCatalog.add(windowsCourse); imoocMainCourseCatalog.add(javaCourseCatalog); imoocMainCourseCatalog.print(); } }

Java日记之设计模式总结(结构型)
文章图片
组合模式运行结果
Java日记之设计模式总结(结构型)
文章图片
组合模式UML 从运行结果我们也可以看到,我们new了一个主目录然后分别添加1个二级目录和2个课程,以输出前面空格来代表层级,在Java目录下面又有3个Java课程,这里从UML也可以看出CatalogComponent和CatalogComponent形成了组合关系。但是这里就会体现组合设计模式的缺点了,yejiushi也就是如果说要动态的对类型进行限制,会使业务逻辑比较复杂,例如说现在先Java课程目录本身是二级目录,慕课网目录是一级目录,对于空格来说,二级目录是需要打印2个的,从而区分从属关系,对于这里就要进行动态的类型判断了。也就是说使用组合设计模式,我们就要对抽象组件的设计多花点心思,而且还有缺点目录和课程哪些符合业务逻辑的重写,哪些不符合。
接着,我们现在还要改代码,其实很简单,只要添加1个层级就好了
public class CourseCatalog extends CatalogComponent {private List items = new ArrayList<>(); private String name; //添加层级 private Integer level; ......@Override public void print() { System.out.println(this.name); for (CatalogComponent catalogComponent : items){ //进行输出 if (this.level != null){ for (int i = 0; i


Java日记之设计模式总结(结构型)
文章图片
添加level后的输出结果


6.桥接模式
  • 定义:将抽象部分与它的具体实现部分分离,使它们都可以独立地变化。通过组合的方式建立两个类之间联系,而不是继承。
  • 适用场景:抽象和具体实现之间增加更多的灵活性,适用桥接模式就可以避免这两个层次之间建立静态的继承关系,通过桥接模式建立一层关联关系,另外抽象部分和具体的实现部分都可以以继承的关系独立扩展,互不影响。就可以将抽象化的子类的对象和一个具体实现化的对象进行组合。一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。还有不希望使用继承,或因为多层继承导致系统类的个数剧增。
  • 优点:分离抽象部分及其具体实现部分。提高了系统的可扩展性。
  • 缺点:增加了系统的理解和设计难度,由于类之间的关系建立了抽象层,要求编程的时候,一开始就要对抽象层进行设计编程。需要正确的识别出系统中两个独立变化的维度。
  • 相关设计模式:组合模式(更强调部分和整体间的组合),桥接模式强调平行级别不同类的集合。还有就是适配器模式,这两个设计模式都是为了两个东西配合合作,适配器改变已有的接口,让他们之间可以相互配合,而qia桥接是分离抽象和具体的实现类,目的是分离。
代码举例:
老样子举个例子,在中国有很多银行,农业(ABC)和工商银行(ICBC),然后我们就有自己的账号,关于存蓄账号分为定期账号和活期账号。定期可以存3个月或者1年,银行还会给利息,利息比活期账号利息高很多。这里面分2大块,银行和账号。·
//定义账号接口 public interface Account {Account openAccount(); //查看账户类型 void showAccount(); }//定期账号 public class DepositAccount implements Account{@Override public Account openAccount() { System.out.println("打开定期账号"); return new DepositAccount(); }@Override public void showAccount() { System.out.println("这是一个定期账号"); } }//活期账号 public class SavingAccount implements Account{@Override public Account openAccount() { System.out.println("打开活期账号"); return new SavingAccount(); }@Override public void showAccount() { System.out.println("这是一个活期账号"); } }

//银行抽象类 public abstract class Bank {protected Account account; public Bank(Account account){ this.account = account; }abstract Account openAccount(); }//农业银行 public class ABCBank extends Bank{public ABCBank(Account account) { super(account); }@Override Account openAccount() { System.out.println("打开中国农业银行账号"); //这里要注意委托实现 account.openAccount(); return account; } }//工商银行 public class ICBCBank extends Bank {public ICBCBank(Account account) { super(account); }@Override Account openAccount() { System.out.println("打开中国工商银行账号"); //这里要注意委托实现 account.openAccount(); return account; } }

Java日记之设计模式总结(结构型)
文章图片
桥接模式UML
首先我们创建了Account,它是这个桥的实现接口,左侧是实现层,右侧是抽象层,桥接模式最重要的就是桥的左侧和右侧划分清楚,然后在Bank抽象类里持有Account接口的引用,这就是桥接模式的核心,通过组合的方式,底下的都是具体的类实现。而且可以一直扩展,这就是桥接模式的精华所在。
public class Test {public static void main(String[] args) { Bank icbcBank = new ICBCBank(new DepositAccount()); Account icbcAccount = icbcBank.openAccount(); icbcAccount.showAccount(); Bank abcBank = new ICBCBank(new SavingAccount()); Account abcAccount = abcBank.openAccount(); abcAccount.showAccount(); } }


Java日记之设计模式总结(结构型)
文章图片
桥接模式运行结果


7.代理模式
  • 定义:为其它对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介的作用。其实就跟租房子是一样的,假设找中介租房子,然后找全托管的房子,最后出租出来的房子是房东,房东是目标对象,合同朝你,水电费结算等等都是代理类来做,代理类就是中介,但是我们可以直接和代理类也就是中介签合同,不需要和房东直接接触。
  • 优点:代理模式能将代理对象与真实被调用的对象分离,一定程度上降低了系统的耦合度m,扩展性好,保护目标对象,也增强目标对象。
  • 缺点:代理模式会造成系统设计中类的数目增加。在客户端和目标对象增加了一个代理对象,会造成请求速度变慢,也增加了系统的复杂度。
  • 扩展:静态代理。动态代理(Jdk中的动态代理只能对实现的接口的类生成代理,并不能具体的针对实现类,动态代理无法代理类,但是可以代理接口)。CGLib代理(是可以代理类的,它是针对类实现进行代理 ,如果代理一个类,CGLib会生成一个被代理类的子类,覆盖其中的方法。通过继承)。
  • 相关设计模式:装饰者模式(实现上比较相似,但是目的不同,装饰者是给对象加上行为,代理模式是控制访问,更加注重设置代理人的方式来增加目标对象)。适配器模式(它主要考虑对象的接口,而代理模式是不能改变所代理的接口)。
代码举例:
动态代理
Java日记之设计模式总结(结构型)
文章图片
代理模式UML图,侵删
从UML图可以看出代理类与实现类都是继承了抽象类,这样的好处就是可以有一样的方法。
//接口 public interface Subject {void visit(); }//真实类 public class RealSubject implements Subject{private String name = "juju"; @Override public void visit() { System.out.println(name); } }//代理类 public class ProxySubject implements Subject{private Subject subject; public ProxySubject(Subject subject){ this.subject =subject; }@Override public void visit() { subject.visit(); } }//测试 public class Test {public static void main(String[] args) { ProxySubject proxySubject = new ProxySubject(new RealSubject()); proxySubject.visit(); } }


Java日记之设计模式总结(结构型)
文章图片
静态代理运行结果
从上面代码可以看出,代理类接收一个Subject对象,任何实现该接口的真实类,都可以通过这个代理类进行代理,增加了通用性,但是也有一个缺点,就是每一个代理类都必须要实现真是类的接口,这样会造成后期的臃肿。


动态代理
接口和真实类还是和静态代理一样,然后我们在创建一个DynamicProxy来进行动态代理来继承InvocationHandler,并重写invoke()方法。然后进行测试。
public class DynamicProxy implements InvocationHandler {private Object object; public DynamicProxy(Object object){ this.object = object; }@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(object,args); return result; } }//测试类 public class Test { public static void main(String[] args) { Subject realSubject = new RealSubject(); DynamicProxy dynamicProxy = new DynamicProxy(realSubject); ClassLoader classLoader = realSubject.getClass().getClassLoader(); Subject subject = (Subject) Proxy.newProxyInstance(classLoader,new Class[]{Subject.class},dynamicProxy); subject.visit(); } }

【Java日记之设计模式总结(结构型)】这里创建动态对象通过Proxy.newProxyInstance()来进行创建,第一个参数为表示当前使用到的appClassloader,第二个为目标对象实现的一组接口,第三个为表示当前的InvocationHandler实现实例对象。这里的动态对象创建是通过Jdk来实现的,当然还有其他通过非Jdk来实现的方法。


参考
  • Java设计模式之代理模式
  • Java设计模式精讲 Debug方式+内存分析

    推荐阅读