装饰者模式
跟新中….
为什么要使用装饰者模式
Decorator Pattern
装饰者模式,动态的透明的增加一些另外的功能而不需要改变原来的代码,拥有更好的灵活性和可扩展性
从名字中也可以看出,装饰者模式就是给一个对象加上一层装饰,穿上一件衣服,装饰者在我解除的技术里面在函数式语言中的应用比较广泛,就是在装饰函数里传入一个目标函数,装饰函数同样返回一个目标函数,但是返回的函数是进过装饰了的
使用装饰者模式可以不改变原来的代码而增加一些额外的功能,讲增加的功能和原来的功能代码解耦了,代码可移植性更强了,原来代码还可以用在其他地方其他项目或则增加另外的装饰来实现更多的功能
在Java中的应用和实现
实现案例
下面先来看一个 Shape 自己实现的案例:
Shape
顶层接口:
public interface Shape {
String display();
}
Circle
和Rectangle
具体实现
public class Circle implements Shape{
private String name;
public Circle(){
this.name = "Circle";
}
public String display() {
return this.name;
}
}
------------------------------------------------
public class Rectangle implements Shape {
private String name;
public Rectangle() {
this.name = "Rectangle";
}
public String display() {
return this.name;
}
}
所有装饰器的顶层抽象对象,这里通过构造方法和set方法可以传入被装饰对象
public abstract class ShapeDecorator implements Shape {
protected Shape shape;
public ShapeDecorator(){}
public ShapeDecorator(Shape shape) {
this.shape = shape;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
//这里不做任何的装饰 由具体的装饰者实现类去实现装饰
@Override
public String display() {
return this.shape.display();
}
}
装饰者对象RedShapeDecorator
实现红色边框装饰
public class RedShapeDecorator extends ShapeDecorator {
public String desc; //描述
public RedShapeDecorator(){}
public RedShapeDecorator(Shape shape) {
super(shape);
this.desc = "RedBorder:";
}
//这里实现具体的装饰
@Override
public String display() {
return this.desc + this.shape.display();
}
}
测试
Circle circle = new Circle(); //Circle
RedShapeDecorator dector = new RedShapeDecorator(circle);
dector.display(); // RedBorder:Circle
Rectangle rectangle = new Rectangle();
dector.setShape(rectangle);
dector.display(); //RedBorder:Rectangle
总结一下
- 装饰者模式让代码更加解耦
- 装饰者模式可以很轻松的增强一个对象的功能而不改变他本来的代码,增加了代码的可复用性
- 可以清晰的知道装饰者增强了哪些功能,有利于代码阅读
实际应用
比如Java IO
中的读取和写入就是装饰者模式的典型应用:
//字节流 byte
FileInputStream fileInputStream = new FileInputStream("/home/pb/tmp/a.txt");
//字符流char 将字节流用字符流编码读取 装饰:FileInputStream
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
//缓冲流 装饰:InputStreamReader
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println(bufferedReader.readLine());
在Go中的应用和实现
实现案例
装饰者在go中实现比较简单,go中没有 类、继承(通过匿名属性实现继承)、抽象 等相关的概念,主要是通过 函数式编程 实现,将函数当做入参和返回值,这样就可以轻松实现代码装饰
类型一: 吃啥拉啥
type MyFunc func(string) error
func Logger1(target MyFunc) MyFunc {
return func(v string) error {
log.Println("【before】logger-one.....")
err := target(v)
log.Println("【after】logger-one.....")
return err
}
}
func Logger2(target MyFunc) MyFunc {
//返回装饰之后的函数
return func(v string) error {
log.Println("【before】logger-two.....")
err := target(v) //需要执行被装饰的函数
log.Println("【after】logger-two.....")
return err
}
}
func main() {
//目标函数
f := func(v string) error {
log.Println("f:", v)
return nil
}
f = Logger1(f) //一层装饰
f("a")
f = Logger2(f) //二层装饰
f("b")
}
类型二: 有参数的装饰,闭包
type MyFunc func(string) error
type MyMiddleware func(MyFunc) MyFunc
func Logger(prefix string) MyMiddleware {
return func(next MyFunc) MyFunc {
return func(v string) error {
log.Println("【before】", prefix)
err := next(v)
log.Println("【after】", prefix)
return err
}
}
}
func main() {
f := func(v string) error {
log.Println("f1:", v)
return nil
}
f = Logger("logger-one......")(f)
f("a")
f = Logger("logger-two......")(f)
f("b")
}
类型三: 装饰链
基于上面的类型三,加上一个装饰链
func MiddlewareChain(outer MyMiddleware, others ...MyMiddleware) MyMiddleware {
return func(next MyFunc) MyFunc {
for i := len(others) - 1; i >= 0; i-- { //注意顺序 剥洋葱模型
next = others[i](next) //装饰
}
return outer(next) //最外层
}
}
func main() {
f := func(v string) error {
log.Println("f1:", v)
return nil
}
f = MiddlewareChain(Logger("one"), Logger("two"), Logger("three"))(f)
//one->two->three->a
f("a")
}
实际应用
上面的应用在各大web
框架比如:gin、go-kit、buffalo 中都有广泛应用,特别是在go-kit
这个微服务工具包中,其编写代码都是按照这种模式进行分层的,达到很好的代码解耦的作用
在Python中的应用和实现
实现案例
Python中可以通过 注解 的方式实现装饰(当然和go一样传入函数也可以,Python注解本质都是传入一个目标函数返回一个被装饰之后的函数),下面实现一个 代码运行时间统计 的案例
def metric(func):
def wrapper(*args, **kwargs):
print('{} start....'.format(func.__name__))
start = time.time()
r = func(*args, **kwargs)
end = time.time()
print('{} end:{}'.format(func.__name__, end - start))
return r
return wrapper
@metric
def my_task(num):
print('son:[{}]{} {}'.format(num, os.getpid(),os.getppid()))
time.sleep(1)
print('---------end---------------')
注解本质就是将被注解的函数当做修饰器的第一个参数传入,然后将修饰器返回的函数wrapper
再赋值回目标函数,wrapper(*args,**kwargs)
是为了能修饰任何参数的函数,做一个通用的修饰器,这些参数都要传回目标函数中的,这样无论有多少参数的目标函数都可以加上这个修饰器注解进行修饰
上面不能给修饰器传入参数,如果要给修饰器传入参数则和上面go一样再包一层函数
def logger(prefix="none"):
def d1(target):
def wrapper(*args, **kwargs):
print("--------------------", prefix, "------------------")
return target(*args, **kwargs)
return wrapper
return d1
@logger("AA")
def ff():
print("hello")
if __name__ == '__main__':
ff()
需要将Python的注解和Java的注解区别开来,Python的注解是装饰器模式的一种语法糖,而Java注解就是一种标记,可以通过反射来拿到类相应的注解信息
实际应用
TODO
比较
- 和代理模式比较
- 和继承比较
TODO