面向对象分析设计学习与探索(三):收集需求(Gathering Requirement)
相信每一个人都希望让客户满意。而且现在已经知道写一个好的软件的第一步就是确定软件所作的是客户想让它做的。但是你如何确定什么是客户想要的?如何确定客户知道他们真正想要什么?
的确!在做项目项目的时候,我们常常被不确定的需求变化所困扰,当然如果说在开发时期要求需求一点不变化是不可能的,但是有些需求变化是致命的,会导致工作量成倍的上升,甚至是项目的失败。现在有个场景,我们看看如何做才能很好的挖掘用户的想法(这是编写成功软件的第一步)。
情景:有一个用户要给自己的小狗修一个Dog Door,以至于他们不用再睡觉后听到狗叫,然后起床给它开门。当然,这个Dog Door 并不是简单的一个塑料片,他们希望通过一个遥控器按钮使这个Dog Door打开或者关上。

从需求上看,好像很简单。我们只需要一个Dog Door和一个可以控制们的遥控器,让我们从Dog Door开始,首先要有一个DogDoor的类型,当然这个类型中有一些功能,如:开门、关门。
public class DogDoor
{
private Boolean _isOpen;

public Boolean IsOpen
{
get { return _isOpen; }
}

public DogDoor()
{
_isOpen = false;
}

public void Open()
{
_isOpen = true;
Console.WriteLine("The dog door is opened.");
}

public void Close()
{
_isOpen = false;
Console.WriteLine("The dog door is closed");
}
}
然后在完成遥控器的代码:
public class Remote
{
private DogDoor _door;

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

public void pressButton()
{
if (_door.IsOpen)
{
_door.Close();
}
else
{
_door.Open();
}
}
}
最后我们来测试一下:
class Test
{
static void Main(string[] args)
{
DogDoor door = new DogDoor();
Remote remote = new Remote(door);
Console.WriteLine("The dog is barking, he wanna
");
remote.pressButton();
Console.WriteLine("The dog is outside and
");
remote.pressButton();
Console.WriteLine("The dog is barking, he wanna inside");
remote.pressButton();
Console.WriteLine("The dog is inside");
remote.pressButton();
Console.Read();
}
}

好了,现在可以交给客户使用了。可是不久,客户就发现了问题。


当需要收集需求时,我们最应该做的是:让客户说话。注意什么是系统需要做的,然后列出什么是系统将要做的。
首先来听听他们说些什么

根据他们所说的现在建立一个需求清单
1、 Dog door打开的时候至少12英寸高
2、 按遥控器上的按钮,当门关上的时候,门打开;否则,门关上
3、 一旦门打开了,在一定的时间后如果没有关闭的话,他将自动关闭

这只是一个简单的需求清单,我们需要将他细化,以准确的指出当小狗要出去时都发生了什么!

细化之后觉得这个清单中的第六步还是有些问题。在Fido(那只要出去的小狗)近来之前,门一直是开着的。这样的话,它近来之前就不必再叫了。现在我们在来修改一下。

现在这个清单已经很精确了,实际上,用了这就是根据需求清单写出的用例(use case)
用例描述了你的系统为完成用户需求中的一个细节所需要做的事情。

那么如何写好一个用例呢?
通常一个用例由三部分组成:Clear Value,Start and Stop,External Initiator
1、 Clear Value:很抱歉的是我不太明确应该怎么翻译,我觉得是一个明确的目标或者价值。每一个用例都要有一个Clear Value。如果说用例没能帮助客户达到他们的目标,那么用例就没有用了
2、 Start and Stop:开始和结束。每一个用例都要定义一个起始点和一个结束点。一个进程开始后,在一个条件下,进程结束。在上面的用例中,Fido开始叫,这个进程就开始了。当Fido进门后,门自动关上,这个进程就结束了。
3、 External Initialtor:我管它叫外部触发者。它必须是系统之外的人或物。在上面的用例中就是那个要出去的小狗。

我们有了需求清单和用例,现在我们要做的是回到需求,确定覆盖了系统必须做的所有事情。(将对应的需求填入用例,N/A是not applicable)

这个方法不错,可以需求清单和用例很好的对应,在我们编写相应程序时不会因为不明确而漏掉什么。
现在有了需求和用例,我们可以开始编码了。首先要修改Remote类型,使其在打开门一段时间之后自动把门关上,代码如下:
public class Remote
{
private DogDoor _door;

Timer time;
TimerCallback callback;

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

public void pressButton()
{
if (_door.IsOpen)
{
_door.Close();
}
else
{
_door.Open();
callback = new TimerCallback(_door.Close);
time = new Timer(callback,_door,1000,0);
}
}
}
static void Main(string[] args)
{
DogDoor door = new DogDoor();
Remote remote = new Remote(door);
Console.WriteLine("The dog is barking, he wanna
");
remote.pressButton();
Console.WriteLine("The dog is outside and
");
Thread.Sleep(10000);
//remote.pressButton();
Console.WriteLine("The dog is barking, he wanna inside");
remote.pressButton();
Console.WriteLine("The dog is inside");
//remote.pressButton();
Console.Read();
}
测试结果还可以:

现在客户感觉满意了,我们也初战告捷了。

的确!在做项目项目的时候,我们常常被不确定的需求变化所困扰,当然如果说在开发时期要求需求一点不变化是不可能的,但是有些需求变化是致命的,会导致工作量成倍的上升,甚至是项目的失败。现在有个场景,我们看看如何做才能很好的挖掘用户的想法(这是编写成功软件的第一步)。
情景:有一个用户要给自己的小狗修一个Dog Door,以至于他们不用再睡觉后听到狗叫,然后起床给它开门。当然,这个Dog Door 并不是简单的一个塑料片,他们希望通过一个遥控器按钮使这个Dog Door打开或者关上。
从需求上看,好像很简单。我们只需要一个Dog Door和一个可以控制们的遥控器,让我们从Dog Door开始,首先要有一个DogDoor的类型,当然这个类型中有一些功能,如:开门、关门。



























然后在完成遥控器的代码:






















最后我们来测试一下:




















好了,现在可以交给客户使用了。可是不久,客户就发现了问题。
客户忘记了用遥控器把门关上,结果很多小动物就进来了。看来这个们没有按照客户希望的工作。很显然,这个简单的软件并不符合客户的要求(当然这种意见他们原来并没有提出来)。现在我们回过头来重新审查我们对这个需求的理解,已确定我们的软件是客户想要的。现在一个问题产生了:什么是需求?
是系统为了工作正常而必须做的工作细节
当需要收集需求时,我们最应该做的是:让客户说话。注意什么是系统需要做的,然后列出什么是系统将要做的。
首先来听听他们说些什么
根据他们所说的现在建立一个需求清单
1、 Dog door打开的时候至少12英寸高
2、 按遥控器上的按钮,当门关上的时候,门打开;否则,门关上
3、 一旦门打开了,在一定的时间后如果没有关闭的话,他将自动关闭
这只是一个简单的需求清单,我们需要将他细化,以准确的指出当小狗要出去时都发生了什么!
细化之后觉得这个清单中的第六步还是有些问题。在Fido(那只要出去的小狗)近来之前,门一直是开着的。这样的话,它近来之前就不必再叫了。现在我们在来修改一下。
书上教给我们一个不错的办法,对于有问题的步骤,或者是描述不足的部分使用小号修改,不必把后面删掉重新排号。
现在这个清单已经很精确了,实际上,用了这就是根据需求清单写出的用例(use case)
用例描述了你的系统为完成用户需求中的一个细节所需要做的事情。
那么如何写好一个用例呢?
通常一个用例由三部分组成:Clear Value,Start and Stop,External Initiator
1、 Clear Value:很抱歉的是我不太明确应该怎么翻译,我觉得是一个明确的目标或者价值。每一个用例都要有一个Clear Value。如果说用例没能帮助客户达到他们的目标,那么用例就没有用了
2、 Start and Stop:开始和结束。每一个用例都要定义一个起始点和一个结束点。一个进程开始后,在一个条件下,进程结束。在上面的用例中,Fido开始叫,这个进程就开始了。当Fido进门后,门自动关上,这个进程就结束了。
3、 External Initialtor:我管它叫外部触发者。它必须是系统之外的人或物。在上面的用例中就是那个要出去的小狗。
我们有了需求清单和用例,现在我们要做的是回到需求,确定覆盖了系统必须做的所有事情。(将对应的需求填入用例,N/A是not applicable)
这个方法不错,可以需求清单和用例很好的对应,在我们编写相应程序时不会因为不明确而漏掉什么。
现在有了需求和用例,我们可以开始编码了。首先要修改Remote类型,使其在打开门一段时间之后自动把门关上,代码如下:



























注意,这个地方的变化造成了一些修改,当然程序有很多种写法,这里就不再探讨这个问题了,造成修改的地方是:DogDoor类型中加入一个Close的重载方法:
public void Close(object source)
{
Close();
}
最后我们需要修改原来的测试代码,去掉开门后按按钮的动作,让门自动关闭,在测试代码中用线程休眠来表示Fido已经出去,测试代码如下:


















测试结果还可以:
现在客户感觉满意了,我们也初战告捷了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构