eaglet

本博专注于基于微软技术的搜索相关技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

从动物叫三声,谈谈设计模式

Posted on 2008-08-15 09:41  eaglet  阅读(3376)  评论(15编辑  收藏  举报

从动物叫三声,谈谈设计模式

 

作者:肖波

 

         今天在博问中看到一个动物叫三声的设计模式讨论,觉得很有意思,抽空实现了一下,觉得有必要写下来,和大家一起探讨探讨。

         原始需求:http://space.cnblogs.com/question/2360/ 我把原文转贴如下:

         我有一个动物类,它只有一个“叫三声”的方法,然后猫类与狗类都从它继承而来。现在我想猫应该“喵喵喵”叫,狗应该“汪汪汪”叫,我是用如下代码实现,但,有没有更好的方法,或是一些能用得上设计模式的方法(我是为设计模式而设计模式)。

         从这篇博问的回复中,很多意见是使用抽象方法描述 基类 animal 中的shout 方法,不同动物对这个方法进行重载,以达到用不同方式叫的目的。

         我谈谈我的设计:

         首先我们看到这个需求中有两个重要概念:

          一个是叫,还有一个是叫声。

         Shout(叫)是一个动作,不同动物有不同的叫声,但只要是动物就会叫(不会叫的动物不在这里讨论,原始需求中并没有要求),另外原始需求中要求叫就是

叫三声,所以怎么叫也规定了。显然把Shout这个方法应该放到基类中。

         Sound(叫声)是一个属性,不同动物有不同的叫声,同一个动物不同情况下也可能会发出不同的叫声。叫声不是动作,因为不张口,有声音也出不来。所以这里应该把叫声这个概念单独提取出来作为一个属性。

         下面看我的代码:

        

    /// <summary>
    
/// 叫声属性
    
/// </summary>
    [AttributeUsage(AttributeTargets.All)]
    
class SoundsAttribute : Attribute
    {
        
string _Sounds;

        
/// <summary>
        
/// 叫声
        
/// </summary>
        public string Sounds
        {
            
get
            {
                
return _Sounds;
            }
        }

        
public SoundsAttribute(string sounds)
        {
            _Sounds 
= sounds;
        }
    }

        上面代码是叫声属性,采用属性(Attribute)来描述,而不是特性(Property),可以时程序看起来更优雅。

 

    /// <summary>
    
/// 动物类
    
/// </summary>
    abstract class Animal
    {
        
private SoundsAttribute m_AnimalSounds;

        
/// <summary>
        
/// 获取动物的一般叫声
        
/// </summary>
        public Animal()
        {
            
//获取当前对象的类型
            Type myType = this.GetType();

            
//获取一般叫声
            foreach (Attribute attr in myType.GetCustomAttributes(true))
            {
                
if (attr is SoundsAttribute)
                {
                    m_AnimalSounds 
= attr as SoundsAttribute;
                }
            }
        }

        
/// <summary>
        
/// 叫
        
/// </summary>
        public void Shout()
        {
            SoundsAttribute animalSounds 
= m_AnimalSounds;

            
//叫三声
            if (animalSounds != null)
            {
                
for (int i = 0; i < 3; i++)
                {
                    Console.Write(animalSounds.Sounds);
                }
            }

            Console.WriteLine();
        }
    }

 上面代码是动物类,由于动物本身是一个抽象概念,所以在设计时最好和自然规律吻合,将其设计为抽象类应该是不错的选择。

 

    [Sounds("")]
    
class Cat : Animal
    {
    }

    [Sounds(
"")]
    
class Dog : Animal
    {
    }

  猫猫狗狗

    class Program
    {
        
static void Main(string[] args)
        {
            
//
            Cat cat = new Cat();
            
//猫叫三声
            cat.Shout();

            
//
            Dog dog = new Dog();
            
//狗叫三声
            dog.Shout();

        }
    }

 猫叫三声,够叫三声

 结果:

 喵喵喵

 汪汪汪

 

写到这边,基本的设计思路和程序实现已经完成。

下面我们来扩展一下需求,看看当前的设计架构是否可以很好的适应需求的变化。

第一,我们希望有个别的动物,叫的时候不是叫三声,而是采取一些特殊的叫法。要做到这点很简单,我们只要将

Animal类中的Shout 方法改为virtual(虚拟)方法,那么派生出来的动物如果不想采用这种方法叫,就可以覆盖这个

虚拟方法,实现其特殊的叫法。

 

第二,狗在Frenzy(狂怒)时,往往不是发出"汪汪"的叫声,可能是那种低沉的"吼吼"的叫声,现在我们要为狗的狂怒设计一个

方法。由于只是发出的声音不同,我们希望依然使用Animal 类中的Shout方法来发出叫声,但声明Frenzy这个方法采用

"吼"这种叫声。


下面看代码,我们将Shout 方法修改如下:

        /// <summary>
        
/// 叫
        
/// </summary>
        virtual public void Shout()
        {
            SoundsAttribute animalSounds 
= null;

            Type myType 
= this.GetType();

            
//获取上一个调用函数
            System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1);
            MethodBase methodBase 
= stackFrame.GetMethod();

            
//获取特殊叫声
            foreach (Attribute attr in methodBase.GetCustomAttributes(true))
            {
                
if (attr is SoundsAttribute)
                {
                    animalSounds 
= attr as SoundsAttribute;
                }
            }

            
//如果没有特殊叫声,用一般叫声叫
            animalSounds = animalSounds == null ? m_AnimalSounds : animalSounds;

            
//叫三声
            if (animalSounds != null)
            {
                
for (int i = 0; i < 3; i++)
                {
                    Console.Write(animalSounds.Sounds);
                }
            }

            Console.WriteLine();
        }
    }

 

 我们再为狗这个类添加一个Frenzy(狂怒)方法

 

    [Sounds("")]
    
class Dog : Animal
    {
        
/// <summary>
        
/// 狗狂怒
        
/// </summary>
        [Sounds("")]
        
public void Frenzy()
        {
            Shout();
        }
    }

 

 狗要叫了...... 好怕....

            //
            Dog dog = new Dog();
            
//狗叫三声
            dog.Shout();
            
//狗狂叫三声
            dog.Frenzy();

 结果:

汪汪汪

吼吼吼

 

 

下面给出全部完整的代码

 

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace TestCosole
{
    
/// <summary>
    
/// 叫声属性
    
/// </summary>
    [AttributeUsage(AttributeTargets.All)]
    
class SoundsAttribute : Attribute
    {
        
string _Sounds;

        
/// <summary>
        
/// 叫声
        
/// </summary>
        public string Sounds
        {
            
get
            {
                
return _Sounds;
            }
        }

        
public SoundsAttribute(string sounds)
        {
            _Sounds 
= sounds;
        }
    }


    
/// <summary>
    
/// 动物类
    
/// </summary>
    abstract class Animal
    {
        
private SoundsAttribute m_AnimalSounds;

        
/// <summary>
        
/// 获取动物的一般叫声
        
/// </summary>
        public Animal()
        {
            
//获取当前对象的类型
            Type myType = this.GetType();

            
//获取一般叫声
            foreach (Attribute attr in myType.GetCustomAttributes(true))
            {
                
if (attr is SoundsAttribute)
                {
                    m_AnimalSounds 
= attr as SoundsAttribute;
                }
            }
        }

        
/// <summary>
        
/// 叫
        
/// </summary>
        virtual public void Shout()
        {
            SoundsAttribute animalSounds 
= null;

            Type myType 
= this.GetType();

            
//获取上一个调用函数
            System.Diagnostics.StackFrame stackFrame = new System.Diagnostics.StackFrame(1);
            MethodBase methodBase 
= stackFrame.GetMethod();

            
//获取特殊叫声
            foreach (Attribute attr in methodBase.GetCustomAttributes(true))
            {
                
if (attr is SoundsAttribute)
                {
                    animalSounds 
= attr as SoundsAttribute;
                }
            }

            
//如果没有特殊叫声,用一般叫声叫
            animalSounds = animalSounds == null ? m_AnimalSounds : animalSounds;

            
//叫三声
            if (animalSounds != null)
            {
                
for (int i = 0; i < 3; i++)
                {
                    Console.Write(animalSounds.Sounds);
                }
            }

            Console.WriteLine();
        }
    }

    [Sounds(
"")]
    
class Cat : Animal
    {
    }

    [Sounds(
"")]
    
class Dog : Animal
    {
        
/// <summary>
        
/// 狗狂怒
        
/// </summary>
        [Sounds("")]
        
public void Frenzy()
        {
            Shout();
        }
    }

    
class Program
    {
        
static void Main(string[] args)
        {
            
//
            Cat cat = new Cat();
            
//猫叫三声
            cat.Shout();

            
//
            Dog dog = new Dog();
            
//狗叫三声
            dog.Shout();
            
//狗狂叫三声
            dog.Frenzy();
        }
    }
}