装饰模式
动态的给一个对象增加一些额外的职责,就增加对象功能来说,装饰功能比生成子类实现更灵活,也是一种对象结构型模式。
跟之前所设计图片格式在不同的操作系统中显示一样。同样存在以下几个问题
(1)系统扩展麻烦,之前提供了画笔问题,如果需要增加新的显示效果或者别的控件,又得增加好几个类继承Component。
(2)代码重复,并且冗余,不只是窗体需要设置滚动条,文本框,列表框等也要设置滚动条,在其他几个类中存在相同的具体实现过程,不利于修改或者维护。
(3)系统庞大,类的数目非常多。
角色说明
(1)Component(抽象构件):抽象装饰类和具体构件的共同父类,声明了具体构件的业务方法,它的引入可以使客户端一致的方式处理未被装饰的对象和装饰之后的对象,对客户端透明操作。
(2)ConcreteComponent(具体构件)抽象构件的子类,用于定义具体的构件对象,实现了抽象构件声明的方法,装饰器可以给它增加额外职责。
(3)Decorator(抽象装饰器)抽象构件的子类,给具体构件增加职责,具体的职责由其子类实现,子类扩展该方法,已达到装饰的目的。注意这里实现了抽象构件的方法,但是其并未真正的实施装饰,只是调用了成员变量component的operation方法
(4)ConcreteDecorator(具体装饰类)它是抽象装饰类的子类,负责向构件中添加新的职责,每一个具体的装饰类都定义了一些新的行为,可以调用抽象装饰类定义的方法,并增加新的方法扩充对象的行为。
具体代码如下
package com.learn.designmode.mode.decorator.demo;
/**
* 抽象构件类
*/
public interface Component {
void operation();
}
/**
* 具体构件类
*/
class ConcreteComponent implements Component{
@Override
public void operation() {
System.out.println("具体构件类实现");
}
}
/**
* 抽象装饰类
*/
class Decorator implements Component{
// 这里其实是具体构件类
private Component component;
/** 通过构造方法注入
* @param component
*/
public Decorator(Component component){
this.component = component;
}
// 实现抽象构件类的方法,但是这里只是单纯的调用,并没有进行扩充
@Override
public void operation() {
component.operation();
}
}
/**
* 具体装饰类
*/
class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
/**
* 具体装饰类给具体构件类增加业务
*/
@Override
public void operation(){
super.operation();
this.addBehavior();
}
/**
* 增加的业务方法
*/
public void addBehavior(){
System.out.println("新增业务方法");
}
public static void main(String[] args) {
Component component,component1;
component = new ConcreteComponent();
component1 = new ConcreteDecorator(component);
component1.operation();
}
}
注意抽象装饰配的成员变量component,这里其实是依赖注入了具体构件类,这里还可以注入已经扩展的Decorator的子类对象,进行多层装饰
可以,第一个维度是系统本身的业务,第二个维度时扩展的业务;
相同处:都是将抽象与具体实现分离,使各个维度可以独立的变化。
不同处:桥接模式是把所有的维度组合在一起,而装饰模式是一层一层的套上新的维度。
完整的解决方案
package com.learn.designmode.mode.decorator;
public abstract class Component {
abstract void display();
}
/**
* 窗体框类
*/
class Windows extends Component{
@Override
void display() {
System.out.println("展示窗口");
}
}
/**
* 文本框类
*/
class Text extends Component{
@Override
void display() {
System.out.println("展示文本框");
}
}
/**
* 列表框类
*/
class ListBox extends Component{
@Override
void display() {
System.out.println("显示列表框");
}
}
/**
* 抽象装饰类
*/
class Decorator extends Component{
private Component component;
public Decorator(Component component){
this.component = component;
}
@Override
void display() {
component.display();
}
}
/**
* 具体装饰类(带滚动条)
*/
class ScrollBarDecorator extends Decorator{
public ScrollBarDecorator(Component component) {
super(component);
}
@Override
void display() {
super.display();
this.setScrollBar();
}
public void setScrollBar(){
System.out.println("为构件添加滚动条");
}
}
/**
* 具体装饰类(带黑色边框)
*/
class BlackBorderDecorator extends Decorator{
public BlackBorderDecorator(Component component) {
super(component);
}
@Override
void display() {
super.display();
this.setBlackBorder();
}
public void setBlackBorder(){
System.out.println("为构件增加黑色边框");
}
}
package com.learn.designmode.mode.decorator;
public class Client {
public static void main(String[] args) {
Component component,component1;
component = new Windows();
component1 = new ScrollBarDecorator(component);
component1.display();
}
}
如果需要更改其他的控件跟样式,只需要客户端进行带动,可以进行多个装饰。
透明装饰模式与半透明装饰模式
装饰模式再好,也存在问题,如果客户端需要单独调用新增具体装饰类实现的业务方法,如setBlackBorder和setScrollBar 那么客户端的代码将改成如下
package com.learn.designmode.mode.decorator;
public class Client {
public static void main(String[] args) {
Component component;
ScrollBarDecorator component1;
component = new Windows();
component1 = new ScrollBarDecorator(component);
component1.setScrollBar();
}
}
如果component1也是用Component 声明的话,将会出现编译错误,因为抽象类Component 中并没有setScrollBar方法,这种情况称为半透明装饰模式,下面我们来研究透明跟半透明,主要是针对客户端的调用情况进行分析
透明装饰模式
要求客户端完全针对抽象类Component 编程。也就是完整的解决方案中
半透明装饰模式
因为这会将失去具体构件类实现基本的业务方法,原来的业务操作都无法实现,更不用说增加新的职责了。
装饰模式注意事项
(1)尽量保持装饰类和被装饰类的接口相同,也就是说,尽可能使用透明的装饰模式。
(2)尽量保持具体构件类ConcreteComponent是一个“轻”类,也就是说不要把太多的行为放在具体构件中,可以使用装饰类进行扩展。
(3)如果只有一个具体构件类,那么抽象构件类可以直接作为子类。
总结
优点
(1)跟桥接模式一样,不需要太多的类,增加一个功能,增加类的个数不会太多。
(2)可以通过一种动态的方式来扩展一个对象的功能,从配置文件中选择具体的装饰类,从而实现不同的行为。
(3)可以对一个对象进行多次的装饰,通过不同的具体装饰类装饰的组合,可以得到功能强大的对象。
(4)具体装饰类和具体构件类可以独立化,用户可以根据需要增加新的具体构件类和装饰类,符合开闭原则
缺点
(1)使用装饰模式会产生很多小对象,这些对象的区别在于他们之间连接的方式有所不同,而不是他们的类或者属性值有所不同,大量小对象势必会占用更多的系统资源,在一定程度上影响性能。
(2)调试较为繁琐,因为职责是一层层附上去的,排查错误需要一级一级的去查找。
适用场景
(1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
(2)当不能采用继承的方式对系统进行扩展或者采用继承不利于扩展和维护可以使用,不能使用继承有两类
1.系统中存在大量独立的扩展,每支持一个扩展都需要产生大量的子类,使得子类数目爆炸增长。
2.类不能被继承(比如java中的final)