接着上回的“针对接口编程,不针对实现编程”原则说。
假设开始我们设计了一个鸭子基类,发声、游泳和显示外观是鸭子类的功能。发声和游泳是通用的方法,放在基类实现,显示外观由于鸭子的不同而不同,放在继承类实现。可以设计出下面的类结构。
public abstract class DuckBase
{
public virtual void Quack()
{
Console.WriteLine("我会嘎嘎叫");
}
public virtual void Swin()
{
Console.WriteLine("我会游泳");
}
public abstract void Display();
}
public class GreenHeadDuck : DuckBase
{
public override void Display()
{
Console.WriteLine("我是绿头鸭子");
}
}
public class RedHeadDuck : DuckBase
{
public override void Display()
{
Console.WriteLine("我是红头鸭子");
}
}
看起来还是比较OO的,利用了继承,复用了游泳和发声的代码。可是突然有一天需要创新,提出鸭子也应该可以飞,有人就提出来“这还不简单,在基类上加一个方法,鸭子都会飞了”。
public abstract class DuckBase
{
public virtual void Quack()
{
Console.WriteLine("我会嘎嘎叫");
}
public virtual void Swin()
{
Console.WriteLine("我会游泳");
}
public abstract void Display();
public virtual void Fly()
{
Console.WriteLine("我是鸭子,我会飞");
}
}
貌似解决了鸭子会飞的问题。可是有一天问题来了,橡皮鸭子怎么会飞呢?它没有生命啊?
怎么回事呢?就是由于我们在基类添加的实现方法,继承类继承了这个方法,所有各种类型的鸭子都会飞了。可是还是有一些是不应该飞的,就像橡皮鸭子,木头鸭子等等。看来为了复用“飞”,而使用继承,结局并不理想。
当然了,也有解决的办法。既然是基类的方法,我们可以在继承类中重写它,如果不会飞就什么都不做?发声也出现了类似问题,不是每种鸭子都“嘎嘎”叫的,有的是“吱吱”发声,好吧,重写发声方法,以前的呢?都检查一遍,以后的新类型鸭子也需要注意叫声和是否会飞,好像有点麻烦?
public class RubberDuck : DuckBase
{
public override void Display()
{
Console.WriteLine("我是橡皮鸭子");
}
public override void Quack()
{
Console.WriteLine("我会咯吱咯吱叫");
}
public override void Fly()
{
Console.WriteLine("我不会飞");
}
}
这样的继承造成了以下几个问题:
- 鸭子不能同时飞和发声。
- 改变牵一发,而动全身,其他类型的鸭子平白无故的多了一些不需要的功能。如果确实不要还要修改其他类型鸭子的代码。
有人又提出来了,利用接口吧。将会飞和发声写两个接口,然鸭子类去实现,实现了接口的就具有了这两个功能,没有实现的就不受影响。
public abstract class DuckBase
{
public virtual void Swin()
{
Console.WriteLine("我会游泳");
}
public abstract void Display();
}
public class GreenHeadDuck : DuckBase,IFlyBehavior,IQuackBehavior
{
public override void Display()
{
Console.WriteLine("我是绿头鸭子");
}
public void Fly()
{
throw new NotImplementedException();
}
public void Quack()
{
throw new NotImplementedException();
}
}
public class RedHeadDuck : DuckBase, IFlyBehavior, IQuackBehavior
{
public override void Display()
{
Console.WriteLine("我是红头鸭子");
}
public void Quack()
{
throw new NotImplementedException();
}
public void Fly()
{
throw new NotImplementedException();
}
}
public class RubberDuck : DuckBase
{
public override void Display()
{
Console.WriteLine("我是橡皮鸭子");
}
public override void Quack()
{
Console.WriteLine("我会咯吱咯吱叫");
}
}
public interface IFlyBehavior
{
void Fly();
}
public interface IQuackBehavior
{
void Quack();
}
这样还是有点问题。就是很是很多的鸭子都会飞的,都会叫的,这部分的鸭子的飞和叫的代码就会重复,而且以前现有鸭子都需要检查一遍,都可能需要修改。接口虽然解决了只有继承才会具有相应的功能,但是因为接口没有实现,只有方法体的定义,所以造成了代码没有得到复用。表面看起来这两者好像是矛盾的。
引出另外一个设计原则:
找出应用中可能需要变化的地方,把他们独立出来,不要和那些不需要变化的代码混合在一起。把会变化的代码“封装”起来,好让修改的时候不影响其他部分。使得代码的修改对系统的影响最小,使得系统更具有弹性。
也就是说每次有新的需求,都会造成一部分代码的变化,将这部分变化的代码抽象出来,保持其他代码的稳定性。让系统的某部分修改不影响其他部分,这样其他部分就不需要测试,否则就需要全部测试一遍才可以保证代码的修改是正确的。
上面这个列子中的飞和叫是变化的地方,其他的没有太大变化。飞也有很多种方式,叫也有很多种“声音”。这两种叫做“行为”更好。原来的鸭子类,这两种行为是写死的,能否在初始化鸭子的时候,自己指定行为的方式呢?可以,开放一个属性,或者是一个方法,可以设置定义在鸭子基类中的行为就可以了,或者在构造函数中添加参数,指定行为。
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BeautyCode.DesignPattern.Head.First
{
public abstract class DuckBase1
{
private IFlyBehavior1 _flyBehavior;
public IFlyBehavior1 FlyBehavior
{
get { return _flyBehavior; }
set { _flyBehavior = value; }
}
private IQuackBehavior1 _quackBehavior;
public IQuackBehavior1 QuackBehavior
{
get { return _quackBehavior; }
set { _quackBehavior = value; }
}
public virtual void Swin()
{
Console.WriteLine("我会游泳");
}
public void PerformQuack()
{
_quackBehavior.Quack();
}
public void PerformFly()
{
_flyBehavior.Fly();
}
public abstract void Display();
}
public class GreenHeadDuck1 : DuckBase1
{
public override void Display()
{
Console.WriteLine("我是绿头鸭子");
}
}
public class RedHeadDuck1 : DuckBase1
{
public override void Display()
{
Console.WriteLine("我是红头鸭子");
}
}
public class RubberDuck1 : DuckBase1
{
public override void Display()
{
Console.WriteLine("我是橡皮鸭子");
}
}
public interface IFlyBehavior1
{
void Fly();
}
public class FlyWithWings : IFlyBehavior1
{
public void Fly()
{
Console.WriteLine("有翅膀飞了");
}
}
public class FlyNoWay : IFlyBehavior1
{
public void Fly()
{
Console.WriteLine("没有也飞了");
}
}
public interface IQuackBehavior1
{
void Quack();
}
public class QuackA : IQuackBehavior1
{
public void Quack()
{
Console.WriteLine("嘎嘎");
}
}
public class Squeak : IQuackBehavior1
{
public void Quack()
{
Console.WriteLine("吱吱");
}
}
public class MuteQuack : IQuackBehavior1
{
public void Quack()
{
Console.WriteLine("哇哇");
}
}
}
调用代码duck.FlyBehavior = new Head.First.FlyWithWings();
duck.PerformFly();
【Blog】http://virusswb.cnblogs.com/
【说明】转载请标明出处,谢谢