装饰模式(完全体会了此模式含义)
第三次复习
场景:
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。
如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,
这就是装饰器模式的目标。
要清楚,此模式的场景,是有一个被装饰对象,比如A,不想修改它。
其他装饰后的对象,比如B,C,D,都必须包含A。
本体A,和装饰后的B,C,D,都必须实现装饰功能,也就是接口。
但是B,C,D最好是实现虚类,这样可以规范装饰类必须符合2点,1.必须包含本地,2.实现装饰功能。而接口只能实现第2点。
这个模式,可以形成丰富多彩的类,所以属于结构型模式。
作用是这句:动态地扩展其功能,动态说明不能继承,只能组合,继承是静态扩展。
结构是这句:实现接口的自包含的虚类。
第二次分析感受:
看了下第一次分析。基本是把握到了本质。没有什么偏差。
补充下使用场景需求
1.原对象类必须存在,
2.需要动态组合新功能。
所以下面是推导模式的流程:
所以我们最初的考虑会是添加一个接口,再通过继承复写接口功能来扩展功能。
但是这个继承必须是组合形式的继承。因为需要在原功能的基础上扩展。所以还必须包含原对象。但是这样的话接口就没有强制性了(不能强制要求接口的派生类包含原对象)
所以可以用虚类,定义一个原对象成员变量。而虚类和被装饰的类都派生于同一接口(包含我们需要扩展的函数)。就解决了。
第一次分析:
这个模式也是之前没看懂。
需要看懂的话,要给自己提明确需求。之前没看懂就是因为很多书没有告诉你,为什么要放弃简单的方案。估计那些作者也没有深刻体会吧。
如,一个战士有形象展示。需要给个方案,可以灵活的加特效,如加个冰背景,火背景等。
先不要装b,最开始一定会是直接在类里面加方法。 结果,非常ok。如果你也满意,同事,客户都满意,而且以后改动都不大。那么装饰模式可以根本不要学习。估计你一辈子都用不到。
非常特特特殊情况。装饰动作太繁多,改动太频繁。那么才需要装饰模式。
新的要求。不要在类里面修改。把修改踢到类的外面。 那么逼迫自己想方案。
你会尝试很多方案,大多数都是错误的。
这个方案最起码有几点。
1必须输入输出都是原来的对象。不管你用函数,接口。虚类。各种技术都行,开始别confine自己。
2.需要对原来对象的某个方法复写。
3.继承的写法,是不行的,因为需要无穷的类。想想加火的特效。如果是效果是一点一点的加,想想看。要1000个火特效,需要继承1000次。中间再差点其他特效,而且顺序有关。简直无解。
4.基本上会逼迫你走向一个虚类继承被装饰类,并包含一个被装饰类对象。这样就为形成链条做好了准备,因为包含的是被装饰类,那么最初可以放入基础被装饰类,之后又可以把被装饰类的派生类放进去,形成一个装饰类的派生类,而它又可以放入到。。。
当方法复写的时候。调用包含的装饰类对象的方法。 只有这个时候你才明白,哦,这是装饰模式。才会发现,原来装饰模式这么巧妙。
包含被装饰类,并覆盖方法,调用被装饰类的方法,这个是完成了装饰的目的,比较容易推出来。而最后虚类继承被装饰类,是一个神来之笔。巧妙。
装饰模式从某种意义上,也是对组合方式的支持,继承固定性,无法满足。可以想象为最后展示战士的时候,是一个战士经过一个又一个类,这些类,只是把自己包裹了一下,并对某个方法包装了一下。
个人觉得,除非是真的体现了装饰功能,意思就是多变,否则可以用继承。
而且你对代码要求高,否则可以把新的装饰功能,放入到类中。
总结,非常巧妙的模式,和工厂方法一样。都非常巧妙,令人惊叹,但是有简单,易用的替代方案。如果不是特别严苛的话,基本不会使用。
关键代码:
public abstract class Decorator extends BaseClass//这里是神来之笔
{ public BaseClass mBaseClass;//可以传递BaseClass,又可以之后传递Decorator,完成了链头和链条的衔接。
public Decorator( BaseClass heightBSolider)
{
mBaseClass=heightBSolider;
}
@Override public String showMe()
{
return BaseClass.showMe();
}
}
public class Decorator { //假如我想在showme这个效果上,加点其他效果。第一个想到的便是继承。 //但是如果效果有上10种。而且排列不同,效果不同,那么继承就基本没办法了。 //想象3种效果。 单是组合就有3×2种可能。那么就要6个派生类了。这还没有无限叠加效果。 //当然可以用其他简单方法,直接在类里面加方法,这个类里面的addFire。等方法。简直就是简单到令人发指。 //但是别忘记了设计模式的原则。 对修改关闭。在一个类中加方法来达到扩展这种做法。比格不高。必须把修改踢到类外部。 //所以才有了装饰模式。 //使用不是很常见,因为普通青年,并没有 严苛到 对于很少概率的修改,都把代码提升到设计模式的高度。 //简单又高效的类,对于一个战士,想加什么特效自己动手,但是比格不高(没有对修改关闭) public class Soldier { public String showMe() { return "..."; } public String addFire(String show) { return "$"+show+"$"; } public String addWater(String show) { return "~"+show+"~"; } public String addSword(String show) { return "-"+show+"-"; } } public class HeightBSolider { public String showMe() { return "..."; } } //装饰模式,本意是装饰,不改变对象 //所以,1.必须是输入和输出都是原来的对象。 //其次,2.一般是对某个方法的复写。 //所以最直观的是继承。但是不灵活,2中变化,排列要4个类。 //所以整来整去,装饰类必须继承被装饰类,才能同时满足1,2. 2个条件。 //而且装饰类必须包含一个被装饰类,这样当装饰类复写方法的时候,不是调用基类的方法,而是调用成员变量的方法。 //好处在哪里呢?原来固定的多重继承的顺序才能实现的效果,经过包含一个变量,并使用变量的方法来复写。由固定变成了灵活的组合。 //最终1.装饰类必须继承被装饰类2.构造函数需要传递一个被装饰类,并设置为成员变量。3复写某个需要装饰的方法时,调用类型是被装饰类,而且是自己的成员变量的的方法, //来代替基类。 public class AddFire extends HeightBSolider { @Override public String showMe() { return "$"+super.showMe()+"$"; } } public class AddFirePlus extends HeightBSolider { @Override public String showMe() { return "$"+super.showMe()+"$"; } } public abstract class ABSDecorator extends HeightBSolider { public HeightBSolider mHeightBSolider; public ABSDecorator(HeightBSolider heightBSolider) { mHeightBSolider=heightBSolider; } @Override public String showMe() { return mHeightBSolider.showMe(); } } public class buff_fire extends ABSDecorator { public buff_fire(HeightBSolider heightBSolider) { super(heightBSolider); } public String showMe() { return "$"+mHeightBSolider.showMe()+"$"; } } public class buff_Water extends ABSDecorator { public buff_Water(HeightBSolider heightBSolider) { super(heightBSolider); } public String showMe() { return "~"+mHeightBSolider.showMe()+"~"; } } public void Run() { // Soldier LeeDragen=new Soldier(); // String show=LeeDragen.showMe(); // show=LeeDragen.addFire(show); // show=LeeDragen.addWater(show); // LSComponentsHelper.LS_Log.Log_INFO(show); HeightBSolider Lee=new HeightBSolider(); Lee=new buff_fire(Lee); Lee=new buff_Water(Lee); Lee=new buff_Water(Lee); Lee=new buff_Water(Lee); LSComponentsHelper.LS_Log.Log_INFO( Lee.showMe()); } }