结构型设计模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了结构型设计模式相关的知识,希望对你有一定的参考价值。

架构型设计模式成员

  • 门面模式
  • 代理模式
  • 装饰器模式
  • 组合模式
  • 享元模式
  • 桥接模式
  • 适配器模式

1. 代理模式

1.1 定义

为其他对象提供一种代理以控制对这个对象的访问

解决问题:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层

1.2 分类

1.2.1 静态代理

  • 一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行
  • 而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码
接口类

package com.zhunongyun.spring.proxy;

public interface Image {
   void display();

   void change(String imagePath);
}

--------------------------------------------------

接口的实现类

package com.zhunongyun.spring.proxy;

public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }

   @Override
   public void display() {
      System.out.println("显示图片: " + fileName);
   }

   @Override
   public void change(String imagePath) {
      System.out.println("替换图片: " + imagePath);
   }

   private void loadFromDisk(String fileName){
      System.out.println("加载图片: " + fileName);
   }
}

-------------------------------------------------------

代理类

package com.zhunongyun.spring.proxy;

public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){
      this.fileName = fileName;
   }

   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }

   @Override
   public void change(String imagePath) {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.change(imagePath);
   }
}

---------------------------------------------------

测试类

package com.zhunongyun.spring.proxy;

public class ProxyPatternDemo {

   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");

      // 图像将从磁盘加载
      image.display(); 
      System.out.println("---------------------");
      // 图像不需要从磁盘加载
      image.display();  
   }
}

输出结果:
加载图片: test_10mb.jpg
显示图片: test_10mb.jpg
---------------------
显示图片: test_10mb.jpg

1.2.2 动态代理

1.2.2.1 JDK 自带的动态代理

  • java.lang.reflect.Proxy: 生成动态代理类和对象
  • java.lang.reflect.InvocationHandler(处理器接口):可以通过invoke方法实现对真实角色的代理访问

每次通过 Proxy 生成的代理类对象都要指定对应的处理器对象

接口类

package com.zhunongyun.spring.proxy.dynamic;

public interface Subject {
    int sellBooks();

    String speak();
}

---------------------------------------------------------

接口实现类

package com.zhunongyun.spring.proxy.dynamic;

public class RealSubject implements Subject{
    @Override
    public int sellBooks() {
        System.out.println("卖书");
        return 1 ;
    }

    @Override
    public String speak() {
        System.out.println("说话");
        return "张三";
    }
}

------------------------------------------------------------------

动态代理类

package com.zhunongyun.study.toalibaba.spring.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 定义一个处理器
 *
 * @author gnehcgnaw
 * @date 2018/11/5 19:26
 */
public class MyInvocationHandler implements InvocationHandler {
    /**
     * 这其实业务实现类对象,用来调用具体的业务方法
     */
    private Object target;

    /**
     * 绑定业务对象并返回一个代理类
     */
    public Object bind(Object target) {
        //接收业务实现类对象参数
        this.target = target;

        //通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用
        //创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    /**
     * @param proxy  代理类
     * @param method 正在调用的方法
     * @param args   方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Object result=null;

        System.out.println("预处理操作——————");
        //调用真正的业务方法  
        result=method.invoke(target, args);

        System.out.println("调用后处理——————");
        return result;
    }
}

-------------------------------------------------------------------

测试类

package com.zhunongyun.spring.proxy.dynamic;

import java.lang.reflect.Proxy;

/**
 * 调用类
 * @author gnehcgnaw
 * @date 2018/11/7 20:26
 */
public class Client {
    public static void main(String[] args) {
        //真实对象
        Subject realSubject =  new RealSubject();

        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
        //代理对象
        Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);

        proxyClass.sellBooks();

        proxyClass.speak();
    }
}

------------------------------------------------------------------------
输出结果:

预处理操作——————
卖书
调用后处理——————
预处理操作——————
说话
调用后处理——————

1.2.2.2 CGlib动态代理

操作类

package com.zhunongyun.study.toalibaba.spring.proxy.cglib;

public class BookFacadeImpl {
    public void addBook() {  
        System.out.println("新增图书...");  
    }  
}  

-------------------------------------------

CGlib代理类

package com.zhunongyun.study.toalibaba.spring.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;

public class BookFacadeCglib implements MethodInterceptor {
    /**
     * 业务类对象,供代理方法中进行真正的业务方法调用
     */
    private Object target;

    /**
     * 相当于JDK动态代理中的绑定
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        //给业务对象赋值
        this.target = target;

        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();

        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(this.target.getClass());

        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);

        // 创建动态代理类对象并返回
        return enhancer.create();
    }

    /**
     * 实现回调方法
     * @param obj
     * @param method
     * @param args
     * @param proxy
     * @return
     * @throws Throwable
     */
    @Override
    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;
    }
}

------------------------------------------------

测试类

package com.zhunongyun.study.toalibaba.spring.proxy.cglib;

public class CglibDemo {
    public static void main(String[] args) {
        BookFacadeImpl bookFacade = new BookFacadeImpl();

        BookFacadeCglib cglib = new BookFacadeCglib();

        BookFacadeImpl bookCglib = (BookFacadeImpl) cglib.getInstance(bookFacade);

        bookCglib.addBook();
    }
}

----------------------------------------

输出结果

预处理——————
新增图书...
调用后操作——————

1.2.2.3 JDK动态代理与CGlib动态代理

  • JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法
  • CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理

以上是关于结构型设计模式的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段5——HTML元素结构

Android Java:在 onCreateView() 中返回空视图的片段

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

分享几个实用的代码片段(第二弹)

分享几个实用的代码片段(第二弹)

用于从 cloudkit 检索单列的代码模式/片段