设计模式(6)-避免浪费与类表现
Flyweight
简介
Flyweight的含义是轻量级,该设计模式的作用是让对象变轻。具体来说,当程序中需要大量对象的时候,如果都使用new关键字来分配内存,将会消耗大量的内存空间,而Flyweight模式的核心思想就是“通过尽量共享实例来避免new出实例”。通常我们在使用的时候,会将共享实例使用一个工厂来进行管理,每次需要相应示例的时候,都从工厂中获得。工厂会将这些实例管理在一个pool中,当有请求过来的时候,如果pool中已经存在了对应的实例,则将其返回共享出去,而不用重新创建;如果没有,则新创建一个返回之后,将其管理到pool中,以便后续的共享。但既然是进行了共享,那么就会出现一处修改多处改变的情况,这种时候就需要我们认真考虑对象是否能够进行共享,需要结合具体的场景。
Proxy
简介
Proxy表示代理,也就是代理模式。在代理模式下,真实的功能实现对象会被增加一层代理。具体来说,代理对象会持有一个功能对象,然后向外暴露接口。我们在使用功能的时候,需要使用的是代理对象,而代理对象对应方法的核心功能,则又是由被代理的功能对象来实现的。在代理模式中有如下角色:
Subject
:该角色定义了功能角色和代理角色需要实现的接口,该接口使得Proxy角色与RealSubject角色具有一致性。在使用的时候,我们不需要在意使用的是哪种角色,因为对于功能的提供来说,两者都是能够完成的Proxy
:该角色为代理角色。它会持有一个RealSubject
对象,同时会实现Subject接口RealSubject
:该角色为实际的功能提供角色
示例程序
在示例程序中,我们会模拟一个打印过程。我们通过一个代理对象PrinterProxy
来进行功能调用,该对象实际上调用了真实的Printer
对象来实现相关功能。
首先,我们可以声明一个接口表示打印所需的相关功能,这个接口应该同时由代理对象与被代理对象实现。接口Printable
内容如下:
1 |
|
之后,可以完成核心的功能实现对象Printer
,它完成的是打印过程的核心功能,同时需要实现上面的接口。这里为了模拟一个复杂耗时的创建操作,在程序中模拟了5s的睡眠。
1 |
|
完成实际的功能对象之后,就可以完成对应的代理对象PrinterProxy
。该对象会持有一个Printer对象,但是并不会完成初始化。只有当真正需要使用到它的时候才会去完成初始化。
1 |
|
至此就完成了所有的示例代码,我们可以进行如下测试。在测试代码中,我们先给代理对象命名为Alice,但是此时它持有的Printer对象并没有初始化。之后我们改名为Bob,再调用print的时候,此时才进行Printer对象的初始化。
1 |
|
说明
在示例程序中,我们发现代理对象可以使得实际功能对象的创建延后,在必要的时候才进行创建,可以很好地改善用户体验。当然也可以通过代理模式实现AOP的效果,具体来说就是在代理对象方法中,调用功能对象方法的前后增加相关的逻辑,例如日志的打印和记录等。
在代理模式中,代理对象需要持有一个被代理对象,但是反过来,被代理对象是不知道代理对象的,它并不知道代理对象的存在。
Command
简介
当我们调用一个对象的方法完成相关工作的时候,我们可以从结果的变化中发现工作已经完成,但是并没有办法留下工作的历史记录。而Command就希望解决这种问题,它使用命令对象来表示进行工作这件事,一次工作就对应一个实例。之后再进行相关工作的时候,就不是调用对应对象的方法,而是调用命令对象的execute方法。这样的好处在于我们可以进行工作历史记录的管理,实际上就是管理一个命令对象的集合,同时我们还可以随时重新执行这些命令。而关于命令中应该包含哪些信息,实际上没有绝对的答案,命令的目的不同,应该包含的信息也不同,并且根据保存信息的不同,我们能够重现的粒度也不同。