OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式
前言
前面我们学习了创建型设计模式,其中有5中,个人感觉比较重要的是工厂方法模式、单例模式、原型模式。接下来我将分享的是结构型模式!
一、适配器模式
1.1、适配器模式概述
适配器模式(Adapter)属于结构型设计模式,它的作用如同它的名字一样,用于转换接口。像我们的手机、电脑的电源适配器一样,适配器模式可以使彼此不兼容的代码间优雅地协作。
适配器模式将某个类的接口转换成客户端(用户)期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。
1)角色及职责
目标接口: Target,即我们想用适配器转换过去的新的目标接口。
被适配者: Adaptee,适配器应用的对象。
适配器: Adapter,转换接口的类
2)适用场景
大多数情况下适配器模式会用在这两个场景下:
系统中历史遗留的代码与新的代码之间的兼容处理,用于将旧的接口转换为新的接口,使得新的代码与旧的代码能够愉快地玩耍。
有两个或者多个系统或者模块存在不兼容的接口,用于将各个模块或系统间互不兼容的接口转换为通用的接口,使得它们愉快地合作。
1.2、类的适配器模式
核心思想就是:有一个Source类,拥有一个方法,待适配,目标接口是Targetable,通过Adapter类,将Source的功能扩展到Targetable里。
public class Source { public void method1() { System.out.println("this is original method!"); } } public interface Targetable { /* 与原类中的方法相同 */ public void method1(); /* 新的方法 */ public void method2(); } //类Source和接口Targetable因为不兼容,导致不能在一起工作//适配器Adapter则可以在不改变源代码的基础上解决这个问题//这样Targetable接口的实现类Adapter的对象即使Targetable类型,也能访问到Source中的方法public class Adapter extends Source implements Targetable { public void method2() { System.out.println("this is the targetable method!"); } } //测试类 这样Targetable接口的实现类就具有了Source类的功能。public class AdapterTest { public static void main(String[] args) { Targetable target = new Adapter(); target.method1(); target.method2(); } }
类的适配器模式
1.3、对象的适配器模式
基本思路和类的适配器模式相同,只是将Adapter类作修改,这次不继承Source类,而是持有Source类的实例,以达到解决兼容性的问题。
//只需要修改Adapter类的源码即可:public class Wrapper implements Targetable { private Source source; public Wrapper(Source source){ this.source = source; } public void method2() { System.out.println("this is the targetable method!"); } public void method1() { source.method1(); } }//测试类 输出与第一种情况一样,只是使用的适配方法不同而已。public class AdapterTest { public static void main(String[] args) { Source source = new Source(); Targetable target = new Wrapper(source); target.method1(); target.method2(); } }
对象的适配器模式
1.4、接口的适配器模式
接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,
有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式。
解决:借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取
得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。例如我们这GUI这个章节应该是见过不少的监听器接口的适配器类:XxxxAdapter。
public interface Sourceable { public void method1(); public void method2(); } //抽象类public abstract class Wrapper implements Sourceable{ public void method1(){} public void method2(){} }
接口的适配器模式
之后在我们写的子类中需要什么方法去重写什么方法就可以了,就不需要把接口中的所有方法都实现了。
1.5、总结
三种情况适配器模式的总结:
类的适配器模式:
当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
对象的适配器模式:
当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。
接口的适配器模式:
当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。
二、装饰器模式
2.1、适配器概述
装饰器模式(Decorator),属于结构型设计模式。通过对象组合的方式给对象动态增加行为。
顾名思义,装饰器模式就是给一个对象增加一些新的功能,而且是【动态】的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例
这里的动态指的是用户可以根据自己的需求把之前定好的功能任意组合。
JDK中的IO流部分就是典型的使用了装饰模式,回忆一下BufferedReader对象的是如何创建的?
1)角色以及职责
Component: 装饰器模式应用的目标组件的抽象
Concrete Component: Component 的具体实现
Decorator: 装饰器的抽象
Concrete Decorator: 装饰器的实现类
2)适用场景
想通过对象组合的方式动态的给对象增加行为
2.2、代码实现
//功能接口public interface Action {public void go();}//被装饰的类 就是需要我们装饰的目标public class Person implements Action{public void go() {System.out.println("我在走路");}}//抽象的装饰类public abstract class Decorator implements Action{private Action action;public Decorator(Action action) {this.action = action;}public void go() {this.action.go();}}//具体的装饰类 可以添加一个听音乐的功能public class ListenDecorator extends Decorator{public ListenDecorator(Action action) {super(action);}public void go() {listen();//可以在go方法【前】添加一个听音乐的功能 super.go();}public void listen(){System.out.println("我在听音乐");}}//具体的装饰类 可以添加一个休息的功能public class RelaxDecorator extends Decorator{public RelaxDecorator(Action action) {super(action);}public void go() {super.go();relax();//可以在go方法【后】添加一个休息的功能 }public void relax(){System.out.println("我在休息");}}//测试类public class Test { /*用户可以根据需求 任意给go方法添加听音乐或者休息的功能*///Action a = new Person();//Action a = new ListenDecorator(new Person());//Action a = new RelaxDecorator(new Person());//Action a = new RelaxDecorator(new ListenDecorator(new Person()));Action a = new ListenDecorator(new RelaxDecorator(new Person()));a.go();}
适配器模式
装饰器模式的应用场景:
需要扩展一个类的功能,但是又不能修改源代码。
动态的为一个对象增加功能,而且还能动态撤销。
缺点:产生过多相似的对象,不易排错!
三、代理模式(Proxy)-重点
3.1、代理模式概述
代理模式(Proxy)属于结构型设计模式。很多时候,为了节省资源或者控制对象访问,我们需要通过一个代理对象去访问指定的对象。这个代理对象,将作为真实对象的“替身”或者“占位符”,
让我们在控制对象访问的同时保持对目标对象的访问的透明性。这种编程技巧在面向对象编程中有一个固定的套路和约定的名称,即为代理模式。
其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?
因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做,此处的代理就是这个意思。
再如我们有的时候打官司,我们需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们的想法。
1)角色与职责
Subject: 代理模式作用的目标对象的抽象。有些人把它翻译为主题,不过我认为这里翻译为主体或许更为恰当。
Real Subject: Subject 的具体实现。可以简单的理解为代理模式作用的目标对象对应的类。
Proxy: 代理对象,Real Subject 的替身。
3.2、代码简单实现
//公共接口public interface Sourceable { public void method(); } //目标类/被代理类public class Source implements Sourceable { public void method() { System.out.println("the original method!"); } }//代理类public class Proxy implements Sourceable { private Source source; public Proxy(Source source){ this.source = source; } public void method() { before(); source.method(); atfer(); } private void atfer() { System.out.println("after proxy!"); } private void before() { System.out.println("before proxy!"); } } //测试类public class ProxyTest { public static void main(String[] args) { Source target = new Source();Sourceable proxy = new Proxy(target); proxy.method(); } }
简单的实现
1)代理模式的应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
1)修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
2)就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护!
2)对代理模式的一些重要扩展
用户tom---买--->商品
由于各种原因导致不是很方便购买,所以就找代购
用户tom---找--->代购者zs---买--->商品
那么在代理模式中,用户tom就是目标对象,代购者zs就是代理对象
创建目标对象的类叫目标类或者被代理类
创建代理对象的类叫代理类
3.3、代理模式和装饰器模式的区别
装饰器模式和代理模式在很多情况下,大部分代码都是类似的,但是这俩种设计的意图是不一样的,装饰模式是增强被包装对象的功能,代理模式是控制被代理对象的行为 。
例如:一块代码,如果被描述为使用了装饰模式,那么我们就知道设计的意图是增加被包装对象的功能,
如果被描述为使用了代理模式,那么我们就知道设计的意图是控制被代理对象的行为,虽然这俩种情况下他们的代码结构基本相同。
装饰器模式:能动态的新增或组合对象的行为。
代理模式 :为目标对象提供一种代理以便控制对这个对象的访问。
装饰模式是“新增行为”,而代理模式是“控制访问”。
分析:
装饰模式:对被装饰的对象增加额外的行为
如:杯子生产线,杯子必须可以装水,在生产线上可以给杯子涂颜色,加杯盖,但要保证杯子可以装水。
代理模式:对被代理的对象提供访问控制。
如:客户网上商城订购商品,网上商城是厂家的代理,网上商城可以帮客户完成订购商品的任务,但是商城可以对商品进行控制,
不交钱不给商品,人不在不给商品,也可以赠送你额外的礼品,代金券。
3.4、代理类的分类
代理类可分为两种:
静态代理类:
由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理类:
在程序运行时,运用反射机制动态创建而成。
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,
因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包下面的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
CGLib代理(第三方类库)
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib 采用了非常底层的字节码技术,
其原理是通过字节码技术为目标对象创建一个子类对象,并在子类对象中拦截所有父类方法的调用,然后在方法调用前后调用后都可以加入自己想要执行的代码。
需要这种方法只是需要俩个第三方jar包: cglib-3.2.1.jar和asm-5.0.4.jar
同时很多框架已经把这些jar包整合到一起了,比如spring框架的spring-core-3.2.4.RELEASE.jar,这一个jar包就包括上述俩个jar包的大多数功能。
3.5、代码实现
1)静态代理: staticProxy
//公共接口public interface HelloService {void sayHello();}//委托类public class HelloServiceImpl implements HelloService{public void sayHello() {System.out.println("hello world");}}//代理类public class HelloServiceProxy implements HelloService{private HelloService target;public HelloServiceProxy(HelloService target) {this.target = target;}public void sayHello() {System.out.println("log:sayHello马上要执行了...");target.sayHello();}}//测试类public class Test {public static void main(String[] args) {//目标对象HelloService target = new HelloServiceImpl();//代理对象HelloService proxy = new HelloServiceProxy(target);proxy.sayHello();}}
静态代理
2)JDK的动态代理: dynamicProxy
//Student类public class Student {private long id;private String name;private int age;get/set}//日志类public class StudentLogger {public void log(String msg){System.out.println("log: "+msg);}}//Service接口 处理学生的相关业务public interface IStudentService {void save(Student s);void delete(long id);Student find(long id);}//接口的一个简单实现public class StudentServiceImpl implements IStudentService {public void delete(long id) {System.out.println("student is deleted...");}public Student find(long id) {System.out.println("student is found...");return null;}public void save(Student s) {System.out.println("student is saved...");}}//InvocationHandler接口的实现类 //JDK动态代理中必须用到的接口实现public class MyHandler implements InvocationHandler{private Object target;private StudentLogger logger = new StudentLogger();public MyHandler(Object target, StudentLogger logger) {this.target = target;this.logger = logger;}public MyHandler(Object target) {this.target = target;}//参数1 proxy 将来给目标对象所动态产生的代理对象//参数2 method 将来你所调用的目标对象中的方法的镜像//参数3 args 将来你所调用方法的时候所传的参数public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String msg = method.getName()+"方法被调用了...";logger.log(msg);Object o = method.invoke(target, args);return o;}}//测试类public class DProxyTest {public static void main(String[] args) {IStudentService target = new StudentServiceImpl();ClassLoader loader = target.getClass().getClassLoader();Class<?>[] interfaces = target.getClass().getInterfaces();InvocationHandler h = new MyHandler(target);//参数1 loader 目标对象的类加载器//参数2 interfaces 目标对象所实现的接口//参数3 h InvocationHandler接口的实现类对象IStudentService proxy = (IStudentService)Proxy.newProxyInstance(loader, interfaces, h);proxy.delete(1);proxy.save(null);proxy.find(1);System.out.println(proxy.toString());System.out.println(proxy.getClass());System.out.println(target.getClass());}}
jdk的动态代理
3)第三方jar包提供的动态代理(cglib) CglibProxy
//目标的对象 没有实现接口public class BookService {public void addBook() { System.out.println("添加书籍成功"); } }//产生代理对象的工厂类public class MyCglibProxyFactory implements MethodInterceptor { public Object getInstance(Class<?> c) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(c); enhancer.setCallback(this); return enhancer.create(); } public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开始执行方法"); //这句代码最终会执行到我们目标对象中的方法 proxy.invokeSuper(obj, args); System.out.println("方法执行结束"); return null; } }//测试类public class TestCglibProxy {public static void main(String[] args) { MyCglibProxyFactory cglib=new MyCglibProxyFactory(); BookService bookCglib= (BookService)cglib.getInstance(new BookService().getClass()); bookCglib.addBook(); System.out.println(bookCglib.getClass());} }
cglib代理
喜欢就点个“推荐”哦!
转载于:https://www.cnblogs.com/zhangyinhua/p/7741932.html
OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式相关推荐
- 设计模式05——结构型模式
一.概述 结构型模式描述如何将类或对象按某种布局组成更大的结构.它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象.由于组合关系或聚合关系比继承关系耦合 ...
- 备战面试日记(3.3) - (设计模式.23种设计模式之结构型模式)
本人本科毕业,21届毕业生,一年工作经验,简历专业技能如下,现根据简历,并根据所学知识复习准备面试. 记录日期:2022.1.9 大部分知识点只做大致介绍,具体内容根据推荐博文链接进行详细复习. 文章 ...
- 经典伴读_GOF设计模式_结构型模式
经典伴读系列文章,不是读书笔记,自己的理解加上实际项目中运用,旨在5天读懂这本书.如果这篇文章对您有些用处,请点赞告诉我O(∩_∩)O. 如何使用设计模式抽象实例化过程.请参考<经典伴读_GOF ...
- 设计模式之结构型模式(5种)
目录 结构型模式(Structural Pattern):怎么构造一个对象(行为.属性) 一.适配器模式 二.桥接模式(Bridge) 三.装饰者模式 设计模式在JAVA I/O库中的应用 案例 使用 ...
- JAVA23种设计模式(2)-结构型模式7种
JAVA23种设计模式(2)-结构型模式7种 把类结合在一起形成更大的结构 适配器模式(adapter) 一句话:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容 这是平时比较常见的一种模 ...
- Java设计模式之结构型:享元模式
一.什么是享元模式: 享元模式通过共享技术有效地支持细粒度.状态变化小的对象复用,当系统中存在有多个相同的对象,那么只共享一份,不必每个都去实例化一个对象,极大地减少系统中对象的数量.比如说一个文本系 ...
- Java设计模式之结构型:外观模式
一.什么是外观模式: 外观模式通过对客户端提供一个统一的接口,用于访问子系统中的一群接口.使用外观模式有以下几点好处: (1)更加易用:使得子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要 ...
- Java设计模式之结构型:代理模式
前言: 我们一般在租房子时会去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你做:再比如我们打官司需要请律师,因为律师在法律方面有专长,可以替我们进行操作,表达我们 ...
- 设计模式 之 结构型模式
设计模式 之 结构型模式 模式 & 描述 包括 结构型模式 这些设计模式关注类和对象的组合.继承的概念被用来组合接口和定义组合对象获得新功能的方式. 适配器模式(Adapter Pattern ...
- 设计模式3——结构型模式
结构型模式描述如何将类或对象按某种布局组成更大的结构,它分为类结构型和对象结构型模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象. 由于组合关系或聚合关系比继承关系耦合度低,满足&q ...
最新文章
- 【错误记录】Android 应用配置第三方 so 动态库 ( /data/app/comxxx==/base.apk/lib/arm64-v8a]couldn‘t find “libx.so“ )
- python 多分类模型优化_【Python与机器学习】:利用Keras进行多类分类
- CodeForces - 1486D Max Median(二分+最长连续子段和)
- Qt文档阅读笔记-重现GUI事件进行单元测试
- Storm 配置说明
- 【转载】Gradle命令行黑魔法
- 美国商务部发布软件物料清单 (SBOM) 的最小元素(中)
- sqlserver数据库分组查询
- [MDB] EXP 导入数据库
- 绿盟WEB服务扫描漏洞处理
- word插入公式不自动斜体的解决办法
- 潜入蓝翔技校二十天 探究蓝翔黑客真正的奥秘
- 一个计算机程序员高手的成长 [转]
- 关于计算机素养论文,计算机应用及青少年网络素养培养论文
- java梅森素数计算程序_梅森素数计算器(prime95)
- 产品思考 - 免费移动宽带自带魔百和的烦恼
- 实时即未来,车联网项目之电子围栏分析【六】
- 【VSCode】安装VSCode都需要配置什么?
- angular实时监控技术
- 7-3 求n以内最大的k个素数以及它们的和
热门文章
- 条码打印软件之排版工具的应用
- Swift Invalid bitcode version (Producer: '802.0.41.0_0' Reader: '800.0.42.1_0')
- UILabel attributedText
- Airflow Python工作流引擎的重要概念介绍
- 微软市值突破2万亿美元!我入职后,股票翻了近9倍!
- java castor_Castor功能与应用参考四
- ad中使用智能粘贴_AD10 复制问题(复制方法和智能粘贴 拼版)
- [个人向]超快速了解微信小程序:看这篇就够了!(注册、语言、框架、配额等简要说明)
- java计算一元二次方程的根_java基础 --- 求一元二次方程的根(分情况讨论)-Go语言中文社区...
- DAPP开发(三)——智能合约开发