代码组织的方式可以分为横向和纵向,横向指的是抽象与具体的关系(接口或抽象类与实际类的关系),这种方式能够让代码依赖于抽象的东西,更加灵活地扩展,体现了对于增加开放,对于修改关闭的程序设计原则。对于稍微复杂的功能模块,我们需要考虑的不仅仅是横向的组织方式,在纵向(深度)上也需要考虑如何来组织依赖关系,总体的原则是高层依赖低层。更具体来说,注入基于抽象的类的引用到高层类。这里我们通过控制功能拆分粒度来确保灵活性。
装饰者模式是一种非常常见的在代码深度层次上组织代码的方式。(个人认为无所谓装饰者模式,这种做法很常见。),那么这种模式是如何演化的呢?
1.最基本的需求场景
一个人发邮件的方法通常是这样的:
class Person{ public void SendEmail(){ System.out.println("send"); } }逻辑很简单,就是发送,我们可以这样来调用:
Person p = new Person(); p.SendEmail();2.扩展需求场景
在发送邮件之前我们需要校验一些内容(比如是否有非法内容),发送之后我们需要写一个日志记录用户的行为,我们可以用一个类继承Person,然后重写sendEmail方法。
class SuperPerson extends Person{ public void SendEmail(){ System.out.println("before send"); super.SendEmail(); System.out.println("after send"); } }我们在调用父类的sendEmail方法前,做一些事情,在调用后再做一些事情。
我们调用的时候就是这样:
Person p1 = new SuperPerson(); p1.SendEmail();父类引用指向子类的对象,灵活性更好一些,任何继承自Person的子类都可以被指向;
3.延伸的需求场景
我们用继承自Person的类来做具体的事情,在实际情况下,顶级的Person通常是一个抽象类(或者接口),更多的时候是起到一个定义规范的作用,而实际的功能在子类中,这时候我们使用Person类来做事情就不合理了,需要使用Person的子类,这时候,我们需要一个容器-IOC容器,于是我们很自然地想到,我们可以使用IOC机制来注入实现了Person的子类。
class SuperPerson2 extends Person{ private Person person; public SuperPerson2(Person p){ this.person = p; } public void SendEmail(){ System.out.println("before send"); person.SendEmail(); System.out.println("after send"); } }我们可以这样来使用:
Person p = new SuperPerson(); Person p2 = new SuperPerson2(p); p2.SendEmail();输出结果:
before send2 before send1 send after send1 after send2这里我们可以看到,其实这里包装了三层,person,superPerson,SuperPerson2,一层一层的关系很明显,并且中间任何一层都可以替换,因为是依赖于抽象的Person,这样从各个方向上都实现了较好的灵活性。
在Java中,I/O模块就是基于装饰者模式来设计的:
public class IOTest { public static void main(String[] args) throws IOException { // 流式读取文件 DataInputStream dis = null; try{ dis = new DataInputStream( new BufferedInputStream( new FileInputStream("test.txt") ) ); //读取文件内容 byte[] bs = new byte[dis.available()]; dis.read(bs); String content = new String(bs); System.out.println(content); }finally{ dis.close(); } } }从这个Test中很明显可以窥见,所以装饰者模式在于设计灵活架构方面还是比较重要的,模式的学习不是一朝一夕,结合实际的经验,灵活运用才是真正的高手!