结构型设计模式----装饰模式

引子

先列举一个生活中的场景。假如我们买了一套房(买不起。。),但是是一个毛胚房(指没有任何装修的房子),只能住。那么我们为了让房子变得温馨舒适,就要进行装修了,这样没有改变房子原本用来居住的功能,还增加了很多新的功能(比如做饭)。

那么这一期的装饰模式就是基于这样的动机,在不改变原有功能的情况下添加新功能

那么给一个类或对象增加新的行为可以通过两种方式来完成:

  • 继承:通过继承可以使子类在拥有自身方法的同时拥有父类方法,但这种增加行为的方式是静态的,用户不能控制增加行为的方式和时机,只能被动的使用新子类的行为。
  • 关联:通过给装饰器嵌入一个对象,在装饰器中可以调用原有类的方法,也可以增加新的方法扩充原有类。可以动态的给对象添加新的行为。

模式简介

定义:动态的给一个对象添加新的职责,就增加对象功能来说,比生成子类的方式要更加灵活。

模式结构图:
装饰模式结构图
说明:

  • Component:抽象构件类,是具体构件和抽象装饰类的共同父类。其中定义了具体构件中需要实现的业务方法,它的存在使得客户端可以一致的处理未被装饰的对象以及装饰后的对象,实现客户端的透明操作(后面说明什么是透明操作)。
  • ConcreteComponent:具体构件,它是抽象构件类的子类,实现了其中的业务方法。装饰器可以给它添加新的职责。
  • Decorator:抽象装饰类,它是抽象构建类的子类,用于给具体构建类添加职责,具体实现在其子类中。它维护一个指向抽象构件类对象的引用,通过该引用调用未被装饰对象的方法,并通过子类扩展该方法达到装饰的目的。
  • ConcreteComponent:具体装饰类,负责向构建添加新的职责。

举个例子

抽象构件:

public interface Component {
    void show();
}

具体构建和抽象装饰类

public class ConcreteComponent implements Component {

    @Override
    public void show() {
        System.out.println("我是目标对象。。。");
    }
}
public class Decorator implements Component {

    private Component com;

    public Decorator(Component com) {
        this.com = com;
    }

    @Override
    public void show() {
        com.show();
    }
}

具体的装饰类:

public class ConcreteDecorator extends Decorator {


    public ConcreteDecorator(Component com) {
        super(com);
        System.out.println("开始装饰。。。");
    }

    public void show() {
        super.show();
        System.out.println("增加新功能A。。。");
    }
}
public class ConcreteDecoratorB extends Decorator {

    public ConcreteDecoratorB(Component com) {
        super(com);
    }

    public void show() {
        super.show();
        System.out.println("添加第二个新功能。。。");
    }
}

测试:

public class DecoMain {

    public static void main(String[] args) {
        Component target = new ConcreteComponent();
        System.out.println("未装饰前-----------");
        target.show();
        Decorator fist = new ConcreteDecorator(target);
        System.out.println("第一次装饰--------");
        fist.show();
        Decorator sec = new ConcreteDecoratorB(target);
        System.out.println("第二次装饰---------");
        sec.show();
        Decorator three = new ConcreteDecoratorB(fist);
        System.out.println("装饰两次----------");
        three.show();
    }
}
//运行结果
未装饰前-----------
我是目标对象。。。
开始装饰。。。
第一次装饰--------
我是目标对象。。。
增加新功能A。。。
第二次装饰---------
我是目标对象。。。
添加第二个新功能。。。
装饰两次----------
我是目标对象。。。
增加新功能A。。。
添加第二个新功能。。。

这里举了一个简单的例子,可以看出来装饰模式的一些特点了。可以动态的增加新的功能,且容易控制添加新行为的时机。

优缺点

优点

  • 扩展一个对象的行为,装饰模式比继承要更加灵活,不会导致类的个数急剧增加。
  • 可以通过动态的方式来扩展一个类的功能。
  • 对一个对象可以多次装饰。
  • 具体构件类和具体装饰类可以独立变化,用户可以根据需求增加。

缺点

  • 使用装饰模式的过程中会产生很多的小对象,比如多次装饰时,中间的类。
  • 虽然装饰模式比继承要更加灵活,但也意味着更加容易出错,排错也更困难。比如多级装饰的时候,排错需要逐级排查,较为繁琐。

适用场景

  • 在不影响其他对象的情况下,动态、透明的方式为单个对象添加功能。
  • 不能适用继承或者适用继承不利于系统的扩展和维护时可以使用装饰模式。

常见的使用场景

  • java.swing中很多场景,比如为一个Jlist添加滚动条
JList list = new JList();
JScrollPane jsp = new JScrollPane(list);
  • 其实最经典的就是java的IO
    在这里插入图片描述
    其中抽象构件是InputStream,具体构件是FileInputStream、ByteArrayInputStream等。抽象装饰类是FilterInputStream,具体装饰类BufferedInputStream(提供缓冲的功能)等。

一些问题

  • 在开始的时候我们说到抽象构件的存在是为了使客户端的操作透明。那么这里说的透明就是说客户端完全针对抽象编程,即所有的声明都应该是抽象构件类型。在上面的示例中应当表现为这样:
        Component target = new ConcreteComponent();
        Component fist = new ConcreteDecorator(target);
        Component sec = new ConcreteDecoratorB(target);
        Component three = new ConcreteDecoratorB(fist);
	    .......

这个也好理解,就是多态的体现罢了。

  • 还有一种半透明的模式,这种模式下允许客户声明具体的装饰类,并且调用具体装饰类中新增的方法。(我举的例子中是直接输出的,原理相似)可以是这样的使用模式:
ConcreteDecorator  obj = new ConcreteDecorator(target);
 obj.newMethod();//新增的方法

注意事项

  • 尽量保证装饰类的接口和被装饰类的接口相同,这样对于客户端来说装饰前后的对象都可以一致对待,也就是说尽可能使用透明模式
  • 保证具体的装饰类是一个“轻”类,也就是说不要添加太多的行为,可以通过装饰的方式添加。
  • 如果只有一个具体构件类,那么抽象装饰类可以直接继承它。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
支持移动端html5 可以控制滚动条样式,方便实用。 /* 1、showArrows:是否显示滚动箭头,模式是false; 2、maintainPosition:当滚动区重新初始化后,是否保持滚动条的原有位置,默认是true; 3、stickToBottom:当maintainPosition设置为true,且滚动区域到达底部,当有新内容添加的时候任然会固定在滚动区域底部,默认是false; 4、stickToRight:与stickToBottom属性原理类似,只是方向是右侧而不是底部; 5、autoReinitialise:自动初始化滚动区,内部实现机制实际上是一个定时器,当检测到内部有内容新增时,重新初始化,由于性能原因,默认false; 6、autoReinitialiseDelay:当autoReinitialise设置为true时,该属性表示自动初始化的延时,默认是500ms; 7、verticalDragMinHeight:垂直可拖动的最小高度,默认是0; 8、verticalDragMaxHeight:垂直可拖动的最大高度,默认是99999; 9、horizontalDragMinWidth:水平可拖动的最小距离,默认是0; 10、horizontalDragMaxWidth:水平可拖动的最大距离,默认是99999; 11、contentWidth:滚动区域的宽度,一般不要设置,该插件会根据内容实际宽度计算,默认undefined; 12、animateScroll:当调用scrollTo 或者scrollBy的时候,设置一个动画效果,包括时长duration和渐变ease,默认false; 13、animateDuration:动画时长,默认300ms; 14、animateEase:动画渐变函数,默认linear; 15、hijackInternalLinks:劫持锚链接,定位到滚动区域指定位置,默认false; 16、verticalGutter:垂直方向,内容和滚动条之间的距离,默认是4px; 17、horizontalGutter:水平方向,内容和滚动条之间的距离,默认是4px; 18、mouseWheelSpeed:鼠标滚轮的速度,默认是10px; 19、arrowButtonSpeed:方向按钮滚动内容的速度,默认是10px; 20、arrowRepeatFreq:按住方向按钮,内容滚动的频率,默认是100ms; 21、arrowScrollOnHover:当鼠标悬浮在方向按钮上时,是否允许滚动,默认false; 22、verticalArrowPositions:垂直方向按钮和固定点的位置,默认split; 23、horizontalArrowPositions:同上,水平方向; 24、enableKeyboardNavigation:是否允许键盘导航,默认true; 25、hideFocus:是否隐藏焦点框,默认false; 26、clickOnTrack:当点击固定点的时候,是否向相应方向滚动内容,默认true; 27、trackClickSpeed:点击固定点的滚动速度,默认是30px; 28、trackClickRepeatFreq:点击固定点的滚动频率,默认是100ms。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值