UML:关联?依赖?组合?
最近在看flask框架,flask实在是太自由了,让用户自己设计程序结构,这不禁让我在考虑软件的组织问题。然而后来我发现拿脑子来缓存程序结构实在是太……蛋疼了,看到一些东西就忘了。然后我想借助UML图来分析程序结构,确实发现了书上的程序结构的一些高明之处。但是呢,我看UML图描述关系的一些词,实在是太模糊了,比如说,依赖,关联,组合,这些词实在是太模糊了,在网上查了不少解释,居然每种说法都不同。
究竟为什么会存在这种模糊性?
UML本身并不是专门用来设计程序的,他的全称是“UniverseryModelLanguage”,统一建模语言。我觉得可能是软件这种东西实在是太抽象了,而UML并不抽象,也并不具体。既不抽象,也不具体,为什么会这样呢?抽象,就是抽去不重要的,留下重要的,抽去模糊的,留下明确的,数学发展出的公理化方法就是抽象设计的典范,但是UML所谓的“依赖”,“关联”的具体含义居然连自己的手册里面都说什么“这只是一些语义用词”,怎么可能达到抽象,而所谓具体,就是有实际的详细情景,但是UML本身又是一种抽象,他屏蔽了代码中的很多细节,但是居然没有明确性,这实在太操蛋了,导致我花了好几个小时居然连一张UML图都没有画满意。
设计模式?
的确,设计模式也存在这种不确定性。有本书叫做“设计模式之禅”,禅,玄学,意思就是设计模式这种东西,他应该怎么做,很多情况之下就是一种很难说清的东西,应该怎么设计,我知道这么做好,但是我也不知道为啥要这么做,设计最后变成了参禅.
两种尝试将之公理化的方法
那么问题来了,我很讨厌这种将这些问题归结于“这只是一种语义问题”,“这只是一种强弱问题”,“这要看实际情况”,怎么将这种“设计模式”或者UML关系进一步明确(公理)化?想了几个小时,觉得可以从以下几个方面尝试
命名空间的污染
在那首著名的《zen of python》之中有一句叫做“命名空间是一个好东西”(怎么又是zen?怪不得不少人说编程是一种手艺,都是玄学啊),函数,包,类,这些在python中的表现都是命名空间。和别的纯面向对象语言实在有很多不同。因为那些编程语言的哲学(编程和哲学到底有多少关系,手动滑稽),本身就假定了它所谓的“面向对象”就是对现实的一种模拟。那么问题来了,为什么要对现实进行模拟,仅仅是为了迎合人的思维习惯,让人像看待一个真实的物体一样看待一段代码吗?我觉得不是,真正的原因应该是命名空间的划分。
为什么程序的设计方法从最早的汇编语言,到结构化设计,到面向对象设计呢?(当然还有很多别的编程范式,这里就不提了,然而,对于别的编程范式,我也很不了解),结合《zen of python》,我觉得是这样的,这些设计的最终目的都是降低编程系统的耦合度,让人在考虑大多数关于这个编程系统的问题的时候,复杂度(表现为解决或者证明这个问题所需引用的代码或者包数量)降低。如何降低复杂度呢,就是把一些代码包起来,放进“命名空间”,让这个命名空间中间和外界能接触的东西变少。
结构式设计这种东西,只能提供函数这种最简单的包装,但也能隔离一些东西。虽然很简陋,但是早期的结构式的编程语言也比汇编语言好多了。为什么呢,因为汇编语言都是在直接操作硬件的,直接操作实际的物体,这就没有办法隔离指令,因为实际的物体多少总是有联系的,而汇编语言是在直接操作实际的硬件。当然汇编语言自己也有一些方法来降低耦合度。
后来有了面向对象编程,对象这又是个什么玩意,比起模拟实际的物体,我更觉得类这种东西是一种用来生成命名空间的命名空间。类本身当然是命名空间,因为他自己有静态方法,有静态成员,那么,这里提一下我所说的命名空间更明确的定义
命名空间
通过一个元素的某种指针(或者是是索引),可以访问在编写代码时当前不能直接访问的任何元素(包括使用当前没有直接写出的代码),那么这个元素是命名空间。
所以面向对象式编程的命名空间有更多层次,有包,类,对象,函数,这就比结构化编程高明多了。
那么现在开始尝试用这种办法来说明可以通过命名空间的污染来分清关联和依赖。或许可以这样定义:
类A对类B存在依赖,是指类A显式地通过某种指针(index),调用了B的命名空间或者B生成的的命名空间中的任何元素
当类A不用另外获得B的命名空间的指针,就能改变B或者B生成的命名空间中的任何元素,就说这A关联于B
看起来这两个人定义挺不错的。据说UML的官方手册说”关联,依赖,这种东西没有代码中的实际含义(有的话怎么会”universery呢?”)”,但是呢……
意图的污染
前面那种定义看起来不错,但是这里还有另外一个问题,所谓“设计模式”是否是说同样功能的代码存在一种最佳的组织形式?好像并不是。因为按照设计模式的原则,一个命名空间一行代码,显然这样耦合度就最低了,但这是不可能的。降低耦合度势必带来命名空间的增多,但是命名空间一则增多,虽然命名空间所包含的代码耦合度极低,但是命名空间之间自己也会出现耦合。
另外面向对象这种东西显然在迎合人类本身“意图”,让人类可以更方便的把对待现实物体的那些抽象方法移植到代码上。
一个设计模式中间的原则叫做“单一职责原则”,就是说一个命名空间尽量只做一件事。那么,我想,这是不是在降低按照人类思维来的代码的阅读难度?因为人类按照自己的思想来“追踪”代码,而一个命名空间就是索引中的一条,按照人的思维,索引中的一条所指向的东西当然是符合“意图”观念,方便在索引中搜索的东西。。。
以上很多东西也是我边写边想的,上课回来有时间接着研究下。
本文太监了,思考方向不对
Update on 2017-11-14 00:06:50
在八个月之后我发现了《计算机程序的构造与解释》,也就是著名的SICP,令我印象深刻的是这本书里面明确地说
编程的本质就是对复杂度的管理
非常意外。既然我本来就有这种想法,而且得到了麻省理工的经典教材支持,我也只能暂时认为这是正确的。