8. 装饰模式


顾名思义, 装饰模式就是给一个对象增加装饰一些新的功能, 而且是动态的, 要求装饰对象和被装饰对象实现同一个接口, 装饰对象持有被装饰对象的实例

Source类是被装饰类, Decorator类是一个装饰类, 可以为Source类动态的增加一些功能, 代码如下:

public interface Sourceable {
 public void method();
}
public class Source implements Sourceable {
 @Override
 public void method() {
   System.out.println("the original method!")
}
}

public class Decorator implements sourceable {
 private Sourceable source;
 private Decorator(Sourceable source) {
   super();
   this.source = source;
}
 @Override
 public void method() {
   System.out.println("before decorator!");
   source.method();
   System.out.println("after decorator!");
}
}

测试类:

before decorator!
the original method!
after decorator!

装饰器模式的应用场景:

  1. 需要扩展一个类的功能
  2. 动态的为一个对象增加功能, 而且还能动态的撤销. (继承不能做到这一点, 继承的功能是静态的, 不能动态增删)

缺点:

产生过多的相似对象, 不易拍错!

反思: 装饰器的本质是什么?

装饰器本身的一个本质, 就是不想改变原来的函数, 但是还想给该函数增加或者删除一些功能.就是装饰器, 和python的也很类似, 装饰器就是适配器思想的一种实现

装饰模式是针对接口的

9. 代理模式(面试)

其实每个模式名称就表明了改模式的作用, 代理模式就是多一个代理类出来, 替原对象进行一些操作, 代理又分为动态代理和静态代理

静态代理

比如我们在租房子的时候去找中介, 为什么呢?因为你对该地区房屋信息掌握的不够全面, 希望找一个更熟悉的人去帮你做, 此处的代理就是这个意思. 代码如下:

public interface Sourceable {
 public void method();
}
public class Source implements Sourceable {
 @Override
 public void method() {
   System.out.println("the original method!");
}
}
public class Proxy implements Sourceable {
 private Source source;
 public Proxy() {
   super();
   this.source = new Source;
}
 @Override
 public void method() {
   before();
   source.method();
   after();;
}
 
 private void before() {
   system.out.println("before proxy!");
}
 
 private void after() {
   system.out.println("after proxy!");
}
}

测试类:

public class ProxyTest {
 public static void main(String[] args) {
   Sourceable source = new Proxy();
   source.method();
}
}

输出:

before proxy!
the original method!
after proxy!

代理模式的应用场景:

如果已有的方法在使用的时候需要对原有的方法进行改进, 此时有两种方法:

  1. 修改原来的方法来适应. 这样违反了"对扩展开放, 对修改关闭"的原则.
  2. 就是采用一个代理类调用原有的方法, 且对生产的结果进行控制, 这种方法就是代理模式

使用代理模式, 可以将功能划分的更加清晰, 有助于后期维护.

装饰和模式和静态代理模式的区别:

静态代理, 更加关注的类, 传入的是这个对象的具体实例

而装饰模式传入的是一个接口, 在调用接口实现的时候, 会调用接口实现里面的方法, 是动态的.

动态代理

JDK动态代理

package com.lnjecit.proxy;

/**
* Subject
* 抽象主题接口
* @author
* @create 2018-03-29 14:16
**/
public interface Subject {

   void doSomething();
}
/**
* RealSubject
* 真实主题类
* @author
* @create 2018-03-29 14:21
**/
public class RealSubject implements Subject {
   @Override
   public void doSomething() {
       System.out.println("RealSubject do something");
  }
}

基于接口去实现的动态代理

package com.lnjecit.proxy.dynamic.jdk;

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

/**
* JDKDynamicProxy
* jdkd动态代理
*
* @author
* @create 2018-03-29 16:17
**/
public class JDKDynamicProxy implements InvocationHandler {

   private Object target;

   public JDKDynamicProxy(Object target) {
       this.target = target;
  }

   /**
    * 获取被代理接口实例对象
    * @param <T>
    * @return
    */
   public <T> T getProxy() {
       return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
  }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("Do something before");
       Object result = method.invoke(target, args);
       System.out.println("Do something after");
       return result;
  }
}

测试类:

package com.lnjecit.proxy;

import com.lnjecit.proxy.dynamic.jdk.JDKDynamicProxy;

/**
* Client
* client测试代码
*
* @author
* @create 2018-03-29 14:26
**/
public class Client {
   public static void main(String[] args) {
       // 保存生成的代理类的字节码文件
       System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

       // jdk动态代理测试
       Subject subject = new JDKDynamicProxy(new RealSubject()).getProxy();
       subject.doSomething();
  }
}

输出结果:

Do something before
RealSubject do something
Do something after

CGLib动态搭理

实现一个业务类,注意,这个业务类并没有实现任何接口:

package com.jpeony.spring.proxy.cglib;

public class HelloService {

   public HelloService() {
       System.out.println("HelloService构造");
  }

   /**
    * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
    */
   final public String sayOthers(String name) {
       System.out.println("HelloService:sayOthers>>"+name);
       return null;
  }

   public void sayHello() {
       System.out.println("HelloService:sayHello");
  }
}

自定义MethodInterceptor:

package com.jpeony.spring.proxy.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
* 自定义MethodInterceptor
*/
public class MyMethodInterceptor implements MethodInterceptor{

   /**
    * sub:cglib生成的代理对象
    * method:被代理对象方法
    * objects:方法入参
    * methodProxy: 代理方法
    */
   @Override
   public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       System.out.println("======插入前置通知======");
       Object object = methodProxy.invokeSuper(sub, objects);
       System.out.println("======插入后者通知======");
       return object;
  }
}

生成CGLIB代理对象调用目标方法:

package com.jpeony.spring.proxy.cglib;

import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;

public class Client {
   public static void main(String[] args) {
       // 代理类class文件存入本地磁盘方便我们反编译查看源码
       System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
       // 通过CGLIB动态代理获取代理对象的过程
       Enhancer enhancer = new Enhancer();
       // 设置enhancer对象的父类
       enhancer.setSuperclass(HelloService.class);
       // 设置enhancer的回调对象
       enhancer.setCallback(new MyMethodInterceptor());
       // 创建代理对象
       HelloService proxy= (HelloService)enhancer.create();
       // 通过代理对象调用目标方法
       proxy.sayHello();
  }
}

输出:

HelloService构造
======插入前置通知======
HelloService:sayHell
======插入后者通知======

动态代理的本质:

都不是通过自己时间, 都是通过中间的代理对象, 代理对象调用父类的方法, 还可以在前面和后面增加一些功能

行为型设计模式


模板方法模式

策略模式

其他设计模式


MVC设计模式

委托设计模式