代码改变世界

《深入浅出设计模式-中文版》读书笔记 策略模式(二)

2010-07-02 07:25  Virus-BeautyCode  阅读(2445)  评论(5编辑  收藏  举报

  接着上回的“针对接口编程,不针对实现编程”原则说。

  假设开始我们设计了一个鸭子基类,发声、游泳和显示外观是鸭子类的功能。发声和游泳是通用的方法,放在基类实现,显示外观由于鸭子的不同而不同,放在继承类实现。可以设计出下面的类结构。

  

代码
 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("哇哇");
        }
    }

}


 

  调用代码

  

 Head.First.DuckBase1 duck = new Head.First.GreenHeadDuck1();
            duck.FlyBehavior 
= new Head.First.FlyWithWings();
            duck.PerformFly();

  如果增加新的飞行方式,也只需要添加一个飞接口的实现,然后调用代码的duck.FlyBehavior = new Head.First.FlyWithWings();这一句进行修改就可以了,指定到新实现上面,具有了新的飞行方式。