装饰者模式 - 装饰边框与被装饰物的一致性。
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比承更有弹性,装饰者模式也体现了开闭原则(ocp)
- 这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上以代码的形式体现,请同学们注意体会。
类比:
穿衣服是使用装饰的一个例子。 觉得冷时, 你可以穿一件毛衣。 如果穿毛衣还觉得冷, 你可以再套上一件夹克。 如果遇到下雨, 你还可以再穿一件雨衣。 所有这些衣物都 “扩展” 了你的基本行为, 但它们并不是你的一部分, 如果你不再需要某件衣物, 可以方便地随时脱掉。
示例程序
功能是给文字添加装饰边框。
例如为Hello,world加装饰边框。
类的一览表:
- Display:用于显示字符串的抽象类
- StringDisplay:用于显示单行字符串的类
- Border:用于显示装饰边框的抽象类
- SideBorder:用于显示左右边框的类
- FullBorder:用于显示上下左右边框的类。
- Main:测试程序的类
类图:
Display:用于显示字符串的抽象类
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));
}
}
}
StringDisplay:用于显示单行字符串的类
//实现了DisPlay的抽象方法。
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() {//单行字符串的行数为1
return 1;
}
@Override
public String getRowText(int row) {//仅当行数为0时返回值
if(row == 0){
return string;
} else {
return null;
}
}
}
Border:用于显示装饰边框的抽象类
//装饰边框的抽象类,通过继承,装饰边框与被装饰五具有相同的方法。意味着他们具有一致性。
public abstract class Border extends Display {
protected Display display;//表示被装饰物
protected Border(Display display){
this.display = display;
}
}
SideBorder:用于显示左右边框的类
public class SideBorder extends Border{
private char ch; //装饰边框的字符
protected SideBorder(Display display, char ch) {
super(display);
this.ch = ch;
}
@Override
public int getColumns() {//字符数为字符串数加上两边加的字符数
return 2 + display.getColumns();
}
@Override
public int getRows() {//只在字符串两边加字符,所以行数不变,直接调用display方法即可
return display.getRows();
}
@Override
public String getRowText(int row) {
return ch + display.getRowText(row) + ch;
}
}
FullBorder:用于显示上下左右边框的类
public class FullBorder extends Border{
public FullBorder(Display display) {
super(display);
}
@Override
public int getColumns() {
return 2 + display.getColumns();//在字符串上下添加字符,行数加2
}
@Override
public int getRows() {
return 2 + display.getRows();
}
@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 buffer = new StringBuffer();
for(int i = 0;i < count;i ++){
buffer.append(ch);
}
return buffer.toString();
}
}
Main:测试程序的类
public class Main {
public static void main(String[] args) {
Display b1 = new StringDisplay("Hello");
Display b2 = new SideBorder(b1, '#');
Display b3 = new FullBorder(b2);
b1.show();
System.out.println();
b2.show();
System.out.println();
b3.show();
}
}
- 部件 (Component) 声明封装器和被封装对象的公用接口。
- 具体部件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。
- 基础装饰 (Base Decorator) 类拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。
- 具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。
- 客户端 (Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。
装饰模式适合应用场景
-
如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。
-
装饰能将业务逻辑组织为层次结构, 你可为各层创建一个装饰, 在运行时将各种不同逻辑组合成对象。 由于这些对象都遵循通用接口, 客户端代码能以相同的方式使用这些对象。
-
如果用继承来扩展对象行为的方案难以实现或者根本不可行, 你可以使用该模式。