6.1穿什么有这么重要?
- 时间: 3月16日20点 地点: 大鸟房间 人物: 小菜, 大鸟
“大鸟, 明天我要去见娇娇了, 你说我穿什么去比较好?” 小菜问答鸟道.
“这个你也来问我, 干脆我代你去得了.” 大鸟笑道.
“别开玩笑, 我是诚心问你的”
“哈哈, 小菜呀, 你别告诉我说大学四年你都没和女生约过会?”
….此处省略500字….
“哦, 我想着约会的事, 把学习给忘了, 今天学什么模式?”
6.2 小菜扮靓第一版
“先不谈模式, 说说你刚才提到的穿衣服的问题. 我现在要求你写一个可以给人搭配不同服饰的系统, 比如类似 qq, 网络游戏, 或论坛都有的 avatar 系统. 你怎么开发”
“你是说那种可以换各种各样的衣服裤子的个人形象的系统?”
“是的, 现在你就简单点儿吧, 用控制台的程序, 写可以给人搭配嘻哈服或白领的代码.”
“哦, 我试试看吧.”
半小时后, 小菜的第一版代码出炉.
Person类
123456789101112131415161718192021222324252627282930313233343536public class Person {private String name;public Person(String name) {this.name = name;}public void wearTShirts() {System.out.print("T恤 ");}public void wearBigTrouser() {System.out.print("垮裤 ");}public void wearSneakers() {System.out.print("破球鞋 ");}public void wearSuit() {System.out.print("西装 ");}public void wearTie() {System.out.print("领带 ");}public void wearLeatherShoes() {System.out.print("皮鞋 ");}public void show() {System.out.println("装扮的 " + name);}}客户端代码
12345678910111213141516171819public static void main(String[] args) {Person xc = new Person("小菜");System.out.println("第一种装扮");xc.wearTShirts();xc.wearBigTrouser();xc.wearSneakers();xc.show();System.out.println("第二种装扮");xc.wearSuit();xc.wearTie();xc.wearLeatherShoes();xc.show();}结果展示
1234第一种装扮T恤 垮裤 破球鞋 装扮的 小菜第二种装扮西装 领带 皮鞋 装扮的 小菜
“哈, 不错, 功能是实现了. 现在的问题就是如果我需要增加 ‘超人’ 的装扮, 你的如何做?”
“那就改改 ‘Person’ 类就行了,” 小蔡说完就反应过来, “哦, 不对, 这就违背了, 开放封闭原则了. 哈, 我知道了, 应该把这些服饰都写成子类就好了. 我去改.”
大鸟抬起手指对小才点了点, “你呀, 刚学的这么重要的原则, 怎么还会忘?”
##6.3 小菜扮靓第二版
过了不到十分钟, 小菜的第二版代码出炉
Person 类
1234567891011public class Person {private String name;public Person(String name) {this.name = name;}public void show() {System.out.println("装扮的 " + name);}}抽象服饰类
123public abstract class Finery {public abstract void show();}各种服饰子类
123456789101112131415public class TShirts extends Finery {public void show() {System.out.println("T恤");}}public class BigTrouser extends Finery {public void show() {System.out.println("垮裤");}}// 以下类似, 省略....客户端代码
1234567891011121314151617181920212223public static void main(String[] args) {Person xc = new Person("小菜");System.out.println("第一种装扮");Finery dtx = new TShirts();Finery kk = new BigTrouser();Finery pqx = new Sneakers();dtx.show();kk.show();pqx.show();xc.show();System.out.println("第二种装扮");Finery xz = new Suit();Finery ld = new Tie();Finery px = new LeatherShoes();xz.show();ld.show();px.show();xc.show();}
|
|
“这样写意味着什么?” 大鸟问道.
“就是把 ‘大T恤’, ‘垮裤’, ‘破球鞋’, 和 ‘装扮的小菜’, 一个词一个词的显示出来呀.”
“说得好, 我要的就是你这句话, 这样写就好比: 你光着身子, 当着大家的面, 先穿T恤, 在穿裤子, 在穿鞋, 仿佛在跳穿衣舞. 难道你穿衣服都是在众目睽睽下穿的吗?”
“你的意思是, 应该在内部组装完毕, 然后在显示出来? 这好像是建造者模式呀?”
“不是的, 建造者模式要求建造的过程必须是稳定的, 而现在我们这个例子, 建造过程是不稳定的, 比如完全可以穿西装, 外套T恤, 再加披风, 打上领带, 皮鞋外在穿上破球鞋: 当然也完全可以只穿条裤衩就算完成. 换句话就是说, 通过服饰组合出一个有个性的人完全可以有无数种方案, 并非是固定的.”
“啊, 你说得对, 其实先后顺序也是有讲究的, 比如你说, 先穿内裤后传外裤, 这叫凡人, 内裤外穿, 这就是超人了.”
“哈, 很会举一反三嘛, 那你说该如何办呢?”
“我们需要把所需的功能按正确的顺序串联起来进行控制, 这好像很难办哦.”
“不懂就学, 其实也没什么稀罕的, 这可以用一个非常有意思的设计模式来实现.”
6.4 装饰模式(Decorator)
装饰模式(Decorator), 动态的给一个对象添加一些额外的职责, 就增加功能来说, 装饰模式比生成子类更为灵活.
“啊, 装饰这词真好, 无论衣服, 鞋子, 领带, 披风其实都可以理解为对人的装饰.”
“我们来看看它的结构.”
“Component 是定义一个对象接口, 可以给这些对象动态的添加职责. ConcreteComponent 是定义了一个具体的对象, 也可以给这个对象添加一些职责. Decorator, 装饰抽象类, 继承了 Component, 从外类来扩展 Component 类的功能, 但对于 Component 来说, 无需知道 Decorator 的存在的. 至于ConcreteDecorator 就是具体的装饰对象, 祈祷给 Component 添加职责的功能[DPE].“
“来看看基本的代码实现.”
Component 类
123public abstract class Component {public abstract void operation();}ConcreteComponent 类
123456public class ConcreteComponent extends Component {public void operation() {System.out.println("具体的对象操作");}}Decorator 类
123456789101112131415161718public class Decorator extends Component {protected Component component;// 设置 Componentpublic void setComponent(Component component) {this.component = component;}// 重写 operation(), 实际执行的是// Component 的 operation() 方法public void operation() {if (null != component) {component.operation();}}}ConcreteDecoratorA 类
1234567891011121314151617181920212223242526272829public class ConcreteDecoratorA extends Decorator {// 本类独有的功能, 以区别于 ConcreteDecoratorBprivate String addedState;public void operation() {// 首先执行原来 Component 的 operation()// 在执行本类的功能, 如 addedState, 相当于// 对原 Component 进行了装饰super.operation();addedState = "New State";System.out.println("具体装饰对象A的操作");}}public class ConcreteDecoratorB extends Decorator {public void operation() {// 同理, 限制性原 Component 的 operation() 方法// 在执行本类的功能.super.operation();AddedBehaviour();System.out.println("具体装饰对象B的操作");}private void AddedBehaviour() {// 本类独有的方法, 以区别于 ConcreteDecoratorA}}客户端代码
12345678910111213public static void main(String[] args) {ConcreteComponent c = new ConcreteComponent();ConcreteDecoratorA d1 = new ConcreteDecoratorA();ConcreteDecoratorB d2 = new ConcreteDecoratorB();// 装饰对象的方法是: 首先用 ConcreteComponent 实例化// 对象 c, 然后用 ConcreteDecoratorA 实例化的对象 d1// 来包装 c, 再用ConcreteDecoratorB 的对象 d2 包装 d1// 最终执行 d2 的 operation()d1.setComponent(c);d2.setComponent(d1);d2.operation();}
“我明白了, 原来装饰模式实例用 setComponent 来对对象进行包装的. 这样每个装饰对象的实现就和如何使用这个对象分开了, 每个装饰对象只关心自己的功能, 不需要关心如何被添加到对象链当中[DPE]. 用刚才的例子来说就是, 我们完全可以先穿外裤, 在穿内裤, 而不一定要先内后外.”
“既然你明白了, 还不快些把刚才例子改成装饰模式吗? 大鸟提醒道.”
“我还有个问题, 刚才我写的那个例子中 ‘人’ 类士 Component 还是 ConcreteComponent 呢?”
“哈, 学习模式要善于变通, 如果只有一个 ConcreteComponent 类, 而没有抽象的 Component 类, 那么 Decorator 类, 可以使 ConcreteComponent 的一个子类. 同样道理, 如果只有一个 ConcreteDecorator 类, 那么就没有必要建立一个单独的 Decorator 类, 而可以吧 Decorator 和 ConcreteDecorator 的责任合并成一个类.“
“啊, 原来如此啊. 在这里我们就没有必要 Component 类了, 直接让服饰 Decorator 继承人类 ConcreteComponent 即可”
6.5小菜扮靓第三版
二十分钟后, 小菜的第三版代码出炉.
“Person” 类 (ConcreteComponent)
1234567891011121314public class Person {private String name;public Person() {}public Person(String name) {this.name = name;}public void show() {System.out.println("装扮的 " + name);}}服饰类 (Decorator)
123456789101112131415public class Finery extends Person{protected Person component;public void decorate(Person component) {this.component = component;}public void show() {if (null != component) {component.show();}}}服饰类 (Decorator)
123456789101112131415public class Finery extends Person{protected Person component;public void decorate(Person component) {this.component = component;}public void show() {if (null != component) {component.show();}}}具体服饰类(ConcreteDecorator)
1234567891011121314151617public class TShirts extends Finery {public void show() {System.out.println("T恤");super.show();}}public class BigTrouser extends Finery {public void show() {System.out.println("垮裤");super.show();}}// 其余的类似, 省略...客户端代码
12345678910111213141516171819202122232425public static void main(String[] args) {Person xc = new Person("小菜");System.out.println("第一种装扮");Sneakers pqx = new Sneakers();BigTrouser kk = new BigTrouser();TShirts dtx = new TShirts();pqx.decorate(xc);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(xc);ld.decorate(px);xz.decorate(ld);xz.show();}结果显示
1234第一种装扮T恤 垮裤 破球鞋 装扮的 小菜第二种装扮西装 领带 皮鞋 装扮的 小菜
“如果我换一种装饰方式, 结果会怎么样呢?” 大鸟改动了小菜的代码.
- 结果就会显示12第三种装扮领带 垮裤 皮鞋 破球鞋 装扮的 小菜
“哈, 光着膀子, 打着领带, 下身垮裤, 左脚皮鞋, 右脚破球鞋的几句个性的小菜就展现在我们面前了.”
“你这家伙, 又开始那我开涮. 我要这样子, 比拌超人还丢人.”
6.6 装饰模式总结
“来来来, 总结一下, 你感觉装饰模式如何?”
“我觉得装饰模式是为已有的功能动态的添加更多功能的一种方式.但到底什么时候用它呢?”
“答得很好, 问的问题更好. 你起初的设计中, 当系统需要更新功能的时候, 是向旧的类中添加新的代码. 这些新加的代码通常装饰了原有类的核心职责的主要行为, 比如用西装或嘻哈服来装饰小菜, 但这种做法的问题在于, 它们在主类中加入了新的字段, 新的方法和新的逻辑, 从而增加了主类的复杂度, 就像你起初的那个 ‘人’ 类, 而这些新加入的东西仅仅是为了满足一些在只在某种特定情况下才会执行的特殊行为的需要. 而装饰模式却提供了一个非常好的解决方案, 它把每个要装饰的功能放在单独的类中, 并让这个类包装他所要装饰的对象, 因此, 当需要执行的特殊行为时, 客户端代码就可以在运行时根据需要有选择的, 按顺序的使用装饰功能包装对象了[DP]. 所以就出现了上面个那个例子的情况, 我可以通过装饰, 让你全服武装到牙齿, 也可以让你只挂到一丝内裤.”
“那么装饰模式的优点我总结下来就是, 把类中的装饰功能从类中搬移出去, 这样可以简化原有的类.“
“是的, 这样做更大的好处就是有效的把类的核心职责和装饰功能区分开了. 而且可以去除相关类的重复的装饰逻辑.“
“这个模式真心不错, 我以后要记着常使用他”
“你可要当心哦, 装饰模式的装饰顺序很重要哦, 比如加密数据和过滤词汇都可以是数据持久化的装饰功能, 但若先加密了数据再用过滤功能就会出问题了, 最理想的情况, 是保证装饰类之间彼此独立, 这样他们就可以任意的顺序进行组合了.”
“是呀, 穿上西装再套上T恤实在不是设么好穿法.”
“……..”