问题描述:实现一个可以给人搭配不同的服饰的系统。
先来看看比较随意的一版:
直观但是很差的实现
//Person类
namespace zhuangshimoshi
{
class Person
{
private string name;
public Person(string name)
{
this.name = name;
}
public void WearTShirts()
{
Console.Write("大T恤");
}
public void WearBigTrouser()
{
Console.Write("垮裤");
}
public void WearSneakers()
{
Console.Write("破球鞋");
}
public void WearSuit()
{
Console.Write("西装");
}
public void WearTie()
{
Console.Write("领带");
}
public void WearLeatherShoes()
{
Console.Write("皮鞋");
}
public void Show()
{
Console.WriteLine("装扮的{0}", name);
}
}
//客户端代码
static void Main(string[] args)
{
Person xc = new Person("小菜");
Console.WriteLine("\n第一种装扮:");
xc.WearTShirts();
xc.WearBigTrouser();
xc.WearSneakers();
xc.Show();
Console.WriteLine("\n第二种装扮:");
xc.WearSuit();
xc.WearTie();
xc.WearLeatherShoes();
xc.Show();
Console.Read();
}
}
观察这一版,虽说简单的完成了需求,但是违背了最基本的开放-封闭原则,例如需要添加新的装扮,就需要对于Person类进行修改,这是十分不可取的。
考虑符合开放-封闭原则,将各种服饰都写成子类,可以得到如下修改过的版本:
针对开放-封闭原则进行修改的实现
//Person类
class Person
{
private string name;
public Person(string name)
{
this.name = name;
}
public void Show()
{
Console.WriteLine("装扮的{0}", name);
}
}
//服饰抽象类
abstract class Finery
{
public abstract void Show();
}
//各种服饰子类
class TShirts : Finery
{
public override void Show()
{
Console.Write("大T恤");
}
}
class BigTrouser : Finery
{
public override void Show()
{
Console.Write("垮裤");
}
}
class Sneakers : Finery
{
public override void Show()
{
Console.Write("破球鞋");
}
}
class Suit : Finery
{
public override void Show()
{
Console.Write("西装");
}
}
class Tie : Finery
{
public override void Show()
{
Console.Write("领带");
}
}
class LeatherShoes : Finery
{
public override void Show()
{
Console.Write("皮鞋");
}
}
//客户端代码
static void Main(string[] args)
{
Person xc = new Person("小菜");
Console.WriteLine("\n第一种装扮:");
Finery dtx = new TShirts();
Finery kk = new BigTrouser();
Finery pqx = new Sneakers();
dtx.Show();
kk.Show();
pqx.Show();
xc.Show();
Console.WriteLine("\n第二种装扮:");
Finery xz = new Suit();
Finery ld = new Tie();
Finery px = new LeatherShoes();
xz.Show();
ld.Show();
px.Show();
xc.Show();
Console.Read();
}
上述代码通过修改,实现了服饰与人的分离,如需增加装扮,只要添加服饰子类即可。但是,观察客户端实现,仍然存在不合理之处。可以看到对于每一个服饰,实现方法是一个一个打印出来的。书上举了一个很形象的例子,就是类似于光着身子,大庭广众之下一件一件地穿衣服,这是不合适的。另外还有一点需要注意的是,我们不光需要把穿衣服的过程隐藏起来,同时穿衣服搭配的过程也是不固定的。也就是说,我们需要把所需的功能按正确的顺序串联起来进行控制。听上去似乎很复杂,这里就要引出装饰模式。
装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。下面给出装饰模式的基本代码实现。
装饰模式的基本代码
//Component类
abstract class Component
{
public abstract void Operation();
}
//ConcreteComponent类
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("具体对象的操作");
}
}
//Decorator类
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
{
component.Operation();
}
}
}
//ConcreteDecoratorA类
class ConcreteDecoratorA : Decorator
{
private string addedState;
public override void Operation()
{
base.Operation();
addedState = "New State";
Console.WriteLine("具体装饰对象A的操作");
}
}
class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("具体装饰对象B的操作");
}
private void AddedBehavior()
{
}
}
//客户端代码
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();
Console.Read();
}
上述代码中,Component是定义一个对象接口,可以给这些对象动态地添加职责。ConcreteComponent是定义了一个具体地对象,也可以给这个对象添加一些职责。Decorator,装饰对象类,继承了Component,从外类来扩展Component类地功能,但对于Component来说,是无需知道Decorator的存在的。至ConcreteDeco-rator就是具体的装饰对象,起到给Component添加职责的功能。
装饰模式利用SetComponent来对对象进行包装,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链中。利用装饰模式改写的程序如下:
采用装饰模式的实现
//Person类
class Person
{
public Person()
{ }
private string name;
public Person(string name)
{
this.name = name;
}
public virtual void Show()
{
Console.WriteLine("装扮的{0}", name);
}
}
//服饰类
class Finery : Person
{
protected Person component;
public void Decorate(Person component)
{
this.component = component;
}
public override void Show()
{
if (component != null)
{
component.Show();
}
}
}
//具体服饰类
class TShirts : Finery
{
public override void Show()
{
Console.Write("大T恤");
base.Show();
}
}
class BigTrouser : Finery
{
public override void Show()
{
Console.Write("垮裤");
base.Show();
}
}
class Sneakers : Finery
{
public override void Show()
{
Console.Write("破球鞋");
base.Show();
}
}
class Suit : Finery
{
public override void Show()
{
Console.Write("西装");
base.Show();
}
}
class Tie : Finery
{
public override void Show()
{
Console.Write("领带");
base.Show();
}
}
class LeatherShoes : Finery
{
public override void Show()
{
Console.Write("皮鞋");
base.Show();
}
}
//客户端代码
static void Main(string[] args)
{
Person xc = new Person("小菜");
Console.WriteLine("\n第一种装扮:");
Sneakers pqx = new Sneakers();
BigTrouser kk = new BigTrouser();
TShirts dtx = new TShirts();
pqx.Decorate(xc);
kk.Decorate(pqx);
dtx.Decorate(kk);
dtx.Show();
Console.WriteLine("\n第二种装扮:");
LeatherShoes px = new LeatherShoes();
Tie ld = new Tie();
Suit xz = new Suit();
px.Decorate(xc);
ld.Decorate(px);
xz.Decorate(ld);
xz.Show();
Console.Read();
}
以上代码便利用装饰模式实现了需要的功能。
装饰模式是为已有功能动态地添加更多功能地一种方式。当需要向主类中加入新的字段,新的方法和新的逻辑,且这些新加入的东西仅仅是为了满足一些只再某种特定情况下才会执行的特殊行为的需要。可以考虑采用装饰模式来解决,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要修饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
总而言之,装饰模式的优点是把类中的装饰功能从类中搬移去除,这样可以简化原有的类。这样的好处是有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。
参考书籍:《大话设计模式》