面向对象分析设计学习与探索(四):需求变化(Requirements Change)

        需求变化往往是一个项目中不可避免的,当然也是常常是使开发者头痛的。用户在使用软件后常常会出现这样或那样的想法。目前的软件虽然已经满足了他们先前提出的要求。但是要知道,结束意味着新的开始。还记得上一篇文章中的那个客户吗?他们又回来了!
        上一篇中我们为他们开发了一个Dog Door。在使用中都很好,但现在他们有了一些新的建议。他们常常乱放遥控器,以至于当小狗要出去或者近来时,不得不去找一下遥控器放在那里了。(有点头大,但是用户往往是对的,需求经常变化即使你有一个好的用例,你常常要快速改变你的软件以适应那些新的需求。)
        根据他们的需求,我们需要相应的修改我们的用例:看到需求后,感觉我们只需要在小狗barking的时候加一个声音识别装置来接收声音信号,然后再开门。

                

        看了这个用例的确让人疑惑,因为新加入的步骤让人觉得比较混乱。当用例让我们疑惑时,我们需要重写用例。上面用例的问题在于新加入的步骤并不是一个子步骤或者是一个先后的顺序,而是并列的关系。而且根据上面的需求,客户希望Dog Door主动识别小狗的叫声并开门,而不再过多地依靠他们。那好,用例应该是这样的:

                

        注意:现在加了两个标签,Main Path & Alternate Paths
        Main Path:我理解为在一般的情况下都会发生的途径,或者说主要发生的途径
        Alternate Paths:我理解为有可能发生的途径,替代途径
        对于一个用例来说,从开始到结束可能有很多的途径,这时有一个scenario概念出现,可以理解为一个场景或者一种可能发生的途径。书上是这样写的:A complete path through a use case, from the first step to the last, is called scenario. 大多数用例有一些不同的scenario,但是这些scenario一般共有一个目标(说到这里,我想到上一篇文章中提到的:一个用例要定义一个开始点和一个结束点)。
        Alternate Path也是用例中重要的部分,不经常发生的并不是不发生,比如在上面的例子中,如果声音识别器坏掉的情况下…而且Alternate Path使得用例更完整。
        现在用例已经修改完毕了,但是要注意,在编写代码之前,查看一下用例和原来的需求是否可以对应。任何时候改变用例都要回来查看需求的变化。下面来修改我们的需求列表。
                

        下面我们要开始完成代码的编写,首先,需要一个声音识别器类型:
    

    public class BarkRecognizer
    
{
        
private DogDoor _door;

        
public BarkRecognizer(DogDoor door)
        
{
            
this._door = door;
        }


        
public void recoginze(String StrBark)
        
{
            Console.WriteLine(
" BarkRecognizer heard a '" + StrBark + "'");
            _door.Open();
        }

    }

 

        然后我们在对测试代码进行修改:

        static void Main(string[] args)
        
{
            DogDoor door 
= new DogDoor();
            BarkRecognizer recognizer 
= new BarkRecognizer(door);
            Remote remote 
= new Remote(door);
            Console.WriteLine(
"The dog is barking, he wanna ");
            
//remote.pressButton();
            recognizer.recoginze("Woof");
            Console.WriteLine(
"The dog is outside and ");
            Thread.Sleep(
10000);
            
//remote.pressButton();
            Console.WriteLine("The dog is barking, he wanna inside");
            
//remote.pressButton();
            recognizer.recoginze("Woof");
            Console.WriteLine(
"The dog is inside");
            
//remote.pressButton();
            Console.Read();
        }

        测试结果如下:

                

        这次修改中发生了一个问题:Dog door 无法自动关闭了!在实际开发中,需求变化导致的修改往往会让我们的程序千疮百孔,对于这样一个小的程序都会出现这种问题,何况我们的项目呢?但是,这种情况不是不能避免,问题也许是由于原先的设计,也许在于后来的开发上,及时调整,避免问题到了后面,不可收拾。
        我原来把自动关闭的代码写在了Remote中,现在可以同样在BarkRecognizer中贴入同样的代码,但是这并不是一个好的主意。想想如果说今后对于自动关闭的方法有修改时,那么需要修改所有贴有这个代码的地方,如果客户又要加入一种开门的方式,就又要粘贴一下这段代码,显然这与面向对象原则是背道而驰的。相对较好的解决方式应该是修改DogDoor类型,因为自动关闭是DogDoor本身的行为。修改代码如下:

    public class Remote
    
{
        
private DogDoor _door;

        
public Remote(DogDoor door)
        
{
            
this._door = door;
        }


        
public void pressButton()
        
{
            
if (_door.IsOpen)
            
{
                _door.Close();
            }

            
else
            
{
                _door.Open();                
            }

        }

    }


    
public class DogDoor
    
{
        
private Boolean _isOpen;

        Timer time;
        TimerCallback callback;

        
public Boolean IsOpen
        
{
            
get return _isOpen; }
            
set { _isOpen = value; }
        }


        
public DogDoor()
        
{
            _isOpen 
= false;
        }


        
public void Open()
        
{
            _isOpen 
= true;
            Console.WriteLine(
"The dog door is opened.");
            callback 
= new TimerCallback(this.Close);
            time 
= new Timer(callback, this10000);
        }


        
public void Close()
        
{
            _isOpen 
= false;
            Console.WriteLine(
"The dog door is closed");
        }


        
public void Close(object source)
        
{
            
this.Close();
        }

    }


        现在正常了:


                

        通过修改,我们可以看出:有时候需求中的一个变化能反映出系统的一些问题,你甚至不知道有这些问题。(Sometimes a change in requirements reveals problems with your system that you didn’t even know were there.)
变化是经常的,系统也应该在你修改或者编写他的时候经常有所改善。(Change is constant, and your system should always improve every time you work on it.)
        故事讲还在继续,但现在我们可以总结一下这两章中学习到的东西,一些OOA&D的工具或者说思想:
        需求:
            1、 好的需求保证了你的系统工作如同你的客户所期望的
            2、 确定你的需求覆盖了系统用例中的所有步骤
            3、 使用你的用例并找出客户忘记告诉你的事情
            4、 你的用例往往会展现一些需求中不完善或者是丢失的东西,而这些东西你应该加入你的系统中
            5、 随着时间流逝,需求会变化或者膨胀
        面向对象原则:
            1、 封装变化


posted @ 2007-09-04 17:20  KiddLee  Views(2948)  Comments(12Edit  收藏  举报