8.3 建模步骤C-2 识别类的关系
8.3.4 识别关联关系
本节的内容更应该放到“识别泛化关系”的前面来讲,但考虑到关联的内容比较多,决定还是放在“识别泛化关系”的后面。
所以,有必要在这个地方提醒一下,把泛化放在关联前面讲解,不意味着泛化比关联重要,事实恰好相反。
在建模分析模型的时候,不使用泛化关系仅使用关联关系的情况并不罕见,反过来,如果仅使用泛化关系,就要怀疑是不是犯了前面所提到的拼凑泛化“or、er”伪面向对象错误。
8.3.4.1 关联是不得不记住的关系
前面已经说过,泛化和关联是类的静态关系,是系统要记住的关系。这个地方为什么又要强调一遍呢?
因为我们很少看见类似图8-113的泛化错误:
图8-113 我们一般不会犯这样的泛化错误
我们把图8-113的泛化替换成关联,得到图8-114。建模的时候,出现类似8-114的情况很多。
图8-114 类似这样的关联关系很常见
图8-113肯定是错的,图8-114则有可能正确,也有可能错误。所以,我们得来看一看,需要建模的关联关系是怎样的。
假设目标系统是一个企业管理系统。
这个系统可能需要知道一个字符串是否包含另一个字符串,例如“马宝国”里面是否包含“马宝”?
是否需要像图8-115那样,建立字符串的自反关联?
图8-115 字符串的自反关联
不需要,因为这个问题已经有人解决了,例如.NET中的String.Contains。目标系统不需要一个个记住字符串之间的包含关系。
再来看.NET的这个Contains,它是怎样实现的?
是不是通过维护类似下面的字符串之间的关联:
{(“马宝国”,“马”),(“马宝国”,“宝”),(“马宝国”,“国”),(“马宝国”,“马宝”),(“马宝国”,“马国”),……}
然后查询这些关联得到结果?
同样不是。Contains从来没有,也不需要建立和维护这样的“关联”,它的结果是通过已经掌握的规律计算出来的。
★.NET(或Java)的String.Contains(背后是IndexOf)如何实现,读者可自行查询。
目标系统可能还需要计算一个数的立方,例如,x=3,x的立方等于多少?
同理,系统也不需要记住{(1,1),(2,8),(3,27),(4,64),……}这样的对应。
★花絮:国内的某本领域驱动设计名著说,函数式编程的加1函数add1,是根据x的值进行模式匹配得到结果的,还给出代码:
def add1(x: Int): Int=>x match{
case 0 =>1
case 1 =>2
case 2 =>3
case 3 =>4
//...
}
这就相当于建立自反关联了,如图8-116:
图8-116 整数的自反关联
可是,这个add1函数,其定义域和值域都是整数。那可是一个无限集,代码是这样干的吗,这样干行吗?
另外,既然说是“模式匹配”,“模式”这个词蕴含把许许多多个例归纳或分类的意思。模式的个数应该是少量的,例如一个枚举“商品类型”。
这个add1却是把每一个整数做模式匹配,岂不是有无限个模式?
我不是函数式编程专家,只是提出疑问,各位读者有空赐教。
**********
回到正题。
目标系统可能还要知道,如果一名员工的工号是20249527,那么他的直接上级的工号是什么?
这个就不一样了,我们能找到像下面这样的“工号→直接上级工号”背后的计算规律吗?
先判断工号的奇偶,如果是奇数,则乘以3再加1,如果是偶数,除以2,重复以上过程,直至得到1。把停止后得到的数字序列按照UTF-8编码转成字符串,即可得到直接上级工号。
可惜,目前并没有发现这样的规律。
这时候,才需要我们建立关联来解决问题,如图8-117。
图8-117 员工的自反关联
系统得老老实实维护“员工→员工”之间的“直接上级(直接下级)”关联,例如数据库的“员工”表有这样的一些行:
图8-118 数据库里的“员工”表
然后,目标系统在需要时查询这些数据。
我们要建模的,是当前无法计算的内容。
世界上万事万物,只要我们乐意,都可以找出它们之间的关系,但我们要建模的关联,是系统不得不一个个记住的内容,如果不记住,系统当前就无法完成满足需求的计算。
注意“当前”二字。
“工号→直接上级工号”到底有没有一个计算公式呢?
也许是有的,如果我们宇宙是由神级文明创造和控制的,一切运行都有其规律。如果有一天,我们掌握了其规律,很可能系统只需要记住少量参数,就可以完成任意“工号→直接上级工号”的计算。
我们用椭圆来类比。如果不了解椭圆的规律,要记住一个椭圆,需要记住很多个点的坐标值,而且得到的椭圆还不精确。后来,我们掌握了椭圆的方程,只需要记住4个值:两个焦点F1和F2,两个轴长a和b,如图8-119。
图8-119 没掌握规律的椭圆和掌握了规律的椭圆
**********
了解了以上的知识,我们在建模关联的时候,要学会分辨哪些是不得不记住的,哪些是可以计算得到的。
例如,我们把图8-114的下半部图形的内容改成一个大多数人都很熟悉的领域,并把每两个类之间的“关联”补全(共C(4,2)=6条边),得到图8-120。
图8-120 满满的“关联”,需要保留哪些?
删去3条边,只保留3条边就够了,结果如图8-121:
图8-121 删去冗余“关联”后的结果
当然,这种情况下,那些刷废话的无能之辈可能会祭出“性能”的遮羞布,应对方法前文已讲述。
遮羞布的出现,有助于帮助我们识别无能之辈。初步鉴别之后,如果我们有心去翻查他的各种工件,大概率会发现各种各样的脓包。脓包在手,退可以求团结,需要亮剑时,严谨的建模匕首轻轻一划,脓包破裂飞溅满地。