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

  首先看示例程序的类图。

  然后看示例程序代码。

 package bigjunoba.bjtu.decorator;

 public abstract class Display {

     public abstract int getColumns();
public abstract int getRows();
public abstract String getRowText(int row);
public final void show() {
for (int i = 0; i < getRows(); i++) {
System.out.println(getRowText(i));
}
}
}

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

 package bigjunoba.bjtu.decorator;

 public class StringDisplay extends Display{

     private String string;

     public StringDisplay(String string) {
this.string = string;
} @Override
public int getColumns() {
return string.getBytes().length;
} @Override
public int getRows() {
return 1;
} @Override
public String getRowText(int row) {
if (row == 0) {
return string;
} else {
return null;
}
} }

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

 package bigjunoba.bjtu.decorator;

 public abstract class Border extends Display {

     protected Display display;
protected Border(Display display) {
this.display = display;
}
}

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

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

 package bigjunoba.bjtu.decorator;

 public class SideBorder extends Border {

     private char borderChar;

     protected SideBorder(Display display, char ch) {
super(display);
this.borderChar = ch;
// TODO Auto-generated constructor stub
} @Override
public int getColumns() {
return 1 + display.getColumns();
} @Override
public int getRows() {
return display.getRows();
} @Override
public String getRowText(int row) {
return borderChar + display.getRowText(row) + borderChar;
} }

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

 package bigjunoba.bjtu.decorator;

 public class FullBorder extends Border{

     protected FullBorder(Display display) {
super(display);
} @Override
public int getColumns() {
return 1 + display.getColumns() + 1;
} @Override
public int getRows() {
return 1 + display.getRows() + 1;
} @Override
public String getRowText(int row) {
if (row ==0) {
return "[" + makeLine('@', display.getColumns()) + "]";
} else if (row == display.getRows() + 1) {
return "[" + makeLine('@', display.getColumns()) + "]";
} else {
return "|" + display.getRowText(row - 1) + "|";
} } private String makeLine(char ch, int count) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < count + 1; i++) {
stringBuffer.append(ch);
}
return stringBuffer.toString();
} }
  FullBorder类在字符串的上下左右都加上装饰边框。这里需要理解一下这些方法。getColumns方法获得的列数,也就是字符数为被装饰物的字符数加上两侧边框字符数。getRows方法获得的行数是被装饰物的行数加上上下边框的行数
  makeLine方法是连续地显示count次指定的字符ch,声明为private是因为防止被FullBorder以外的类使用。getRowText用于生成指定那一行的字符串,例如,row ==0表示下边框,row == display.getRows() + 1表示上边框。
 package bigjunoba.bjtu.decorator;

 public class Main {

     public static void main(String[] args) {
Display display1 = new StringDisplay("Lianjiang");
Display display2 = new SideBorder(display1, '*');
Display display3 = new FullBorder(display2);
Display display4 =
new SideBorder(
new FullBorder(
new FullBorder(
new SideBorder(
new FullBorder(new StringDisplay("Lianjiang")),'*'
)
)
),'!'
);
System.out.println("这是display1的输出:");
display1.show();
System.out.println(); System.out.println("这是display2的输出:");
display2.show();
System.out.println(); System.out.println("这是display3的输出:");
display3.show();
System.out.println(); System.out.println("这是display4的输出:");
display4.show();
}
}

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

这是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方法是共通的。

最新文章

  1. 自写网站入门阶段之三:兼容大战与jq初探
  2. 随堂笔记javascript篇之chrome调试:
  3. 甲乙(数理逻辑)转自http://www.cnblogs.com/devymex/p/3329635.html
  4. BFC and Haslayout
  5. Django 基础教程
  6. Android控件之圆形Button
  7. 开源top100
  8. Access数据库创建、使用
  9. io cache
  10. leetcode&mdash;word ladder II
  11. hbase运行模式
  12. Activiti初学者教程
  13. PHP学习笔记(七)
  14. console调试--转
  15. 快速搭建Android 开发环境-使用ADT Bundle
  16. Java开发工具与程序调试
  17. asp.net MVC Razor 语法(1)
  18. asp.net core的DI框架思考以及服务实例的获取方式总结
  19. Python自学知识点----Day03
  20. Django_tips

热门文章

  1. java程序猿如何练习java版的易筋经?
  2. 1. jQuery中的DOM操作
  3. Linux入门(磁盘与挂载)
  4. XCTF-upload
  5. idea 新建项目 coding上新建项目 idea推送到coding
  6. js对象参考手册 -戈多编程
  7. JZOJ 3875 星球联盟
  8. Faith 信念
  9. java和JavaScript的注释区别
  10. 使用Xming显示Oracle Linux图形界面