Bootstrap

框架设计范例:以『五子棋』应用框架为例

前言:

EIT造形介于类和设计模式之间。它是由3个类所构成的单一造形;它又能组合出各种设计模式,以及各种框架(Framework)。例如,五子棋的范例里,”棋盘(Chess Board)”与”棋手(Player)”之间是1:N的组合关系,就隐含了一个重要接口:可让用户选择多位棋手。于是,藉由EIT造形的<I>来表述这个接口,而棋盘和棋手就是它的配角:棋盘扮演<E>角色,而棋手扮演<T>角色。这<E>和<T>引导我们设计(创造)出IPlayer接口。

wKioL1MFd2yjSC3SAAEfgKFAUB8610.jpg

ee                                                                        ee

欢迎访问 ==>高老师的博客网页

高焕堂:MISOO(大数据.大思考)联盟.台北中心和东京(日本)分社.总教练


EE                                                                        EE

框架设计范例:以『五子棋』应用框架为例

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

05080106-a566994a9ab740f79b59711a73c989e

 (图片来源:互动百科)

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

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

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

05080135-280ea53c38494098908c60086826f1e

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

05080156-e0d7f297d7ec4faf82d53612f9b39af

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

05080214-9c07b7711e9b408db4d05210e9b1348

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

05080236-a480508ca29d4e969ccfde98ad1976a

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

05080425-1817b38d914b49c0a64adbe384d7d2d

Step-6:还可以继续联想下去,就更加完整了


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

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

05080403-074288068c49474aa86f57f92af7e93

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

  • 架构师知道接口的存在,但没有途径去清晰地表述出来。

  • 由于没有明确传达给开发者,徒增开发者的负担,也提高了失误的可能性。

  • 由于没有凸显接口,无法协助项目经理(PM)掌握最佳的团队分工界线,例如框架开发与App开发的分际。

  • 等等。

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

05080922-b5da2b298519455f82a76256df4835d

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

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

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

05080945-b4f9a87c8f9c42a6a27deab9844561d

  这个 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>,如下图:

05081008-b789a34f984942f78918830b9bcaa95

此时,Chess也扮演两个角色:一方面扮演<Chess Board, IChess, Chess>造形的<T>;另一方面又扮演一个新EIT造形的<E>,提供新的<I>,就是上图Chess类里的hook()抽象函数,来让App的<T>类(如上图的myChess)来撰写其实现代码

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

05081048-82aa82f8d4834936b6a145910a606b8

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

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

  • 选择继承BaseAI或继承HumainPlayer抽象类:可复用(Reuse)这两类里的具象函数。如下图的<T1>类。

  • 如果不适合继承上述两个类,可选择继承BasePlayer抽象类:可复用这个类里的具象函数。如下图的<T2>类。

  • 如果不适合继承上述各个类,可选择直接实现IPlayer接口:必须自己实现IPlayer里所定义的各函数。如下图的<T3>类。

05081106-5f36f644c25047b782589c86c8b27c7

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分析与设计:

05081127-1ce051b1b96e4f0bb57e26e24eb021d

(图片来源:互动百科)


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


05081727-5c84207748544b6ab951e5babdb737b

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

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

05081747-da5d327102094e479977ece72462754

 其中,接口就是一种抽象类,所以在本质上,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接口,如下图:

05081819-253c9a3ba53247ffa8732850f53f7bf

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

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

05081837-96c51af6ea7245b3a37df1b27dc8ddd

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


;