前言

  前面我们学习了创建型设计模式,其中有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();
proxy.save(null);
proxy.find(); 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代理

  

喜欢就点个“推荐”哦!

OOAD-设计模式(四)结构型模式之适配器、装饰器、代理模式的更多相关文章

  1. 【设计模式】结构型03外观模式(Facade Pattern)

    [设计模式]结构型02装饰模式(Decorator Pattern) 意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 主要解决:降低访问 ...

  2. GoF的23种设计模式之结构型模式的特点和分类

    结构型模式描述如何将类或对象按某种布局组成更大的结构.它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象. 由于组合关系或聚合关系比继承关系耦合度低,满足 ...

  3. 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释

    代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...

  4. Head First 设计模式 —— 03. 装饰器 (Decorator) 模式

    思考题 有如下类设计: 如果牛奶的价钱上扬,怎么办?新增一种焦糖调料风味时,怎么办? 造成这种维护上的困难,违反了我们之前提过的哪种设计原则? P82 取出并封装变化的部分,让其他部分不收影响 多用组 ...

  5. Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式

    Hibernate 延迟加载的代理模式 和 Spring AOP的代理模式 主题 概念 Hibernate 延迟加载的代理模式 Spring AOP的代理模式 区别和联系 静态代理和动态代理 概念 代 ...

  6. Java设计模式之结构型模式

    结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 一.适配器模式: 意图: 将一个类的接口转换成客户希望的另外一个接口.Adapter 模式使得原本由于接 ...

  7. Java经典23种设计模式之结构型模式(一)

    结构型模式包含7种:适配器模式.桥接模式.组合模式.装饰模式.外观模式.享元模式.代理模式. 本文主要介绍适配器模式和桥接模式. 一.适配器模式(Adapter) 适配器模式事实上非常easy.就像手 ...

  8. Go语言实现的23种设计模式之结构型模式

    摘要:本文主要聚焦在结构型模式(Structural Pattern)上,其主要思想是将多个对象组装成较大的结构,并同时保持结构的灵活和高效,从程序的结构上解决模块之间的耦合问题. 本文分享自华为云社 ...

  9. 【设计模式】结构型04桥接模式(Bridge Pattern)

    学习地址:http://www.runoob.com/design-pattern/bridge-pattern.html 桥接模式(Bridge Pattern) 桥接模式(Bridge patte ...

  10. 设计模式(7)-结构型模式-Bridge模式

    2.结构性模式 2.2  BRIDGE模式 别名:handle/body 这个模式体现了组合相对于继承的优势. 2.2.1动机 当一个抽象可能有多个实现时,通经常使用继承来协调它们.抽象类定义对该抽象 ...

随机推荐

  1. Java:设计类的继承关系时的技巧

    继承设计的技巧: (1)将公共操作和域放置在超类 (2)不要使用受保护的域 有些程序员认为,将大多数的实例域定义为protected是一个不错的主意,只有这样,子类才能够在需要的时候直接访问他们.然而 ...

  2. python之并发编程之多进程

    一.共享数据 进程间通信应该尽量避免使用本节所讲的共享数据方式 from multiprocessing import Manager,Process,Lock def work(dic,mutex) ...

  3. Struts2第十一篇【简单UI标签、数据回显】

    Struts2UI标签 Sturts2为了简化我们的开发,也为我们提供了UI标签-也就是显示页面的标签-.. 但是呢,Struts2是服务端的框架,因此使用页面的标签是需要在服务器端解析然后再被浏览器 ...

  4. Java报文或者同步的数据有个别乱码情况的处理.

    从其它系统获取到的用户数据,1万多条数据有其中有2条数据是乱码形式,这种形式表现为最后一个字符和本身的分隔符组成了一个乱码   错误数据 :  220296|+|黄燕 鄚+|7|+|7|+|02220 ...

  5. MyEclipse中Source Folder,package,folder的区别

    1.在eclipse下,package, source folder, folder都是文件夹. 但它们有区别如: 2. package:当你在建立一个package时,它自动建立到source fo ...

  6. Docker入门之四搭建私有仓库

    前面学习了下镜像和容器,今天来学习下仓库,来搭建本地私有仓库.当然可以使用远程的共有的仓库,但在企业中有的还是放在本地,所以需要搭建私有仓库. 一.搭建仓库 可以在容器中run一个仓库镜像. dock ...

  7. Eight hdu 1043 八数码问题 双搜

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  8. JavaScript 实现发布消息后,距离当前时间的实现

    某条消息发布后,距离当前时间多久的时间显示 //显示发布时间的函数 function pastTime(_createTime) { //var createTime = _createTime.su ...

  9. SpringMVC框架(四)文件的上传下载,上下文路径

    文件目录: SpringMVC配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...

  10. 【JAVA零基础入门系列】Day6 Java字符串

    字符串,是我们最常用的类型,每个用双引号来表示的串都是一个字符串.Java中的字符串是一个预定义的类,跟C++ 一样叫String,而不是Char数组.至于什么叫做类,暂时不做过多介绍,在之后的篇章中 ...