下面说一个笔者理解了蛮久的模式,笔者基础仅一般,所以理解有些慢。各位如果和笔者相似,请debug代码一步步刨开理解。
现在有这样一个场景,类似qq秀(女生的话理解成暖暖,嘿嘿),用来换装穿衣,文字显示即可,简单代码实现,怎么做呢?
学生党并且基础不好的可能是这样:写一个persion类,属性包括姓名,方法有穿衣服,穿裤子,穿鞋子,方法实现就是打印这几个字,然后show方法用来打印人名,最后在main方法new Persion对象,对象调用穿着方法和show方法即可。
这样编写的代码属于死代码,假如要新增穿帽子方法就得修改源码的类,(三)文章里笔者写了开闭原则,意思是可以扩展但不能修改源码,上述写法就犯了这个错。
普通开发者,没有学习过设计模式的,可能这样写:抽象一个服装类接口,内有show方法,具体修饰类实现接口,最后写一个Perison类用于显示人名。
UML如下:
这样做是符合了开闭原则,但在调用时候每个具体装饰类的对象要各自调用自己的show方法,例如原书案例调用方式如下:
这样做不好,百度有一些理由大家可以搜搜,笔者就认为代码重复不好看,一个作用相同的方法干嘛调用三次呢。
所以下面就是前辈们搞出的装饰模式:动态的给一个对象添加额外的职责(功能,方法)
UML大致如下:
调用类如下:
package com.gcc.decoratorModel;
/**
* 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为
*/
public class TestDecorator {
public static void main(String[] args) {
Persion persion = new Persion("张三");
Persion cpersion = new DecoratorClothes(persion);
Persion ppersion = new DecoratorPants(cpersion);
Persion spersion=new DecoratorShoes(ppersion);
spersion.show();
}
}
Persion类如下:
package com.gcc.decoratorModel;
/**
* 定义被装饰者
*/
public class Persion {
public Persion() {
super();
}
public Persion(String name) {
System.out.print(name);
}
public void show(){};
}
装饰类如下:
package com.gcc.decoratorModel;
/**
*定义装饰者
*/
public class Decorator extends Persion {
private Persion persion;
public Decorator() {
super();
}
public Decorator(Persion persion) {
//super();//可以注解掉,也可以带上,因为再父类的构造方法没有操作,不影响结果
this.persion = persion;
}
@Override
public void show() {
persion.show();
}
}
具体装饰类如下:
package com.gcc.decoratorModel;
public class DecoratorClothes extends Decorator {
public DecoratorClothes() {
super();
}
//super()最近的一个父类,super相当于是指向当前对象的父类,super(参数),调用父类中的某一个构造函数。
public DecoratorClothes(Persion persion) {
super(persion);
}
/**
* 装饰动作
*/
@Override
public void show() {
super.show();
wearClothes();
}
private void wearClothes() {
System.out.print(" "+"穿衣服");
}
}
package com.gcc.decoratorModel;
public class DecoratorPants extends Decorator {
public DecoratorPants() {
super();
}
public DecoratorPants(Persion persion) {
super(persion);
}
@Override
public void show() {
super.show();
wearPants();
}
private void wearPants() {
System.out.print(" "+"穿裤子");
}
}
package com.gcc.decoratorModel;
public class DecoratorShoes extends Decorator {
public DecoratorShoes() {
super();
}
public DecoratorShoes(Persion persion) {
super(persion);
}
@Override
public void show() {
super.show();
wearShoes();
}
private void wearShoes() {
System.out.print(" "+"穿鞋子");
}
}
输出结果如下:
张三 穿衣服 穿裤子 穿鞋子
这里说明下执行过程,原案例是c++的,笔者翻译成了java,笔者习惯是从main方法往上追溯,去理解代码。
首先测试类new了Person对象,debug可以看到代码执行到Person类的构造方法;
然后测试类new了具体的装饰类DecoratorClothes对象并传递Person类型参数,debug看到代码进入DecoratorClothes的构造方法,内部又调用父类构造方法,即Decorator的构造方法,得到的Person对象(注:此对象也可以是Person的子类类型)赋值给类的成员变量,同时提供由成员变量调用的show方法。
接着测试类new了具体的装饰类DecoratorPants对象并传递Person类型参数,此参数是DecoratorClothes对象向上转型到Person得到的,接着步骤和上述一样。
第三次装饰行为和第二次雷同。
最后执行show方法,这时有意思的东西来了,debug执行顺序是:
1:DecoratorShoes的show方法,执行第一行super.show();
2:Decorator的show方法,执行第一行persion.show();
3:DecoratorPants的show方法,执行第一行super.show();
4:Decorator的show方法,执行第一行persion.show();
5:DecoratorClothes的show方法,执行第一行super.show();
6:Decorator的show方法,执行第一行persion.show();
7:DecoratorClothes的wearClothes方法
8:Decorator的show方法,执行最后的"}"
9:DecoratorPants的wearPants方法
10:Decorator的show方法,执行最后的"}"
11:DecoratorShoes的wearShoes方法
12:结束
笔者的基础一般,如果不深究,装饰模式已经说明完毕,通过重写装饰类的show方法,并在方法内执行需要附加的方法,完成动态的装饰效果。
如果深究,笔者有两处不太理解:
一是执行顺序,先new的对象是DecoratorClothes,为什么测试类执行show方法先调用的却是DecoratorShoes的show方法?最后调用顺序却又按照了new对象的顺序又是为什么?笔者粗略猜测是堆栈的原因,但具体要请教各位geek了。
二是测试类的new对象方式像是套用在一起,最后new得到的对象只执行了一次show方法却能分别执行三个具体装饰类的show方法,这是为什么,不太理解原因,是java的哪个特性做到的?多态?和是不是单例有关吗?