面向对象之羊村运动会续

前面的文章面向对象之羊村运动会描述了面向对象在建模过程使用从下往上的方法,分析总结了一些一般会犯的错误。这篇文章接着讨论从上往下会犯的问题

羊村要举办一个动物运动会,喜羊羊,懒羊羊以及灰太郎还有机器羊等都报名参赛。参赛的规则是谁能最快到达终点就是赢。在赛跑开始以后,大家都是一个大步往前冲。开始后只有机器羊还在准备,看看机器羊在准备什么,原来机器羊可以变形成电动车,机器羊开着车去。这个时候灰太郎想开着也行,马上就回家去把自己的飞机搬来,开着飞机去。而这时的懒羊羊就对喜羊羊对,我们别跑了,赶不上他们啦,还不如睡觉呢。喜羊羊想也是,他们一个开电动车,一个开飞机,我有孙悟空的本事就才能赢他们了。突然灵光一闪,有没有规定不可以直接到终点,也就是我不往前,反而往后到达终点(这里假设起点和终点在一起),那我就得第一。

还是和前面的一样的场景,只不过现在不先细化底层的一些类,而是从高层的一些类。比如直接抽象出动物类,动物类的方法如下:

namespace GameApplication
{
    class Animal
    {
        public string Name { get; set; }

        public void Run(){}
    }
}

 

接着用Game类表示这场运动会。Game类应该有两个典型的行为,比如比赛前的登记以及运动会开始。

namespace GameApplication
{
    class Game
    {
        private List<Animal> players;
        // 比赛前的登记
        public void Register()
        {
        }
        // 比赛开始
        public void Start()
        {
        }
    }
}

上面的Register和Start都没有具体实现。

接着我们来思考一下

比赛前的登记Register主要是参赛选手报名,那么就是相当于在Player是新建动物的对象实例然后加到列表里去。

// 比赛前的登记
   public void Register()
   {
       Animal player1 = new Animal();
       player1.Name = "喜羊羊";
       players.Add(player1);

       Animal player2 = new Animal();
       player2.Name = "灰太郎";
       players.Add(player2);

       Animal player3 = new Animal();
       player3.Name = "机器羊";
       players.Add(player3);
   }

先不看上面的问题,继续实现Game类的Start方法

// 比赛开始
       public void Start()
       {
           foreach (Animal player in players)
           {
               ??????
           }
       }

在实现Start方法的时候,就像上面的,不知道怎么写了。这个时候脑袋瓜就在想-难道通过判断名字来表现动物的行为,一想这种方法肯定不行,因为已经有了前一篇文章的教训,不可以用Switch来判断类型然后确定对象行为。而且连通过判断属性来确定对象行为也都是不对的,另外对象类型也是对象属性的一部分。

旁边的村长说“查查前一篇文章的方法,行为应该是对象实例自己负责的。也就是自己要勇于承担责任”

喜羊羊比较聪明一提示就会了,写出了下面的代码,也松了一口气。

// 比赛开始
    public void Start()
    {
        foreach (Animal player in players)
        {
            player.Run();
        }
    }

接着Game的Start方法中的就把控制权交给了Player,而Player是Animal的对象实例,所以我们去看看Animal的代码。

namespace GameApplication
{
    class Animal
    {
        public string Name { get; set; }

        public void Run()
        {
        }
    }
}

这里我们Animal类的Run方法什么都没有做。

接着又想….

这里肯定也不能用判断名称(Name)的方法而切换不同的行为。旁边的人出了一个主意说,我看到过很多的程序喜欢给类之上加一个类型(Type),你也给这个程序加一个类叫AnimalType,然后Animal的Run调用AnimalType的调用。

namespace GameApplication
{
    class AnimalType
    {
        public void Run()
        { }
    }
}

接着又想…

怎么还是要通过属性来判断对象行为啊???

这个问题陷入明显的思维怪圈,又在想是到底是地方出了问题,难道给对象建模实现的时候只能采取从下往上的方法,而不能从上往下的方法。

上面其实也是也个经常会犯的问题- 一个类之上再加个一个类型,比如Animal之上再加一个AnimalType,抱着侥幸的心里认为这样就能实现。

回过头来想,既然在Animal上加一个AnimalType类型不能解决问题,那么试试在Animal类的下面加几个参赛选手的子类Sheep,RobotSheep,Wolf,并把Animal改为抽象类,接着分别实现子类的方法就能达到上一篇文章的效果。实际上结果还是一样的。

 

这个文章是基于前面一篇文章的思考而引发的,也就是我们既要做到能从下往上分析并实现,也要能做到从上往下分析并实现。

下面一篇文章讨论着两篇文章总的问题,也是网友“简单的程序 ”提到如果更多的动物来参赛的话就会有很多的子类而导致类爆炸。

posted @ 2010-07-02 21:21  richardzeng  阅读(1340)  评论(0编辑  收藏  举报