由装饰模式巩固继承

最近学习《大话设计模式》,在学习的过程中又加深了对OO的理解,诸如abstract和virtual的区别之类的问题有了清晰的认识。今天通过装饰模式,重温OO的继承,巩固子类于父类的构造函数调用,子类调用父类方法等问题。

先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person
{
public Person()
{
Console.WriteLine("Person“);
}
}
class Man : Person
{
public Man()
{
Console.WriteLine("Man");
}

}

然后我们在主函数中实例化子类Man

1
Man jack = new Man();

结果会怎样?学习过OO的新手都知道结果是

在实例化Man的时候却优先带出了父类Person,这是因为Man作为子类,将继承父类Person的方法(当然,是共有的,即public,若是爸爸的存折,小孩子当然不能拿过来玩),也就是子类实例化时,把父类能够继承的方法、属性”拷贝“到自身为己用。这是一个倒推的过程,从金字塔的底端一直找到最高端,然后从上到下”展现“出来。

简单的例子便于了解,而复杂一点的就不太容易看明白了,下面请看:

装饰模式,动态的给一个对象添加一些额外的职责,就增加功能来说,设计模式生成子类更为灵活。

源代码引用自《大话设计模式》

Person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person
{
string name;

public Person()
{
}

public Person(string name)
{
this.name = name;
}

public virtual void show()
{
Console.WriteLine("装扮的{0}",name);
}
}

Finery类:(继承Person)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Finery : Person
{
protected Person component;

public Finery()
{
}
public void Decorator(Person component)
{
this.component = component;
}

public override void show()
{
if (component != null)
{
component.show();
}
}

杂类: (继承Finery)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class LeatherShoes : Finery
{
public override void show()
{
Console.WriteLine("LeatherShoes");
base.show();
}
}

class BigTrouser : Finery
{
public override void show()
{
Console.WriteLine("Big Trouser");
base.show();
}
}

class Tshirt : Finery
{
public override void show()
{
Console.WriteLine("T-shirt");
base.show();
}
}

先分析一下,LeatherShoes,BigTrouser和Tshirt均继承自Finery,也就是说对这三个类实例化时,先调用的是Finery的构造函数。Finery中的Decorator方法由于是public,则同样被“拷贝”到各子类中为它们所用。而Finery又继承自Person类,当Finery构造函数调用时,又优先调用了Person的构造函数,并“拷贝”了Person的方法。

最后的结果应该是:

实例化LeatherShoes,BigTrouser和Tshirt任一个时

Person的构造函数被调用(是无参数的)

Person的show方法作为虚方法被拷贝至Finery

Finery的构造函数被调用

Finery重写了虚方法show

子类LeatherShoes,BigTrouser和Tshirt 的构造函数被调用

子类LeatherShoes,BigTrouser和Tshirt重写了虚方法show

即是一个倒推,顺寻展示的过程

在子类LeatherShoes,BigTrouser和Tshirt中的show方法中我们可以看到

1
2
Console.WriteLine("***");  
base.show();

这样的语句有何作用?

同实例化一样,也是倒推的过程,只不过这次针对的不是构造函数,而是show这个方法了。

具体完整的看一遍:

1
2
3
4
5
6
7
8
9
10
11
Person my = new Person("Sonic");

Console.WriteLine("Decorator one:");
LeatherShoes qx = new LeatherShoes();
BigTrouser kk = new BigTrouser();
Tshirt dtx = new Tshirt();

qx.Decorator(my);
kk.Decorator(qx);
dtx.Decorator(kk);
dtx.show();

Person的实例化传递了参数“Sonic”,这时调用的是Person的带参数构造函数

1
2
3
4
5
6
7
public Person(string name)        
{
this.name = name;
}
LeatherShoes qx = new LeatherShoes();
BigTrouser kk = new BigTrouser();
Tshirt dtx = new Tshirt();

三个子类的实例化,其倒推和展示过程已经讲过

1
qx.Decorator(my);  

这是LeatherShoes的对象 qx 继承了父类Finery的方法Decorator,参数是Person的对象my。

来看一下Finery的Decorator是做什么的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected Person component;

public void Decorator(Person component)
{
this.component = component;
}

public override void show()
{
if (component != null)
{
component.show();
}
}

首先定义一个Person对象component,这时 component 应为null,若此时我们没有传进来
qx.Decorator(my),而直接调用Finery的show,那么
component.show()是不执行的,也就是Person中的show不执行。现在我们将my传进来,component = my,将Person的两个对象建立起联系,此时让
Finery执行show,那么component的show 即 my的show,也就是名字为“Sonic”的Person将show出来。(有点绕)

当然,我们这里没有让它执行show,只是通过Decorator把参数传了进去。因为现在进行的是装饰的过程,没有装饰完是不需要展示出来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kk.Decorator(qx);  
//BigTrouser的对象kk继承了Decorator,并传递了 LeatherShoes的对象 qx
dtx.Decorator(kk);
//同理
dtx.show();
//最后装饰完毕,需要展示了,那么倒推的过程开始了:
//dtx即Tshirt的对象,dtx.show 则执行 Console.WriteLine("T-shirt"); 然后base.show();
base.show()
//是它的上一层show,dtx的上一层是谁?当然是它的父类Finery。看看 dtx.Decorator(kk);将kk赋予了Finery中的component。
//那么base.show ()就是让父类Finery中的component,即kk show出来。这样kk.show()将执行 Console.WriteLine("Big Trouser");
//然后又是base.show();
//同理,kk对qx进行了Decorator,那么此时Finery的component就是qx,base.show()就是qx.show();

//依次类推,最后到对象my,再没有任何东西可以向上推了,my.show()就是Person中的virtual方法show,它将show出名为“Sonic”的装扮。

由装饰模式巩固继承

https://wurang.net/decorator_pattern/

作者

Wu Rang

发布于

2011-07-03

更新于

2021-12-06

许可协议

评论