“大鸟,明天我要去见娇娇了,你说我穿什么去比较好?”小菜问大鸟道。
“这个你也来问我。干脆我代你去得了。”大鸟笑言。
“别开玩笑,我是诚心问你的.”
“哈哈,小菜呀,你别告诉我说四年大学你都没和女生约过会?”
“谁叫我念的是理工科大学呢,学校里本来女生就少,所以这些稀有宝贝们,大一时早就被那些老手们主动出击搞定了,我们这些恋爱方面的小菜哪还有什么机会,一不小心就虚度了四年。”小菜突生伤感,叹气摇头,并小声的唱了起来,“不是我不小心,只是真情难以寻觅,不是我存心故意,只因无法找到良机……”
“喂!”大鸟打断了小菜,“差不多就行了,感慨得没完没了。说正事,你要问我什么?”
“哦,你说我穿什么去见娇娇比较好?”
“那要看你想给人家什么印象?是比较年轻,还是比较干练:是比较颓废,还是要比较阳光;也有可能你想给人家一种极其难忘的印象,那穿法又大不一样了!”
“你这话怎么讲?”
“年轻,不妨走点嘻哈路线,大T恤、垮裤、破球鞋,典型的年轻人装扮。”
“啊,这不是我喜欢的风格,我从来也没这样穿过。”
“那就换一种,所谓干练,就是要有外企高级白领的样,黑西装、黑领带、黑墨镜、黑皮鞋……”
“你这叫白领?我看是黑社会。不行不行。”
“哈,来颓废的吧,颓废其实也是一种个性,可以吸引‘一些喜欢叛逆的女生。一般来说,其标志是头发可养鸟、胡子能生虫、衬衣没纽扣、香烟加狐臭。”
“这根本就是‘肮脏’的代表吗,开什么玩笑。你刚才提到给人家难忘印象,是什么样的穿法?”
“哈,这当然是绝妙的招了,如果你照我说的去做。娇娇想忘都难。”
“快说快说,是什么?”
“大红色披风下一身蓝色紧身衣,胸前一个大大的S,表明你其实穿的是‘小号’,还有最重要的是,一定要‘内裤外穿’”
“喂,你拿我寻开心呀,那是‘超人’的打扮呀,S代表的也不是Small是Super的意思。”小菜再次打断了大鸟,还是忍不住笑道,“我如果真的穿这样的服装去见MM那可真是现场的人都终身难忘,而小菜我,估计这辈子也不要见人了。”
“哈,你终干明白了!我其实想表达的意思就是,你完全可以随便一些,平时穿什么,明天还是穿什么,男生嘛,只要干净一些就可以了,关键不在于你穿什么。而在于你人怎么样。对自己都这么没信心,如何追求女孩子。”
“哦,我可能是多虑了一些。”小菜点头道,“好吧,穿什么我自己再想想。没什么事了吧,那我回房了。”
“等等,”大鸟叫住了他,“今天的模式还没有开讲呢,怎么就跑了。”
“哦,我想着约会的事,把学习给忘了,今天学什么模式?”
“先不谈模式,说说你刚才提到的穿衣问题。现在要你开发一个类似于QQ的服饰搭配系统,怎么做?”
“就是那种可以换各种各样的衣服裤子的个人形象系统?”
“是的,就简单点,用控制台程序,写可以给人搭配嘻哈服或白领装的代码。”
“行,我试试看。”
半个小时后,小菜第一版本代码出炉。
//person类代码 public class Person { private String name; public Person(String name) { this.name = name; } public void wearTShirts() { System.out.println("大T恤"); } public void wearBigTrouser() { System.out.println("大裤衩"); } public void wearSneakers() { System.out.println("破球鞋"); } public void wearSuit() { System.out.println("西装"); } public void wearTie() { System.out.println("领带"); } public void wearLeatherShoes() { System.out.println("皮鞋"); } public void show() { System.out.println("装扮的" + name); } } //客户端代码 public class Main { public static void main(String[] args) { Person person = new Person("小菜"); System.out.println("第一种装扮:"); person.wearTShirts(); person.wearBigTrouser(); person.wearSneakers(); person.show(); System.out.println("第二种装扮:"); person.wearSuit(); person.wearTie(); person.wearLeatherShoes(); person.show(); } }
结果显示:
第一种装扮:
大T恤
大裤衩
破球鞋
装扮的小菜
第二种装扮:
西装
领带
皮鞋
装扮的小菜
“哈,不错,功能是实现了。但现在的问题是,如果需要增加‘超人’的装扮,你如何做?”
“那就改‘Person’类就行了,”小菜说完就反应过来了,“哦,不对,这就违背了开放-封闭原则了。哈,我知道了,应该把这些服饰都写成子类就好了。我去改。”
大鸟抬起手伸出食指对小菜点了点,“你啊,刚学了这么多的重要的原则,怎么就全部忘记了?”
过了不到十分钟,小菜第二版本代码出炉。
代码结构图
//Person类 public class Person { private String name; public Person(String name) { this.name = name; } public void show() { System.out.println("装扮的" + name); } } //服饰抽象类 public interface Finery { public void show(); } //各类服饰 public class TShirts implements Finery { public void show() { System.out.println("大T恤"); } } public class BigTrouser implements Finery { public void show() { System.out.println("大裤衩"); } } public class Sneakers implements Finery { public void show() { System.out.println("破球鞋"); } } public class Suit implements Finery { public void show() { System.out.println("西装"); } } public class Tie implements Finery { public void show() { System.out.println("领带"); } } public class LeatherShoes implements Finery { public void show() { System.out.println("皮鞋"); } } //客户端代码 public class Main { public static void main(String[] args) { Person person = new Person("小菜"); System.out.println("第一种装扮:"); Finery dtx = new TShirts(); Finery kk = new BigTrouser(); Finery pqx = new Sneakers(); dtx.show(); kk.show(); pqx.show(); person.show(); System.out.println("第二种装扮:"); Finery xz = new Suit(); Finery ld = new Tie(); Finery px = new LeatherShoes(); xz.show(); ld.show(); px.show(); person.show(); } }
“这下你还能说我不面向对象吗?如果加入超人装扮,只需要增加子类就可以了。”
“我了个擦~,用了继承,用了抽象类就算是用好了面向对象了吗?你现在的代码确实是做到了‘服饰’类与‘人’类的分离,但其他问题还是存在的。”
“虾米问题?”
“你仔细看代码。”
dtx.show(); kk.show(); pqx.show(); person.show();
大鸟:“这样写意味着什么?”
小菜:“就是把大T恤、大裤衩、破球鞋和装扮小菜一个词一个词地显示出来啊。”
“说的好,我要的就是你这句话,这样写好比:你光着身子,当着大家的面,先穿了T恤,再穿了裤衩,再穿鞋。难道你穿个衣服都要在众目睽睽之下吗?”
“你的意思是,应该在内部组装完毕后,然后再显示出来?这好像是建造者模式啊。”
“不是的,建造者模式要求建造的过程必须是稳定的,而现在我们的例子,建造过程并不稳定,比如完全可以内穿西装,外套T恤,再加个披风,打个领带,皮鞋外再套个破球鞋,当然也完全可以只穿条裤衩就算完成了。换句话就是说,通过服饰组合出一个有修改的人完全可以有无数种方案,并非是固定。”
“啊,你说的对,其实午后顺序也是有讲究的,如你说的,先穿内裤后穿外裤,这叫凡人,内裤穿在外裤外面的,那就是超人了。”
“哈,你真会举一反三,那你说该怎么办呢?”
“我们需要把所需的功能按正确的顺序串联起来进行控制,这好像很难办啊。”
“不懂就学,其实也没有什么稀罕的,这可以用一个非常有意思的设计模式来实现。”
装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
“装饰这个词真好,无论衣服、鞋子、领带、披风其实都可以理解为对人的装饰。”
“我们来看看它的结构。”
装饰模式(Decorator)结构图
“Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需要知道Decorator的存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。”基本代码如下。
//Component接口 public interface Component { public void operation(); } //ConcreteComponent类 public class ConcreteComponent implements Component { public void operation() { System.out.println("具体对象的操作"); } } //Decorator类 public class Decorator implements Component { protected Component component; public void operation() { if (null != component) { component.operation(); } } public Component getComponent() { return component; } public void setComponent(Component component) { this.component = component; } } //装饰类 public class ConcreteDecoratorA extends Decorator { private String addedState; public void operation() { super.operation(); addedState="new state"; System.out.println("具体装饰对象A的操作"); } } public class ConcreteDecoratorB extends Decorator { public void operation() { super.operation(); addedBehavior(); System.out.println("具体装饰对象B的操作"); } private void addedBehavior() { } } //客户端代码 public class Main { public static void main(String[] args) { ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); d1.setComponent(c); d2.setComponent(d1); d2.operation(); } }
“我明白了,原来装饰模式是复用setComponent来对对象进行包装的。这样每个装饰对象的实现就和如何使用这个对象分离了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。用刚才的例子来说就是,我们完全可以先穿外裤,再穿内裤,而不一定要先内后外。”
“既然你已经明白了,还不赶快把刚才的例子改成用装饰模式的代码。”
“我还有个问题,刚才我写的例子中的‘人’类是Component还是ConcreteComponent呢?”
“哈,学习模式要善于变通,如果只有一个ConcreteComponent类而没有抽象的Component接口,那么Decorator类可以是ConcreteComponent的一个子类。同样道理,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。”
“啊,原来如此。在这里我们就没有必要有Component接口了,直接让服饰类Decorator继承‘人’类ConcreteComponent就可以了。”
二十分钟,小菜第三版本代码出炉。
代码结构图
//Person类(ConcreteComponent) public class Person { private String name; public Person() { } public Person(String name) { this.name = name; } public void show() { System.out.println("装扮的" + name); } } //服饰类(Decorator) public class Finery extends Person { protected Person component; public void decorate(Person component) { this.component = component; } public void show() { if (null != component) { component.show(); } } } //具体服饰类(ConcreteDecorator) public class TShirts extends Finery { public void show() { super.show(); System.out.println("大T恤"); } } public class BigTrouser extends Finery { public void show() { super.show(); System.out.println("大裤衩"); } } public class Sneakers extends Finery { public void show() { super.show(); System.out.println("破球鞋"); } } public class Suit extends Finery { public void show() { super.show(); System.out.println("西装"); } } public class Tie extends Finery { public void show() { super.show(); System.out.println("领带"); } } public class LeatherShoes extends Finery { public void show() { super.show(); System.out.println("皮鞋"); } } //客户端代码 public class Main { public static void main(String[] args) { Person person = new Person("小菜"); System.out.println("第一种装扮:"); Sneakers pqx = new Sneakers(); BigTrouser kk = new BigTrouser(); TShirts dtx = new TShirts(); pqx.decorate(person); kk.decorate(pqx); dtx.decorate(kk); dtx.show(); System.out.println("第二种装扮:"); LeatherShoes px = new LeatherShoes(); Tie ld = new Tie(); Suit xz = new Suit(); px.decorate(person); ld.decorate(px); xz.decorate(ld); xz.show(); } }
结果显示:
第一种装扮:
装扮的小菜
破球鞋
大裤衩
大T恤
第二种装扮:
装扮的小菜
皮鞋
领带
西装
“如果我换一种装饰方式,结果会怎样呢?”大鸟改动了小菜的代码。
System.out.println("第三种装扮:"); Sneakers pqx2 = new Sneakers(); LeatherShoes px2 = new LeatherShoes(); BigTrouser kk2 = new BigTrouser(); Tie ld2 = new Tie(); pqx2.decorate(person); px2.decorate(pqx2); kk2.decorate(px2); ld2.decorate(kk2); ld2.show();
结果会显示:
第三种装扮:
装扮的小菜
破球鞋
皮鞋
大裤衩
领带
“哈,光着膀子、打着领带、下身大裤衩、左脚皮鞋、右脚破球鞋的极具个性的小菜就展现在我们面前了。”
“你这家伙,又开始拿我开涮。我要是这个样子,比扮超人还丢人。”
“来总结一下装饰模式。”
“我觉得装饰模式是为已有功能动态的添加更多功能的一种方式。但到底什么时候用它呢?”
“说的好,在起初的设计中,当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有的类的核心职责或主要行为,比如用西装或嘻哈服来装饰小菜,但这种做法的问题在于,它们在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,就像你起初的那个‘人’类,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。所以就出现了上面那个例子的情况,我们可以通过装饰,让你全副武装到牙齿,也可以让你只挂一丝到内裤。”
“那么装饰模式的优点我总结下来就是,把类中的装饰功能从类中搬移去除,这样可以简化原有的类。”
“是的,这样做更大的好处在于能够有效地把类的核心职责和装饰功能区分开来,而且可以去除相关类中重复的装饰逻辑。”
“这个模式真不错,我以后要望着常使用它。”
“你可要当心哦,装饰模式的装饰顺序很重要的哦,比如加密数据和过滤词汇都可以是数据持久化前的装饰功能,但若先加密了数据再用过滤功能就会出问题了,最理想的情况,是保证装饰类之间彼此独立,这样它们就可以以任意的顺序进行组合了。”
“是啊,穿上西装再套上T恤实在不是什么好穿法。”
“明天想好了要穿什么去见MM了吗?”
“有了装饰模式,我还用担心我的穿着吗?再说我信奉的是《天下无贼》中刘德华说的一句经典台词:‘开好车就是好人吗?’,小菜我魅力无限,不需要装饰,哇哈哈哈哈~”
大鸟惊奇地望着小菜,无法理解地说:“你丫学完模式,判若两人啊,真强!”