HeadFirst设计模式入门(三)

根据V 0.0.0 .2需求封装的类,此时需求再次变化———V0.0.0.3,要求有一个橡皮鸭子

此时,设计类图为:

 

 

其中,叫声不再是呱呱而是吱吱

代码为:

public class RubberDuck:Duck

    {

        public override  void display()

        {

            Console.WriteLine("我是一个橡皮鸭");

        }

       public new void quack()

       {

           Console.WriteLine("我吱吱叫");

       }

}

测试类:

            #region 测试绿头鸭

            MallarDuck md = new MallarDuck();

            md.display();

            md.quack();

            md.swim();

            md.fly();

            #endregion

            Console.WriteLine(" ");

            #region 测试红头鸭

            RedHeadDuck rd = new RedHeadDuck();

            rd.display();

            rd.quack();

            rd.swim();

            rd.fly();

            #endregion

            Console.WriteLine(" ");

            #region 测试橡皮鸭

            RubberDuck rd1 = new RubberDuck();

            rd1.display();

            rd1.quack();

            rd1.swim();

            rd1.fly();

            #endregion

            Console.ReadKey();

显示:

 

发现塑料鸭也飞起来了,这不符合需求。

现在的解决方法是:

把超类Duckfly()方法重载掉。即:

public class RubberDuck:Duck

    {

        public override  void display()

        {

            Console.WriteLine("我是一个橡皮鸭");

        }

       public new void quack()

       {

           Console.WriteLine("我吱吱叫");

       }

       public new void fly()

       {

       }

}

此时,逻辑上是正确的。

但是,如果由于需求始终在变,如果每次增加一个Duck的子类,都需要去考虑覆盖对应的方法,一旦忘记覆盖或者覆盖错误,那么将是一场噩梦。那么彻底放弃该设计,我们再次重新开始。

此时,想到了一个稍好一些的设计

将经常变化的fly()方法和quack()方法抽象成为接口,这样不同的子类继承接口,通过不同的程序实现能解决一些问题,例如橡皮鸭不能fly(不让RubberDuck类实现Flyable接口即可)

对于设计图如下:

  

 

以上是MallarDuck绿头鸭类的实现,可见MallarDuck继承Duck,实现FlyableQuackable接口。

代码为:

class MallarDuck:Duck,Flyable,Quackable

    {

        public void quack()

        {

            Console.WriteLine("我呱呱叫");

        }

        public override void display()

        {

            Console.WriteLine("我显示为绿头鸭");

        }

        public void fly()

        {

            Console.WriteLine("我可以飞起来");

    }

}

显示为:

 

此时,添加红头鸭类,设计如下:

 

 

 代码为:

public class RedHeadDuck:Duck,Flyable,Quackable

    {

       public override void display()

       {

           Console.WriteLine("我显示为红头鸭");

       }

       public void quack()

       {

           Console.WriteLine("我呱呱叫");

       }

       public void fly()

       {

           Console.WriteLine("我可以飞起来");

       }

 }

测试显示:

 

再添加塑料鸭,不同之处在于塑料鸭不能飞,因此不要实现Flyable接口

 

 

代码:

public class RubberDuck:Duck,Quackable

    {

        public override void display()

        {

            Console.WriteLine("我是一个橡皮鸭");

        }

        public new void quack()

        {

            Console.WriteLine("我吱吱叫");

        }

}

测试显示:

 

逻辑上,完全合乎需求,并且解决了RubberDuck类继承超类而不得不覆盖掉fly()方法的问题(通过不让RubberDuck类实现Flyable接口)

这时,我们会发现使用接口带来的弊端:

对于实现Flyable接口以及Quackable接口的类,在实现时不得不对相同的功能,多次重复编写代码,无法复用,例如:

RedHeadDuck类和MallarDuck类都有如下代码:

public void quack()

       {

           Console.WriteLine("我呱呱叫");

       }

       public void fly()

       {

           Console.WriteLine("我可以飞起来");

       }

使用接口并实现这些接口,虽然不会出现没有覆盖基类方法而出现的逻辑错误,这种使用“依赖”来实现,必然导致无法复用,如果当基类很多时,这个设计就又会出现代码无法复用的缺点。

下个要解决的问题就是如何复用了,来根烟或者来杯咖啡,心脏不好的就来个救心丸,再次彻底放弃上边的设计。

这次,我们不再用Duck的子类来实现Flyable接口、Quackable接口,而是由一组行为类来实现,这组类将所有flyquack的方法统一实现 ,而不是由鸭子子类来实现,而是在鸭子子类中调用这些实现,这样复用了代码。

行为类的设计:

超类Duck设计为:

 

代码为:

public abstract class Duck

    {

        public  FlyBehavior flyBehavior;

        public  QuackBehavior quackBehavior;

        public void swim()

        {

            Console.WriteLine("我可以游泳");

        }

        public abstract void display();

        public void performQuack()

        {//所有鸭子都叫,至于是呱呱、吱吱还是无声,在基类中确定

            quackBehavior.quack();

        }

        public void performFly()

        {//所有鸭子都飞,至于是真飞还是不飞,在基类中确定

            flyBehavior.fly();

    }

}

现在创建绿头鸭类:

 

代码为:

class MallarDuck:Duck

    {

        public MallarDuck()

        {

            //指定绿头鸭是真飞

            flyBehavior = new FlyWithWings();

            //指定绿头鸭是呱呱叫

            quackBehavior = new Quack();

        }

        public override void display()

        {

            Console.WriteLine("我显示为绿头鸭");

        }

    }

测试显示为:

 

同样,添加红头鸭

  

代码为:

class RedHeadDuck:Duck

      {

        public RedHeadDuck()

        {

            //红头鸭指定为真飞

            flyBehavior = new FlyWithWings();

            //红头鸭指定为吱吱叫

            quackBehavior = new Squeak();

        }

        public override void display()

        {

            Console.WriteLine("我显示为红头鸭");

        }

  }

不再添加RubberDuck,自此我们可以看出,子类中不再出现重复的业务功能代码,这种设计大大提高了代码的复用性。

以绿头鸭为例,看一下程序的执行过程:
(1)
Duck  md = new MallarDuck(),调用MallarDuck的构造函数,

此时,将该类中的两个字段赋值:

public MallarDuck()

        {

            flyBehavior = new FlyWithWings();

            quackBehavior = new Quack();

}

此时, flyBehaviorquackBehavior持有FlyWithWings()Quack()类实例的引用。

(2) 调用md.display()方法,执行MallarDuck类中重载的display()方法

public override void display()

        {

            Console.WriteLine("我显示为绿头鸭");

    }

(3) 调用md.performQuack(),由于MallarDuck类没有重载该方法,因此调用其父类方法

public void performQuack()

        {

            quackBehavior.quack();

}

       由于quackBehavior持有Quack()类实例的引用,因此调用Quack类中的实现了

        class Quack:QuackBehavior

        {

        public void quack()

        {

            Console.WriteLine("我呱呱叫");

        }

        }

 这个设计看上去确实不错,你不会再需要为所有子类,编写大量重复的代码了。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值