设计模式-问题-门面模式+装饰者模式+代理模式

代理模式

为什么有静态代理?他解决什么问题?为什么又要使用动态代理,说说具体使用场景体会他的好处。

  • 代理主要还是想在类不被修改代码的情况下做一些增强。

  • 静态代理运行在编译期,也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了

  • 静态代理主要有几个缺点

    • 一个接口只能服务于一种类型的类,如果要代理的方法很多,势必要为每一种方法都进行代理,在程序规模稍大时静态代理代理类就会过多会造成代码混乱

    • 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。 基于上述两个问题,动态代理诞生了

明白了动态代理使用场景,再说说具体动态代理怎么实现的?有哪些实现方式?

  • 动态代理是在程序运行时,通过反射获取被代理类的字节码内容用来创建代理类

  • 一种是通过JDK的动态代理(基于实现接口)

    • $Proxy0

    • 类似于装饰器模式

  • 一种是通过CGLIB的动态代理(基于继承)

    • Enhance

    • 需要注意final的方法和类没法被CGLIB代理

JDK动态代理跟CGLIB动态代理的区别。

  • JDK动态代理是基于实现接口的的

  • CGLIb是基于继承的

    • final的方法和类无法被代理

由课中动态数据源的例子有没有更深刻体会到动态代理的好处?

  • 动态代理能够帮助我们动态的给类增强一些功能,而不需要修改类,这是一系列横切的操作。

  • 能够服务于各种类型的类

门面模式

想一想什么场景下考虑使用门面模式

  • 一个子系统比较复杂时,比如算法或者业务比较复杂,就可以封装出一个或多个门面出来,项目的结构简单,而且扩展性非常好。

  • 对于一个较大项目,为了避免人员带来的风险,也可以使用门面模式,技术水平比较差的成员,尽量安排独立的模块,然后把他写的程序封装到一个门面里,尽量让其他项目成员不用看到这些人的代码,看也看不懂,我也遇到过一个“高人”写的代 码,private方法、构造函数、常量基本都不用,你要一个public方法, 好,一个类里就一个public方法,所有代码都在里面,然后你就看吧, 一大坨程序,看着就能把人逼疯。使用门面模式后,对门面进行单元测试,约束项目成员的代码质量,对项目整体质量的提升也是一个比较好 的帮助。

装饰者模式

仔细对比一下装饰者模式和代理模式结构图,想一想你怎么区分它们,从这里给你什么启发。

  • 装饰器主要强调的是自己,扩展的是自己的能力,并且是对继承的扩展

  • 代理模式则是为了实现对象的控制,提供一个代理对象出来,通过代理对象来控制原有对象的引用

  • 装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但是并不象本身的功能进行增强;

分析一下java IO代码new BufferedReader(new FileReader("")) 为什么可以层层包装,画出类图或从代码层面描述怎么实现的

image-20210104165447074

可以看到在BufferedReader构造方法中可以传入Reader那么在调用的时候就会给其包装一层

private void fill() throws IOException {
  //这一段都是包装一层走的Buffer
  int dst;
  if (markedChar <= UNMARKED) {
    /* No mark */
    dst = 0;
  } else {
    /* Marked */
    int delta = nextChar - markedChar;
    if (delta >= readAheadLimit) {
      /* Gone past read-ahead limit: Invalidate mark */
      markedChar = INVALIDATED;
      readAheadLimit = 0;
      dst = 0;
    } else {
      if (readAheadLimit <= cb.length) {
        /* Shuffle in the current buffer */
        System.arraycopy(cb, markedChar, cb, 0, delta);
        markedChar = 0;
        dst = delta;
      } else {
        /* Reallocate buffer to accommodate read-ahead limit */
        char ncb[] = new char[readAheadLimit];
        System.arraycopy(cb, markedChar, ncb, 0, delta);
        cb = ncb;
        markedChar = 0;
        dst = delta;
      }
      nextChar = nChars = delta;
    }
  }

  int n;
  do {
    //FileReader#read
    n = in.read(cb, dst, cb.length - dst);
  } while (n == 0);
  if (n > 0) {
    nChars = dst + n;
    nextChar = dst;
  }
}

Last updated