zoukankan      html  css  js  c++  java
  • 设计模式(十二)Decorator模式

      Decorator模式就是不断地为对象添加装饰的设计模式。以蛋糕为例,程序中的对象就相当于蛋糕,然后像不断地装饰蛋糕一样地不断地对其增加功能,它就变成了使用目的更加明确的对象。

      首先看示例程序的类图。

      然后看示例程序代码。

     1 package bigjunoba.bjtu.decorator;
     2 
     3 public abstract class Display {
     4     
     5     public abstract int getColumns();
     6     public abstract int getRows();
     7     public abstract String getRowText(int row);
     8     public final void show() {
     9         for (int i = 0; i < getRows(); i++) {
    10             System.out.println(getRowText(i));
    11         }
    12     }
    13 }

      Display类是可以显示多行字符串的抽象类。getColumns方法用来获取横向行数,getRows方法用来获取纵向行数,getRowText方法用于获取指定的某一行的字符串,show方法是用来显示所有行字符串,首先获得行数,然后循环打印每一行的字符串。

     1 package bigjunoba.bjtu.decorator;
     2 
     3 public class StringDisplay extends Display{
     4     
     5     private String string;
     6     
     7     public StringDisplay(String string) {
     8         this.string = string;
     9     }
    10 
    11     @Override
    12     public int getColumns() {
    13         return string.getBytes().length;
    14     }
    15 
    16     @Override
    17     public int getRows() {
    18         return 1;
    19     }
    20 
    21     @Override
    22     public String getRowText(int row) {
    23         if (row == 0) {
    24             return string;
    25         } else {
    26             return null;
    27         }
    28     }
    29     
    30 }

      StringDisplay类用于显示单行字符串。string字段中保存的是要显示的字符串,由于StringDisplay类显示的是单行字符串,因此getColumns方法就是返回字符串的长度,而getRows方法返回的行数就是1,由于返回的是单行字符串,所以getRowText方法只有在传入的参数是0时才会返回字符串。StringDisplay类就相当于生日蛋糕中的核心蛋糕。

    1 package bigjunoba.bjtu.decorator;
    2 
    3 public abstract class Border extends Display {
    4 
    5     protected Display display;
    6     protected Border(Display display) {
    7         this.display = display;
    8     }
    9 }

      Border类继承了Display类,但它是装饰边框的抽象类。这里有一些疑惑。装饰边框与装饰物具有了相同的方法,因此也就具有了一致性。

      还需要注意的是,Border类中的display字段,表示的是被装饰物,也就是说,只要是display类的子类,都可以传递进来保存在display字段中。更有趣的是,当然可以把Border类的子类传递进来,这样Border类的子类表示的装饰边框类中又有一个display字段,又可以传递进去一个边框或装饰物,反复循环。可以理解为实现了不断增加新的装饰物。

     1 package bigjunoba.bjtu.decorator;
     2 
     3 public class SideBorder extends Border {
     4     
     5     private char borderChar;
     6 
     7     protected SideBorder(Display display, char ch) {
     8         super(display);
     9         this.borderChar = ch;
    10         // TODO Auto-generated constructor stub
    11     }
    12 
    13     @Override
    14     public int getColumns() {
    15         return 1 + display.getColumns();
    16     }
    17 
    18     @Override
    19     public int getRows() {
    20         return display.getRows();
    21     }
    22 
    23     @Override
    24     public String getRowText(int row) {
    25         return borderChar + display.getRowText(row) + borderChar;
    26     }
    27 
    28 }

      SideBorder类可以用指定的字符来装饰字符串的左右两侧。borderChar字段用来保存指定的字符。首先通过调用父类的构造器指定display和ch。然后通过调用被装饰物display的相关方法来实现这一装饰目的。

     1 package bigjunoba.bjtu.decorator;
     2 
     3 public class FullBorder extends Border{
     4 
     5     protected FullBorder(Display display) {
     6         super(display);
     7     }
     8 
     9     @Override
    10     public int getColumns() {
    11         return 1 + display.getColumns() + 1;
    12     }
    13 
    14     @Override
    15     public int getRows() {
    16         return 1 + display.getRows() + 1;
    17     }
    18 
    19     @Override
    20     public String getRowText(int row) {
    21         if (row ==0) {
    22             return "[" + makeLine('@', display.getColumns()) + "]";
    23         } else if (row == display.getRows() + 1) {
    24             return "[" + makeLine('@', display.getColumns()) + "]"; 
    25         } else {
    26             return "|" + display.getRowText(row - 1)  + "|";
    27         }
    28 
    29     }
    30     
    31     private String makeLine(char ch, int count) {
    32         StringBuffer stringBuffer = new StringBuffer();
    33         for (int i = 0; i < count + 1; i++) {
    34             stringBuffer.append(ch);
    35         }
    36         return stringBuffer.toString();
    37     }
    38     
    39 }
      FullBorder类在字符串的上下左右都加上装饰边框。这里需要理解一下这些方法。getColumns方法获得的列数,也就是字符数为被装饰物的字符数加上两侧边框字符数。getRows方法获得的行数是被装饰物的行数加上上下边框的行数
      makeLine方法是连续地显示count次指定的字符ch,声明为private是因为防止被FullBorder以外的类使用。getRowText用于生成指定那一行的字符串,例如,row ==0表示下边框,row == display.getRows() + 1表示上边框。
     1 package bigjunoba.bjtu.decorator;
     2 
     3 public class Main {
     4 
     5     public static void main(String[] args) {
     6         Display display1 = new StringDisplay("Lianjiang");
     7         Display display2 = new SideBorder(display1, '*');
     8         Display display3 = new FullBorder(display2);
     9         Display display4 = 
    10                 new SideBorder(
    11                         new FullBorder(
    12                                 new FullBorder(
    13                                         new SideBorder(
    14                                                 new FullBorder(new StringDisplay("Lianjiang")),'*'
    15                                                       )
    16                                                 )
    17                                       ),'!'
    18                               );
    19         System.out.println("这是display1的输出:");
    20         display1.show();
    21         System.out.println();
    22         
    23         System.out.println("这是display2的输出:");
    24         display2.show();
    25         System.out.println();
    26         
    27         System.out.println("这是display3的输出:");
    28         display3.show();
    29         System.out.println();
    30         
    31         System.out.println("这是display4的输出:");
    32         display4.show();
    33     }
    34 }

      这是测试类。这里就不做过多解释了。

    这是display1的输出:
    Lianjiang
    
    这是display2的输出:
    *Lianjiang*
    
    这是display3的输出:
    [@@@@@@@@@@@]
    |*Lianjiang*|
    [@@@@@@@@@@@]
    
    这是display4的输出:
    ![@@@@@@@@@@@@@@@]!
    !|[@@@@@@@@@@@@@]|!
    !||*[@@@@@@@@@]*||!
    !||*|Lianjiang|*||!
    !||*[@@@@@@@@@]*||!
    !|[@@@@@@@@@@@@@]|!
    ![@@@@@@@@@@@@@@@]!
    

       测试结果如图所示。结合测试类来分析,1的装饰边框是2,然后得到完全体后2的装饰框是3,最后4是组合装饰,也就是多重边框。

      下面是Decorator模式的类图。

      Component:增加功能的核心角色。也就是装饰前的蛋糕,只是定义了蛋糕的接口,在示例中,是Display类。

      ConcreteComponent:实现了Component定义的接口的具体蛋糕。

      Decorator:具有与Component相同的接口,内部保存了被装饰对象--Component,示例中也就是Border类。

      ConcreteDecorator:具体的实现类。示例程序中的SideBorder类和FullBorder类。

      这里要扩展的知识是继承和委托。

      1.继承

      继承可以让子类和父类具有一致性。举一个例子,父类Parent和子类Child,有这么一个情况就是,Child类的实例可以保存在Parent类型的变量中,也可以调用从Parent类中继承的方法。比如:

     Parent obj = new Child();
     obj.parentMethod();
    

       也就是说,可以像操作Parent类的实例一样操作Child类的实例,这是将子类当做父类的一个例子。

      反过来,如果想将父类当做子类看待,需要先进行类型转换。

    Perent obj = new Child();
    ((Child)obj).child.Method();
    

       2.委托

      使用委托让接口具有透明性,自己和委托对象具有一致性。

      先看一个例子:

    abstract class Flower{
        abstract void method();    
    }
    
    class Rose extends Flower {
        Violet obj = ...
        void method(){
            obj.method();
        }        
    }    
    
    class Violet extends Flower(
        void method(){
         ...
        }
    }
    

      第一个例子是编写一个共同的抽象类Flower。

    interface class Flower{
        abstract void method();    
    }
    
    class Rose implements Flower {
        Violet obj = ...
        void method(){
            obj.method();
        }        
    }    
    
    class Violet implements Flower(
        void method(){
         ...
        }
    }
    

       第二个例子就是编写一个Flower接口。

      这两个例子主要是说明,Rose和Violet都有相同的method方法,但是Rose将method方法的处理委托给了Vidlet,这样就体现了method方法是共通的。

  • 相关阅读:
    conio
    create file in Linux
    单向链表
    面向对象(Object Oriented,OO)
    LLVM每日谈之五 LLVM文档介绍
    Team Geek 阅读笔记之 第三章 Every Boat Needs a Captain
    重温堆和拷贝构造函数
    LLVM每日谈之六 LLVM的源码结构
    LLVM每日谈之七 Clang
    LLVM每日谈之三 如何创建一个LLVM工程
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8707587.html
Copyright © 2011-2022 走看看