面向对象的设计模式(二)——Decorator模式

来源:互联网 发布:centos6.5 yum安装 编辑:程序博客网 时间:2024/06/06 07:34

(二)“单一职责”模式

在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键就是划清责任。

1.  Decorator 装饰模式

◆  动机:
        在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
        如何使“对象功能的扩展”能够根据需要来动态实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

首先,用代码举一个例子,这是一个操作数据流的类:
//业务操作abstract class Stream {    public abstract char read(int number);    public abstract void seek(int position);    public abstract void write(char data);}//主体类class FileStream extends Stream {    //读文件流    @Override    public  char read(int number){        //***********    }    //定位文件流    @Override    public  void seek(int position){        //***********    }    //写文件流    @Override    public  void write(char data){        //***********    }}class NetworkStream extends Stream {    //读文件流    @Override    public  char read(int number){        //###########    }    //定位文件流    @Override    public  void seek(int position){        //###########    }    //写文件流    @Override    public  void write(char data){        //###########    }}class MemoryStream extends Stream {    //读文件流    @Override    public  char read(int number){        //@@@@@@@@@@@    }    //定位文件流    @Override    public  void seek(int position){        //@@@@@@@@@@@    }    //写文件流    @Override    public  void write(char data){        //@@@@@@@@@@@    }}//扩展操作class CryptoFileStream extends FileStream {    @Override    public char read(int number) {        //额外的加密操作...        //读文件流        super.read(number);    }    @Override    public void seek(int position) {        //额外的加密操作...        //定位文件流        super.seek(position);        //额外的加密操作...    }    //写内存流    @Override    public void write(char data) {        //额外的加密操作...        //写文件流        super.write(data);        //额外的加密操作...    }}class CryptoNetworkStream extends NetworkStream {    @Override    public char read(int number) {        //额外的加密操作...        //读网络流        super.read(number);    }    @Override    public void seek(int position) {        //额外的加密操作...        //定位网络流        super.seek(position);        //额外的加密操作...    }    @Override    public void write(char data) {        //额外的加密操作...        //写网络流        super.write(data);        //额外的加密操作...    }}class CryptoMemoryStream extends MemoryStream {    @Override    public char read(int number) {        //额外的加密操作...        //读内存流        super.read(number);    }    @Override    public void seek(int position) {        //额外的加密操作...        //定位内存流        super.seek(position);        //额外的加密操作...    }    //写内存流    @Override    public void write(char data) {        //额外的加密操作...        //写内存流        super.write(data);        //额外的加密操作...    }}class BufferedFileStream extends FileStream {}class BufferedNetworkStream extends NetworkStream {}class BufferedMemoryStream extends MemoryStream {}class CryptoBufferedFileStream extends FileStream {    @Override    public char read(int number) {        //额外的加密操作...        //额外的缓冲操作...        //读文件流        super.read(number);        //额外的加密操作...        //额外的缓冲操作...    }    @Override    public void seek(int position) {        //额外的加密操作...        //额外的缓冲操作...        //定位文件流        super.seek(position);        //额外的加密操作...        //额外的缓冲操作...    }    @Override    public void write(char data) {        //额外的加密操作...        //额外的缓冲操作...        //写文件流        super.write(data);                //额外的加密操作...        //额外的缓冲操作...    }}class Client {    public static void main(String args[]) {        //编译时装配        CryptoFileStream fs1 = new CryptoFileStream();        BufferedFileStream fs2 = new BufferedFileStream();        CryptoBufferedFileStream fs3 = new CryptoBufferedFileStream();    }}
代码的结构逻辑如下图,这样的数据机构会生成很多很多子类。注意,crypto、Bufferde这些方法是要在FileStream、NetwodStream等对象的基础上才能实现的。FileStream这些类叫做“主体类”,CryptoFileStream这些类叫做“扩展类”,

利用Decorator模式进行优化,优化后代码如下:
//业务操作abstract class Stream {    public abstract char read(int number);    public abstract void seek(int position);    public abstract void write(char data);}//主体类class FileStream extends Stream {    //读文件流    @Override    public  char read(int number){        //***********    }    //定位文件流    @Override    public  void seek(int position){        //***********    }    //写文件流    @Override    public  void write(char data){        //***********    }}class NetworkStream extends Stream {    //读文件流    @Override    public  char read(int number){        //###########    }    //定位文件流    @Override    public  void seek(int position){        //###########    }    //写文件流    @Override    public  void write(char data){        //###########    }}class MemoryStream extends Stream {    //读文件流    @Override    public  char read(int number){        //@@@@@@@@@@@    }    //定位文件流    @Override    public  void seek(int position){        //@@@@@@@@@@@    }    //写文件流    @Override    public  void write(char data){        //@@@@@@@@@@@    }}//继承:接口协议class CryptoStream extends Stream  {    /**     *  组合:复用实现     */    Stream s;// s=new FileStream(); s=new NetworkStream(); s=new MemoryStream();             // 在子类中以父类的实例作为一个成员变量,这是典型的decrorator模式    /**     * 通过构造函数传递一个具体的类型进来     */    public CryptoStream(Stream s)    {        this.s=s;    }    @Override    public char read(int number) {        //额外的加密操作...        //读文件流        s.read(number);    }    @Override    public void seek(int position) {        //额外的加密操作...        //定位文件流        s.seek(position);        //额外的加密操作...    }    //写内存流    @Override    public void write(char data) {        //额外的加密操作...        //写文件流        s.write(data);        //额外的加密操作...    }}class BufferedStream extends Stream {    Stream s;    public BufferedStream(Stream s)    {        this.s=s;    }}class Client {    public static void main(String args[]) {        //运行时装配        Stream s1=new CryptoStream( new FileStream());        Stream fs2 = new BufferedStream(new FileStream());        Stream fs3 = new CryptoStream(new BufferedStream(new FileStream()));    }}
这样,Decorator模式基本上已经算完成了,不过我们还可以再进行一个小优化:
//业务操作abstract class Stream {    public abstract char read(int number);    public abstract void seek(int position);    public abstract void write(char data);}//主体类class FileStream extends Stream {    //读文件流    @Override    public  char read(int number){        //***********    }    //定位文件流    @Override    public  void seek(int position){        //***********    }    //写文件流    @Override    public  void write(char data){        //***********    }}class NetworkStream extends Stream {    //读文件流    @Override    public  char read(int number){        //###########    }    //定位文件流    @Override    public  void seek(int position){        //###########    }    //写文件流    @Override    public  void write(char data){        //###########    }}class MemoryStream extends Stream {    //读文件流    @Override    public  char read(int number){        //@@@@@@@@@@@    }    //定位文件流    @Override    public  void seek(int position){        //@@@@@@@@@@@    }    //写文件流    @Override    public  void write(char data){        //@@@@@@@@@@@    }}/** * 一个基类,用于提取CryptoStream和BufferedStream共同的成员变量s * 因为这个类没有继承父类所有属性,所以是抽象类 * 根据重构的原则,继承自同一父类的几个子类含有相同的字段或变量时,应该把它往上提, * 而直接放在Stream类里面的话,会影响FileStream、MemoryStream等类的使用,所以我们就要一个基类实现重构 *///继承:接口协议abstract  DecroratorStream extends Stream{    //组合:复用实现    protected Stream s;// s=new FileStream(); s=new NetworkStream(); s=new MemoryStream();    protected DecroratorStream(Stream s){        this.s=s;    }}class CryptoStream extends DecroratorStream  {    public CryptoStream(Stream s)    {        //改为继承父类的方法        super(s);    }    @Override    public char read(int number) {        //额外的加密操作...        //读文件流        s.read(number);    }    @Override    public void seek(int position) {        //额外的加密操作...        //定位文件流        s.seek(position);        //额外的加密操作...    }    //写内存流    @Override    public void write(char data) {        //额外的加密操作...        //写文件流        s.write(data);        //额外的加密操作...    }}class BufferedStream extends DecroratorStream {    Stream s;    public BufferedStream(Stream s)    {        super(s);    }    //....}class Client {    public static void main(String args[]) {        //运行时装配        FileStream fs=new FileStream();        Stream s1=new CryptoStream( new FileStream());        Stream s2 = new BufferedStream(new FileStream());        Stream s3 = new CryptoStream(new BufferedStream(new FileStream()));    }}
现在逻辑结构如下图,可以看到,我们的代码结构简洁了很多!

◆  模式定义:
        动态地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活(消除重复代码&减少子类个数)。
◆  要点总结:
        ●  通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”;
        ●  Decorator类在接口上表现为 is-a Component的继承关系,即Decorator类继承了Component类所有的接口。但在实现上又表现为 has-a Component的组合关系,即Decorator类又使用了另一个Component类。这就是Decorator模式的典型特征;(见下图)

        ●  Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主题类在多个方向上的扩展功能” —— 是为“装饰”的含义;









0 0