程序原本(六十四):应用开发基础——应用开发的背景与成因(寻找第一个有意义的功能,是探索用户需求的起点)

阅读数:31 2019 年 10 月 1 日 16:48

程序原本(六十四):应用开发基础——应用开发的背景与成因(寻找第一个有意义的功能,是探索用户需求的起点)

对于“张三”这个户籍管理员来说,第一个真正有意义的功能是什么呢?答案是:查看身份档案

应用开发必须站在用户视角来看问题。张三的日常工作之一是查看身份档案,这也就是他的功能需求。这项需求可能需要分成三个实现过程:

  • 过程一:列举身份档案
  • 过程二:调出身份档案
  • 过程三:显示身份档案

我们需要明确,张三所需功能其实是第三项,即显示身份档案。这可以实现为在用户操作环境中的图形化界面,或是可以进行交互操作的文字框等。而过程一,其实是一个附带的需求,因为从用户操作流程上来讲,张三可能6需要先看到一个列表,然后才能选择到某个User_IdCard

6 这只是“可能”,因为张三也可以有许多不同的方式来接触到这个User_IdCard,并最终显示它(亦即是过程三)。这涉及到种种不同的人机交互技术的现实应用,但这些交互行为之任一,都并非是实现过程三所必然的选项。

过程二则是一个从计算机角度来看待问题的结果。也就是说,对于计算机的数据系统来说,“显示身份档案”是“调出身份档案”之后的一个后续行为。从计算机的角度来看问题,对于张三有意义的功能是第二项;但从张三的角度来说,他只关心第三项功能。

在实际开发中,我们讨论的将是类似如下的代码7

7 对于现实的户籍管理系统来说,这可能算不得“好的”实现过程,因为它混杂了过程式与面向对象编程风格,并且对于多线程 / 多界面操作来说存在隐患。

复制代码
// - Delphi/Pascal Syntax
(**
* 列举身份档案
* 功能:列举一批档案,返回档案列表
* - ListCard() 可能是一个数据库或内存中的数据结构相关的函数
* - TIdCards 可能是一个与界面无关的数据结构,因此在 ListCard 之后通常有相应的界面过程
*)
function ListCard(fromId: Integer): TIdCards;
begin
// …
end;
(**
* 调出身份档案
* 功能:使用既有的数据查询 / 访问接口,从基础数据层获得指定的身份档案
*)
function LoadCard(Id: Integer): TUser_IdCard;
begin
// …
end;
(**
* 显示身份档案
* 功能:将身份档案显示在输出设备上,可能是手持设备,或 GUI,或控制台。返回用户行为。
* - “显示输出(Show)”是一个抽象的功能,对于特定环境下的实现并没有说明。
* - “用户行为 (Action)”也是抽象定义,但在不同的交互环境中可能是相同的(或仅有实现的不同)。
*)
var
CurrentCard : TOperatingCard;
function ShowCard(Id: Integer): TUserAction;
begin
CurrentCard.User_IdCard := LoadCard(Id);
// …
TModalResult(Result) := TShowCardForm.Create(MainForm).ShowModal;
end;

在这个示例中,我们严格地将“程序员视角下的过程”与“用户操作视角下的过程”区隔开来:只有当TShowCardForm.Create()ShowModal()发生时8,整个程序才是用户交互相关的。这一示例典型地将业务逻辑与用户交互逻辑分离开来,使得观察“什么是用户需求”成为显而易见的事情。

8 这里使用了 Delphi 的一些技巧:(1)TShowCardForm()在关闭时是可以自动释放的;(2)窗体可以向调用过程返回一个值以表明用户所做的界面操作;(3)使用ShowModal()方式打开的窗体能阻止该应用中其他窗体的操作,即这种情况下的用户界面是独占的。

对应于过程一和过程二所述的需求,就其功能的实现来看,其实包括了在三个层次(机器数据层、基础数据层和应用数据层)上对数据的所有操作。其中:

  • TIdCards等类型的设计,以及 Card 作为可操作数据的内存分配与管理等,这些是与计算环境相关,针对机器数据层的设计;
  • Cards 基于有序编号(fromId)、Cards 是否存储在数据库或本地结构化文件中,以及针对上述存储的存取过程等,这些与系统相关的、内在的设定构成了基础数据层
  • CurrentCard作为单例的存在,TUserAction以及它与TModalResult之间的关系等,这些是应用数据层的设定——基本上来说,将应用数据层整体去掉之后,基础数据层仍然足以支撑其他的用户需求与业务。

注意一些高级语言在应用开发中刻意地屏蔽了这里提出的三个数据层次中的多数细节 。例如在 Java 中可以直接使用对象来表达 IdCard,并用内置的 ObjectList 来实现 IdCards 列表,因此开发人员并不需要接触到机器数据层和基础数据层,例如不必了解对象结构,以及它们在 32 位或 64 位机器上的不同实现。类似的,在多层设计中,也倾向用 O/R Mapping 技术来屏蔽后端数据库的细节,因为它们同样处于基础数据层或机器数据层。这一切的主要目标,就是让“面向应用开发的程序员”将重心放在应用数据层的设计之上,并基于那些相对确定的、稳定的,以及更远离用户的数据层次以及解决方案来完成开发过程。

然而这些对于一个具体的操作人员(例如张三)来说,没有什么意义。最后,对于功能三,如果不考虑显示的具体效果的话,它只是在应用数据层上一个“界面交互”的实作而已。

但正是在这一点上,我们发现“用户需求”完整地影响了过程三的设计。

评论

发布