概述
当我们需要向一个既有的类添加一些行为,但无法对该类进行修改时,装饰器模式,又称包装器(Wrapper),将会派上用场。总而言之,装饰器模式就是在不改变类的前提下,给类之前的行为添加新的功能。
装饰器模式结合了继承和组合。它需要具体的类实现了一个行为接口,而这个具体的类(ConcreteClass)和被实现的接口(Interface)将被我们用来实现装饰类(Decorator)。如下图所示
目的
装饰器模式,将行为添加到单独的对象上,而不是对象所属的整个类上,这样可以动态的对一个既有类的行为进行更改。通常装饰器模式仅限于对类进行非常小的调整,因为整个模块依然依赖改组成类的基础行为。
静态代理模式与装饰器模式
对装饰器模式来说,装饰者(decorator)和被装饰者(decoratee)都实现同一个 接口。对代理模式来说,代理类(proxy class)和真实处理的类(real class)都实现同一个接口。他们之间的边界确实比较模糊,两者都是对类的方法进行扩展,具体区别如下:
1、装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。
2、装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;
3、装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;
函数式装饰器模式
假设我们要实现一个计算器,这个计算器有四个基本操作:add、subtract、multiply和divide。现在我们要在这个计算器上实现打印日志的功能,它将我们的计算结果显示在控制台上。
下面是实现好的计算器类代码:
package 装饰器模式
trait Calculator {
def add(a: Int, b: Int): Int
def subtract(a: Int, b: Int): Int
def multiply(a: Int, b: Int): Int
def divide(a: Int, b: Int): Int
}
class ConcreteCalculator extends Calculator {
override def add(a: Int, b: Int): Int = a + b
override def subtract(a: Int, b: Int): Int = a - b
override def multiply(a: Int, b: Int): Int = a * b
override def divide(a: Int, b: Int): Int = a / b
}
在函数的世界里,一种简单的代替方案就是创建一个高阶函数,该函数以既有的行为函数为参数,返回一个新的经过包装的函数:
object Examples extends App {
val calc = new ConcreteCalculator()
val loggingAdd = makeLogger(calc.add)
val loggingSubtract = makeLogger(calc.subtract)
val loggingMultiply = makeLogger(calc.multiply)
val loggingDivide = makeLogger(calc.divide)
loggingAdd(1,2)
loggingSubtract(1,2)
loggingMultiply(1,2)
loggingDivide(1,2)
/**
* 定义装饰器函数,传入原来的行为,添加打印功能并返回新行为
* @param calc
* @return
*/
def makeLogger(calc: (Int, Int) => Int) = (a: Int, b: Int) => {
val result = calc(a, b)
println(result)
result
}
}
总结
类的行为对应为一个函数,那么,对行为的操作可以转化为对函数的操作。