By 高焕堂 

ee                                             ee

 

企业框架API设计范例

 -- 以『五子棋』应用框架为例

 

1.  阶段一:从传统OOAD出发

         

    (图片来源:互动百科)

    从传统OOAD出发,针对『五子棋』进行传统的OOAD分析与设计(Object-Oriented Analysis & Design)。其分析步骤为: 

Step-1:  找到主角,就是:棋手

    首先,寻找『五子棋』的核心概念,成为类造形的内涵。例如:五子棋游戏的主角是棋手(玩家),棋手有两种:电脑和人;其中,电脑棋手又分为数个不同棋力等级,例如:

  

Step-2:  抽象出抽象类别(Super-class),就是BaseAI类:

  

Step-3:  再增添一种棋手,就是:HumanPlayer (人),而且再度进行抽象,得到:

 

Step-4: 再联想到人之外的物——棋盘(Chess Board),它必须呈现于UI画面上,所以设计成为View的子类别,得到:

 

Step-5: 再从棋盘联想到相关的概念:棋(Chess);以及用来控制UI显示的GobangActivity类别。如下图: 

  

Step-6: 还可以继续联想下去,就更加完整了[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

 

 

2.  阶段二:运用EIT造形设计

    从上图里,可以看出来,传统基于类造形的分析与设计,只凸显了类(Class)和关系(Relationship),而将接口(Interface)隐藏于类或关系里,此时EIT造形就派上用场了。例如,上图的”棋盘(Chess Board)”与”棋手(Player)”之间是1:N的组合关系,就隐含了一个重要接口:可让用户选择多位棋手。于是,藉由EIT造形的<I>来表述这个接口,而棋盘和棋手就是它的配角:棋盘扮演<E>角色,而棋手扮演<T>角色,如下图: 

  

    传统上,将<I>隐藏起来,常常带来许多缺点,例如:

  • 架构师知道接口的存在,但没有途径去清晰地表述出来。
  • 由于没有明确传达给开发者,徒增开发者的负担,也提高了失误的可能性。
  • 由于没有凸显接口,无法协助项目经理(PM)掌握最佳的团队分工界线,例如框架开发与App开发的分际。
  • 等等。 

于是,可以藉EIT造形来凸显<I>,如下图:

 

      这个IPlayer就成为框架与App的分工界线了。在买主还没来、或用户还没出现之前,框架团队就能先定义IPlayer接口,然后进行开发Chess Board和Line等类的代码,成为框架代码了。而等到买主来了、或用户出现之后,App团队才开始动手设计棋手(即<T>的类体系),成为App软件代码了。

    由于接口(如上图的IPlayer)只能含有抽象函数,不能含有具象函数,所以这些抽象函数的实现代码都必须写在App的<T>类里。这会增加App开发团队的负担,延迟App开发交付的效率。

    框架开发团队为了提供更多的默认行为(Default Behavior)来让开发团队可加以复用(Reuse),就会设计抽象类(Abstract)来具象函数,来实现这些默认行为。例如下图:

 

     这个 BasePlayer是一个抽样类,内含func()等具象函数,可撰写代码来提供默认行为,让各<T>类来复用,能减轻App开发负担,提高开发交付效率。此时,BasePlayer扮演两个角色:一方面扮演<Chess Board, IPlayer, BasePlayer>造形的<T>;另一方面又扮演一个新EIT造形的<E>,提供新的<I>,就是上图里的hook()抽象函数,来让App的<T>类来撰写其实现代码。于是,这个新的<I>就封装了原来的IPlayer接口,变成框架与App的新接口,也是新的分工界线了。这个新界线的优点是:减轻了App开发者的负担,因而能吸引更多App开发者来使用此软件框架了。

    依样画葫芦,再依循EIT造形,继续设计一个IChess接口,以及设计一个Chess抽象类,来提供框架与App之间的<I>,如下图:

  

    此时,Chess也扮演两个角色:一方面扮演<Chess Board, IChess, Chess>造形的<T>;另一方面又扮演一个新EIT造形的<E>,提供新的<I>,就是上图Chess类里的hook()抽象函数,来让App的<T>类(如上图的myChess)来撰写其实现代码[歡迎光臨 高煥堂 網頁: http://www.cnblogs.com/myEIT/ ]

 

    为了进一步减轻App开发者的负担,藉之吸引更多App开发者来使用框架,通常框架开发团队会持续增添更多的抽象类,来提供更贴心的默认行为。例如下图,架构师将BaseAI和HumainPlayer两个抽象类提升到框架里。

  

    计算机棋手共享的默认行为,可以写在BaseAI抽象类里;而人员棋手的共享默认行为则写在HumainPlayer抽象类里。至于所有棋手都共享的默认行为则提升到最高层的BasePlayer抽象类里。如此,持续丰富框架的内涵,进一步减轻App开发者的负担,则框架的价值就愈来愈高了。

  此外,也提供给App开发者更多的弹性选择空间;例如在上图里,App开发者撰写<T>类时,就有多种选择了:

  • 选择继承BaseAI或继承HumainPlayer抽象类:可复用(Reuse)这两类里的具象函数。如下图的<T1>类。
  • 如果不适合继承上述两个类,可选择继承BasePlayer抽象类:可复用这个类里的具象函数。如下图的<T2>类。
  • 如果不适合继承上述各个类,可选择直接实现IPlayer接口:必须自己实现IPlayer里所定义的各函数。如下图的<T3>类。

  

3. 复习:EIT代码造形的概念

3.1 历史回顾

    回顾1970年代的C语言,函数(Function)和数据结构(Data Structure)是计算机语言的基本模块(Basic Building Block);因此IT系统分析&设计人员就以函数和数据结构去发展建模方法和实现工具。例如当年的结构化(Structured)设计方法和数据库(Data Base)系统等。当时系统分析与设计的基本模块是:单一的函数造形;并且能直接对映(Map)到代码。

    到了1980-90年代,OOP成为业界主流,类(Class)成为C++和Java等OOP语言的基本模块。因而有了UML建模语言和OOAD(Object-Oriented Analysis and Design) 方法问世。于是,系统分析与设计的基本模块扩大为:单一的类造形;并且能直接对映(Map)到代码。

    以此类推,1995年Gamma推出了设计模式(Design Patterns),成功地成为系统分析和设计的基本模块。但是,设计模式花样繁多,其幕后缺乏单一模式来组合出各种设计模式,无法直接对映到代码。因而,设计模式未能扩大系统分析和设计的基本模块。至今,类(Class)仍然是系统架构师或分析师心中的基本设计模块。

 

3.2  先基于类造形而设计

    例如,针对五子棋进行OOAD分析与设计:

 

    (图片来源:互动百科)     

 

      从传统OOAD出发,针对『五子棋』进行传统的OOAD分析与设计(Object-Oriented Analysis & Design)。其分析结果为: 

 

 

3.3  接着,基于”EIT造形而设计

    于2012年5月,高焕堂老师提出了单一的EIT造形,来扩大系统分析和设计的基本模块;并且能直接对映(Map)到代码。这EIT造形内含3种要素:<E>对映到代码的基类(Super-class)、<I>对映到基类的抽象函数(Abstract Function)、而<T>则对映到代码的子类(Subclass),如下图所示:

  

      其中,接口就是一种抽象类,所以在本质上,EIT造形就是由3个类所构成的,能直接对映到代码(即3个类的代码)。EIT造形就像自然界的原子(Atom)造形或DNA螺旋造形一般,自然界的不同物质都具有一致的简单造形,但其内涵却都可以不相同。其中,”EIT”名称是来自汽车的<引擎(Engine)、接口(Interface)、轮胎(Tire)>三种角色,及其之间的关系。

    EIT造形介于类和设计模式之间。它是由3个类所构成的单一造形;它又能组合出各种设计模式,以及各种框架(Framework)。例如,五子棋的范例里,”棋盘(Chess Board)”与”棋手(Player)”之间是1:N的组合关系,就隐含了一个重要接口:可让用户选择多位棋手。

于是,藉由EIT造形的<I>来表述这个接口,而棋盘和棋手就是它的配角:棋盘扮演<E>角色,而棋手扮演<T>角色。这<E>和<T>引导我们设计(创造)出IPlayer接口,如下图:

  

    由于接口(如上图的IPlayer)只能含有抽象函数,不能含有具象函数,所以这些抽象函数的实现代码都必须写在App的<T>类里。这会增加App开发团队的负担,延迟App开发交付的效率。

    框架开发团队为了提供更多的默认行为(Default Behavior)来让开发团队可加以复用(Reuse),就会设计抽象类(Abstract)来具象函数,来实现这些默认行为。例如下图:

 

     这个 BasePlayer是一个抽样类,内含func()等具象函数,可撰写代码来提供默认行为,让各<T>类来复用,能减轻App开发负担,提高开发交付效率。此时,BasePlayer扮演两个角色:一方面扮演<Chess Board, IPlayer, BasePlayer>造形的<T>;另一方面又扮演一个新EIT造形的<E>,提供新的<I>,就是上图里的hook()抽象函数,来让App的<T>类来撰写其实现代码。于是,这个新的<I>就封装了原来的IPlayer接口,变成框架与App的新接口,也是新的分工界线了。这个新界线的优点是:减轻了App开发者的负担,因而能吸引更多App开发者来使用此软件框架了

[Go Back