装饰者模式与代理模式
装饰者模式
-
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。
-
优点:
- 采用组合的方式,可以动态的扩展功能,同时也可以在运行时选择不同的装饰器,来实现不同的功能。
- 被装饰者与装饰者解偶,被装饰者可以不知道装饰者的存在,同时新增功能时原有代码也无需改变,符合开放封闭原则。
- 有效避免了使用继承的方式扩展对象功能而带来的灵活性差,子类无限制扩张的问题。
-
缺点:
- 装饰层过多的话,维护起来比较困难。
- 如果要修改抽象组件这个基类的话,后面的一些子类可能也需跟着修改,较容易出错。
-
角色说明
- ITarget: 接口或者抽象类,被装饰的最原始的对象。具体组件与抽象装饰角色的父类。
- TargetImpl: 实现抽象组件的接口。
- Decorator: 一般是抽象类,抽象组件的子类,同时持有一个被装饰者的引用,用来调用被装饰者的方法;同时可以给被装饰者增加新的职责。
- DecoratorSub: (具体装饰类):抽象装饰角色的具体实现。
-
类图如下:
-
代码实现如下:
interface ITarget { fun operation(): String } class TargetImpl : ITarget { override fun operation(): String { return "我是歌手" } } abstract class Decorator(private val target: ITarget) : ITarget { override fun operation(): String { return target.operation() } } class DecoratorSub(target: ITarget) : Decorator(target) { override fun operation(): String { return addSome(super.operation()) } private fun addSome(origin: String): String { return "$origin,但是我也学会了表演" } } fun main(args: Array<String>) { val target = TargetImpl() val decorator = DecoratorSub(target) decorator.operation() }
静态代理
-
静态代理与装饰者模式很像,但是目的不一样,代理的目的是控制对对象的访问,不会对对象的内容做修改
而装饰者模式的目的是增强对象的功能。 -
类图如下:
-
代码实现如下:
interface ITarget { fun operation(cost: Int) } class TargetImpl(val name: String) : ITarget { override fun operation(cost: Int) { println("$name 参加xx活动演唱,出场费:$cost 元") } } class Proxy(private val targetImpl: TargetImpl) : ITarget { override fun operation(cost: Int) { if (preOption(cost)) { targetImpl.operation(cost) } else { println("老子不差你这点钱") } } private fun preOption(cost: Int): Boolean { return cost > 100000 } } fun main(args: Array<String>) { val aa = TargetImpl("S") val agent = Proxy(aa) agent.operation(100) agent.operation(10000000) }
-
装饰者模式和静态代理模式在实现上很像,但是两者的目的不同,所以在代理类和装饰者类中的实现上有着不同的处理。
动态代理
简介
- 不需要显式实现与目标对象类相同的接口,而是将这种实现推迟到程序运行时由 JVM来实现
- 通过Java 反射机制的method.invoke(),通过调用动态代理类对象方法,从而自动调用目标对象的方法
- 优点:
- 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码,更强的灵活性
- 在使用时(调用目标对象方法时)才会动态创建动态代理类 & 实例,不需要事先实例化。
- 缺点:
- 效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法
- 应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类 创建代理类
- 应用领域
- 基于静态代理应用场景下,需要代理对象数量较多的情况下使用动态代理
- AOP 领域,Aspect Oriented Programming 面向切面编程,是OOP的延续、函数式编程的一种衍生范型,
作用:通过预编译方式和运行期动态代理实现程序功能的统一维护。优点:降低业务逻辑各部分之间的耦合度 、 提高程序的可重用性 & 提高了开发的效率
具体应用场景:日志记录、性能统计、安全控制、异常处理等。
动态代理实现
- 使用java中的InvocationHandler实现
- 类图如下:
- 创建动态代理类实现InvocationHandler接口
public class DynamicProxy<T extends Person> implements InvocationHandler { // 声明代理对象 private T proxyObject; public T newProxyInstance(T proxyObject) { this.proxyObject = proxyObject; return (T) Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject .getClass().getInterfaces(), this); } /** * 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke * * @param proxy 动态代理对象(即哪个动态代理对象调用了method() * @param method 目标对象被调用的方法 * @param args 指定被调用方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { preOperation(); Object result = method.invoke(proxyObject, args); postOperation(); return result; } private void preOperation() { System.out.println("开始调用"); } private void postOperation() { System.out.println("调用结束"); } }
- 创建需要代理的类
public interface Person { void say(); } public class Asian implements Person { @Override public void say() { System.out.println("黄种人"); } }
- 使用
public class Main { public static void main(String[] args) { Asian person = new Asian(); DynamicProxy dynamicProxy = new DynamicProxy(); dynamicProxy.newProxyInstance(person).say(); } }
- 类图如下: