需求
写一个给人模拟搭配不同服饰的程序,可以给人换各种各样的衣服裤子的形象。
初步实现
需求比较简单,直接上代码:
public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
public void wearTShirt(){
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);
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
Person person = new Person();
person.setName("小明");
person.wearTShirt();
person.wearBigTrouser();
person.wearSneakers();
person.show();
person.wearSuit();
person.wearTie();
person.wearLeatherShoes();
person.show();
}
}
运行结果:
分析
通过前面几节的介绍,可以发现这种写法的显著毛病:难以实现功能拓展。比如现在不仅仅只有这两种搭配方式,额外需要添加其他的搭配方式,该怎么办呢?这种写法必然需要对Person类进行手术,违反了开闭原则。
改进实现
很容易结合前面的实例,我们利用面向对象的程序设计方式,通过创造服饰抽象类,让其他具体的西装、领带、垮裤等来继承,实现程序功能的可扩展。
UML类图:
Person类实现:
public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
public void show() {
System.out.println("装扮的" + name);
}
}
服饰基类:
public abstract class Finery {
public abstract void show();
}
其他具体衣服的实现,以大T恤、西装为例:
public class TShirt extends Finery {
@Override
public void show() {
System.out.println("大T恤");
}
}
public class Suit extends Finery {
@Override
public void show() {
System.out.println("西装");
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
Person person = new Person();
person.setName("小明");
Finery xz = new Suit();
Finery ld = new Tie();
Finery px = new LeatherShoes();
xz.show();
ld.show();
px.show();
person.show();
}
}
再次分析
<<大话设计模式>>49页中提及,上面这种改进方案虽然实现了服饰与人的分离,且利用面向对象程序设计的特性可以实现功能的扩展,但是仍然存在一个显著的问题——穿衣服的动作是一步一步完全暴露在外面的。
xz.show();
ld.show();
px.show();
通过在客户端,一步步的进行穿着服饰,这种设计方案是不够良好的。(但实际开发中不好在哪,我也没有深刻理解。)这些操作应该在内部组装完毕,同时也要将需要的功能(服饰)按照一定的顺序进行串联,不能先打领带后穿衬衫等...而装饰模式就是解决这一需求的。
装饰模式
- 定义:动态给一个对象添加一些额外的职责,使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活。
- 设计初衷:通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的。
装饰模式的使用
UML类图:
这里需要注意的是,装饰模式,装饰类与被装饰类都是继承自同一父类,究竟是为什么?这一点在客户端代码中可以体现,需要好好体会,这也是理解装饰模式的关键所在。
代码实现:
-
Component抽象类——被装饰者的抽象类,实际中也不一定有此类:
public abstract class Component { public abstract void meathod(); }
-
具体的被装饰者类:
public class ConcreteComponent extends Component { @Override public void meathod() { System.out.println("具体要被装饰的对象的方法"); } }
-
Decorator抽象类——装饰者抽象类:
public abstract class Decorator extends Component { private Component component; public void setComponent(Component component) { this.component = component; } @Override public void meathod() { if (component != null) component.meathod(); } }
-
具体的装饰类A,B:
public class ConcreteDecoratorA extends Decorator { private void addNewMethodOfA(){ //A装饰器的作用 System.out.println("A装饰器的作用"); } @Override public void meathod() { super.meathod(); addNewMethodOfA(); } }
public class ConcreteDecoratorB extends Decorator { private void addNewMethodOfB(){ //B装饰器的作用 System.out.println("B装饰器的作用"); } @Override public void meathod() { super.meathod(); addNewMethodOfB(); } }
-
客户端类:
public class Client { public static void main(String[] args) { Component component = new ConcreteComponent(); Decorator decoratorA = new ConcreteDecoratorA(); Decorator decoratorB = new ConcreteDecoratorB(); decoratorA.setComponent(component); decoratorB.setComponent(decoratorA); decoratorB.meathod(); } }
总结:
- 装饰模式是用setComponent()方法来进行装饰,其实也就是功能扩展;
- 每个装饰类装饰什么样的功能和这个装饰类如何被调用进行装饰是分离的,即装饰类A/B能完成什么样的装饰与他们什么时候被调用、按照什么顺序是无关的。
利用装饰模式实现换衣
UML类图:
代码实现:
-
Person类——被装饰者;
public class Person { private String name; public void setName(String name) { this.name = name; } public void show() { System.out.println("装扮的" + name); } }
-
Finery类——抽象装饰类;
public abstract class Finery extends Person { private Person person; public void setPerson(Person person) { this.person = person; } @Override public void show() { if (person != null) person.show(); } }
-
其他具体装饰类;
public class BigTrousers extends Finery { @Override public void show() { super.show(); System.out.println("垮裤"); } }
-
客户端类;
public class Client { public static void main(String[] args) { Person person = new Person(); person.setName("小明"); Finery xz = new Suit(); Finery ld = new Tie(); Finery px = new LeatherShoes(); xz.setPerson(person); ld.setPerson(xz); px.setPerson(ld); px.show(); } }
-
运行结果:
装饰模式小结
- 装饰模式是为已有功能动态的添加更多功能的一种方式;
- 装饰模式把类中的装饰功能从类中搬移去除,这样可以简化原有的类。同时有效的把类的核心职责和装饰功能区分开,而且可以 去除相关类中重复的装饰逻辑 ;(后半句话怎么理解呢?望评论出你的看法!)
- 当系统需要新功能时,是向旧的类中添加新的代码。这些新的代码通常装饰了原有类的核心职责或主要行为。但这种做法的问题在于,他们在主类中加入了新的字段、新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为的需要。装饰模式提供了一种非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以根据需要、有选择按顺序的使用装饰功能包装对象;
- 对于前面所说为什么装饰模式,被装饰者与装饰者都要继承同一父类,这一点我觉得相当于给人穿了件衣服后返回的新对象毕竟本质还是人,只是多了件衣服,这样就可以不断的穿衣服不断的返回。