首先说明,接下来 这部分内容,跟面向对象没什么关系,只是描述出我们接下来 "需要做什么 ". 大家都知道电梯是怎么回事了,所以获取需求的过程我就不啰嗦了,直接把最后结果描述出来.(对于计算机专业学生或软件工程毕业设计的需求分析结果应该有些参考意义...起码可以看出怎么样的结果才真正有意义) 电梯楼层 1-10 楼(也就是没有什么地下室也没有中间跳过某些楼层,最普通的情况),一共有2部电梯. 如果一个在n楼(1 <10)的乘客按了下行按钮,那么下一个正在向下走的电梯到了n楼必须停下接收乘客. 如果电梯里已经没有乘客了,电梯应该留在原位置直到再次投入使用. 在将乘客送到目的地以前电梯不允许反向运动.(也就是电梯比如把乘客从9楼带到楼下,如果在走到4楼的时候5楼有人要下,电梯不能从4楼回5楼去,而要把乘客带到楼下再回来) 满载的电梯不再收人. 电梯里有个按钮面版,里面有10个楼层的按钮. 按下按钮表示该楼层变成一个目的地,按钮会发光,到达以后按钮不再发光. 建筑物2-9楼层有一个按钮面版,上面两个按钮分别是向上和向下.1楼只有向上,10楼只有向下. 电梯有一个控制中心来自动控制,不用人手干预. ...............(其他,略) 第二部分 到底上面的需求描述中,哪些东西可以成为我需要来 "面向 "的 "对象 "? 首先,我们要选择出候选的对象,然后再在候选对象里选择真正为程序设计所使用的对象. 候选对象的选择有很多方式,比方说 "名词短语频率分析 ",对上面那段去分析看哪些名词出现次数很多,说明可能比较重要,可以直接拿来当候选对象. 比如楼层,电梯,按钮,乘客,都出现很多次. 当然还有另外的方式,比如 "按方面建立 " (PS: I 'm sorry,我是个实用主义者,只要目的达到了...我不喜欢像一些书本上那样用些概念糊弄人,上面红字的两个方法的名称是我临时取的.....也许不好听,也许他们有更优美的名字.....反正这不是我们该担心的,留给教材编写人员取操心吧...) 从 人\组织\设备\物品\业务\事件\表格 几个方面去找对象去. 这里要注意的是对象的选择要看具体情况的,比如 .... 我们把 "放飞希望 " 作为一个具体的对象实例 ( ^_^ ), 如果在医疗系统中,可以抽象成 "人 " 这个类,由 脑袋\身体\手\脚......等部分. 如果是在教育管理系统中, 就不能这么处理,可能要抽象成 "老师 " 这个类, 包括 教学年限\工资\所教科目\..... 等内容. 还有一点需要注意的是不要跨过系统边界. 系统之外的事情就不要管了,就说我们这个电梯调度, 电梯的生产日期啊什么的,还有乘客的姓名, 都根本雨系统无关,这些是不需要的. 最后有一点被非常非常非常非常非常多的工程师们所忽视的就是建立面向对象分析模型的时候, 只应该考虑逻辑,而不要去考虑跟实现技术相关的东西. 比如按钮是塑料的还是金属的, (当然这个很明显是分外的事,大多数人在做调度分析的时候不会考虑这个,不过有些情况却很隐蔽..) 现在回到我们具体问题,假设现在我们列出来的初步的清单是这样的(可能100个人有100个列法,不过没关系,这是正常的...): 电梯\电梯门\电梯位置\乘客\目的地按钮\大楼..... 这离真正可以用的对象还差很多, 我们需要对每个词进行分析,看看到底是不是,值得不值得把这个当一个对象来考虑.我们分析从以下几个方面看: 它在系统里有功能吗? (比如电梯天花板 就该删除掉....如果有的话....) 其他对象需要这个对象帮忙做点什么事吗? (比如乘客需要电梯把他带上去...) 其他对象需要这个对象的数据吗?(比如控制中心明显需要知道电梯现在在哪..) 这个对象是不是包含了有用却不对别的对象公开的内容.(比如电梯上面的吊链和发动机明显是有用的,不然电梯无法动也就无法调度了, 但是明显对于其他对象,都是不需要公开的, 不管是乘客还是控制中心都不需要控制电梯的马达---这里可能有人要有意见了, 控制中心不控制电梯马达么? 答案是不该控制,控制中心应该只告诉电梯你要向上还是向下还是停, 至于马达转多快怎么转,是电梯自己的电路来控制的才对, 责任分开明确有利于系统的维护.) 这个对象有没有一个生存期,是不是描述了 产生--各种运行状态--消亡 的信息.比如一次电梯召唤-(谁想个好听点的名字? 呵呵....) 产生是用户按了向上或向下的按钮. 然后进入 "电梯来 " 这个状态, 再进入电梯停(包括开门,乘客进去,关门)的状态,然后又是 "电梯走 " 的状态,再之后是电梯再停给用户下去,然后这个电梯召唤以电梯停止移动等待下次召唤作为结束. 如果向应用领域的专家(这里比如是电梯工程师)描述这个对象,他是否可以马上明白重要性? (比如如果你对一个电梯工程师说 "调度工厂 ",他应该就不明白..因为工厂模式是面向对象设计的手段,跟电梯这个主题本身没多少联系) 以上列出的6点,不一定要每点都符合,不过如果每点都不符合,那不用考虑...删掉吧........ 然后考虑系统以外的情况了. 这些对象中: 系统能识别吗? (比如对于超重还是不超重...有些就可以识别,有些电梯就不能..) 哪些是系统必须做出响应的(用户按的按钮明显就是一个...) 对于发生的事件(比如按下按钮,比如电梯门打开) 哪些对象可以识别它? 有没有没用上的? 还需要其他对象吗? 有没有不能识别又不需要响应的对象(比如用户的帽子???? ^_^) ,不过要注意,并不是绝对的,在极少数情况下,不能识别又不需要响应的对象是有用的.不过那是后话了,先不考虑它.... 系统需要跟其他系统相连么? (比如,跟火灾警报系统相连, 火灾发生时不准打开电梯门...这里当然我们不考虑这么复杂,但是毕竟是存在这样的东西) 用于沟通两个对象的对象是不是不小心漏掉了? (这是常见的) 在你删除一个候选对象的时候,问问: 去掉以后系统会怎样? 如果我要重用一个包含了这个对象的一大块功能,这个对象到底会给真的用上么?(注意重用是非常重要的,绝对不要为了一个地方而写程序,程序要可以在不同地方重用,就像不会有生产一个电视机的电视机厂一样) 不断重复上面的过程,最终,你应该从不断增加新东西和不断删除东西后获得一个有些用的列表,假设如下面这样: 到达事件: (就是表示电梯到了,包括开门,进电梯,关门..还有灯亮灯灭等一系列内容..) 到达按钮面版(就是电梯里那10个按钮) 电梯(这个没什么疑问了........) 楼层 ...............................(其他略) 然后要做的事情是给每个词一个文字描述,比如 电梯的描述 如下 Elevator (类名一般请不要用中文.....虽然你要用中文也行....):执行电梯的控制(上走,下走)和报告功能(告诉控制中心现在自己在几楼..), 内部封装(就是不对外公开它是怎么做的,只公开结果)了 控制电梯运行(马达怎么转..), 报告电梯位置,识别电梯是否准备就绪的各种服务. 他包括的属性是电梯的 运动方向\位置\状态 方面的信息. 最后出来的对象不可能证明就是对的,也一般不会全错, ^_^ 人的问题.....注意最终用户也许会对这个有影响,所以最好还要问问他们,如果他们提出些比较麻烦的意见,如某种特定实现技术(比如如果电梯这个楼是一家生产音响的,也许老总有想法就要在电梯里有个音箱,电梯每到一层楼会报一次位置........-_- .....) 应该用专业角度劝他不要这样,如果他一定要.........那你也没办法了,谁让他是出钱的... ^_^ 这个模型可能会在后面被修改很多,做好心里准备. 最后说一下命名, 对象的类名 应该是个类而不是个功能.或一个属性. 比如 可以叫 "电梯 " 却不好叫 "电梯颜色 "或 "电梯上升 ". 对象类名最好全部都惟一,实在不好惟一了,就用不同的包分开,就像把相同文件名的不同文件放不同的文件夹里就行. 对象名称必须在应用领域有意义(也就是在这里必须在电梯界有意义, 而不能只在软件界有意义..) 用 名词或 形容词+名词, 不要用 名词+动词 的方式. 不要出现 "和 ", "或 ", "与 "之类的内容.(比如人就叫 Humen 不要叫 LadiesAndGentalmen.... ^_^ ) 现在,大家列出些什么对象了呢? ArrivalEvent ArrivalPanel DestinationEvent DestinationPanel Elevator ElevatorMotor Floor OverweightSensor SummonsEvent SummonsPanel ....................................................... 大概是这样的一些东西,(可能跟我这个差十万八千里,不过没关系,那也是正常的.....如果照上面说的做了,那应该也错不到哪去..) 好了,.....今天先到这,未完待续 抛砖引玉, 思考面向对象是什么? (面向对象系列之二)
上一个文章驳斥了别人误人子弟,不过深刻记得十几年前老师就教过,
如果你自己不能做得更好,就不要说别人不好.
所以这篇聊聊跟面向对象有关系的话题,希望抛砖引玉.
先考虑一个现实问题,
大家都熟悉的手机发短信.
来看看早期(A
大约是汇编语言时代),中期(B
结构化),现在(C
面向对象)三种思想下的不同实现.
我说的是思想,
因为虽然现在大家使用着面向对象的工具,但是大部分程序员的思想依然没有面向对象.
比如现在我手下这群程序员里有面向对象分析和设计能力的也就一个..
用最面向对象java和C#也可以写出杂乱无章完全不面向对象甚至不结构化的程序.
注意到现在我们的手机号码分成移动和联通两种,
虽然对我们来说,不过是号码不一样收费不太一样,没多少区别,但是两家的短信接口可是完全不一样的.
假设程序要求
用户在界面上输入手机号码(TextBox1),输入一条短信内容(TextBox2),按确定(Button1),就可以把短信发到那个手机上
A
一步一步走,该干什么就干什么...看看伪代码:
st***号码
=
TextBox1.Text;
st***内容
=
TextBox2.Text;
int
第3位数字
=
int.Parse(号码.Substring(2,1));
//把第3位取出来,用来判断是不是移动的手机
如
1390000000
就取出一个
9
if(第3位数字
>
3)
{
....
....
....//这里是一堆长长的代码用来发送***的短信...省略,我们这里只说程序的思想..不涉及技术细节
}
else
{
....
...
...//又是一堆长长的代码用来发送***的短信
}
B
写一个库,定义出发送***短信的函数和发送***短信的函数,还有判断的函数,假设函数原型分别是
发送移动短信(st***手机号码,st***内容);
发送联通短信(st***手机号码,st***内容);
bool
是否是移动号码(st***手机号码);
然后写程序如下:
if(是否是移动号码(TextBox1.Text))
发送移动短信(TextBox1.Text,TextBox2.Text);
else
发送联通短信(TextBox1.Text,TextBox2.Text);
C
定义一个抽象接口
"短信接收者 ",
由
"*** "和
"*** "
两个类分别实现接口.
各自实现发送短信方法.
然后构造一个
"手机工厂 "(一时想不到好的名字,暂时叫这个吧)
,
接收一个号码,返回一个
"短信接收者 "接口(里面根据接收的参数,可能是***或***)
然后程序如下(一行..):
手机工厂.获取接受者(TextBox1.Text).发送(TextBox2.Text);
或写成这样清晰点:
st***号码
=
TextBox1.Text;
st***内容
=
TextBox2.Text;
手机工厂.获取接受者(号码).发送(内容);
OK,对于上面3段伪代码
大家有什么想法?
第3种是不是看起来有点
爽?
也许把,也仅仅是看起来那么一点爽,没什么大不了.
没错,面向对象是在大型的地方更能体现优势,一小堆是展现不出来的.
我们假设程序中一共有100个这样的地方(比如一个是发短信的,一个接短信的,一个打电话的,一个上网的.....)
那么对于A程序,很抱歉,非常要命,要在100个地方复制代码,复制100份,然后对其中99份做修改(或多或少,总要改点..)
B程序只是在每个调用的地方加几行,可以接受.
C程序在调用点也是加1行,同样也可以接受.
这个时候,结构化和面向对象共同的优点体现出来了,复用性
(教科书中讲面向对象总是说说复用是面向对象比其他方法的优势,其实结构化本身就是可复用的)
A方法差不多该抛弃了........这就是结构化发展起来以后,
非结构化很快面临淘汰地步的原因,因为在软件稍微大点,就出麻烦,写写单片机小模块还行.
软件在一天天变大变复杂,仅仅是变大变复杂而已?
当然不是.
也变得多变.
用户的需求时时在变.软件也容易变,.
回到刚才的问题,
现在不是有小灵通么?
你又需要多一种类型,变成
小灵通\移动\联通
3种类型.
那么对于
A
,灾难发生....修改程序的成本不比重新做一个少.
对于B
需要去100个调用的地方多加一个if来判断,然后多加一个对应小灵通的函数.
修改量有点大,不过也不是不行,因为毕竟现在的工具发达,你可以查找--替换.
不过程序是需要测试的,你替换一个地方,就需要多测试一个地方,成本高.
对于C
多加一个实现
接口的
"小灵通 "
类
,
然后修改
"手机工厂 "的
"获取接受者(st***号码) ".
一共2处,测试也只要再测试
这个新类
还有一个方法.
C
方法
面向对象的优势在这个时候体现出来了.
有人这个时候出来抗议了,如果程序写的多了,经验丰富了,有人会看出我上面那些假设的漏洞,就是B
并不是最好的结构化方法,
因为
其实有更好的用一个函数来实现判断类型
那样就跟
C
一样,只要改很少的地方了.
没错,
那样C和B又公平平等了,C还是没什么优势.
请注意2点
第一:
"面向对象 "
不是指
面向对象
的
编程语法,
而是一种思想.
那样写其实
B
已经拿到了一点面向对象的思想了
只是封装在非面向对象的语法中.
第二
不面向对象的确可以写出低耦合的,高效的,可维护的,很牛逼的程序.
但是那是需要很高造诣的人来做的事.
因为没有类的封装性,名字空间的隔绝
还有全局性的变量在程序里走,
要靠程序员自己去避免这些 "可以做,可以方便地做 "却 "会对未来维护带来灾难 "的操作,
对程序员要求很高,你要自觉不用全局变量,就像以前自觉不
用goto语句....还要自觉把功能分好摆好,
需要的分析设计技术是很高的.
而写出同样质量的面向对象程序,只要略知道设计模式的人就都可以了.
这就是面向对象大行的原因之一.
有人说,面向对象就真的封装了?可重用了?
可是我看见很多C#和java程序错乱复杂,
根本拿不出一个
"块 "
出来用,你拿了
"块A "
就调用到
"块B ",
非要把 "块B "也拿来..然后又要
用到无关的C,D,E,F.....最后出来一大落,而且99.9999%是我不需要的,我就只需要那0.00001%而已....
这是现实,的确,至少我看见的代码里垃圾代码占多数(这里是指可以实现功能却很 "有臭味 "的代码),
这主要有一个很大的原因是写代码的人没有面向对象的思想,有的只是面向对象
工具包装的面向过程思想,而且连结构化都说不上.
不是面向对象的错.
差不多,有些人现在认同面向对象了,也知道这不是书上随便说的那些苦涩的概念了,不过还是不明白怎么个面向对象法.
我再换个话题说说,不说手机吧,
说衣服,服装厂生产衣服.
衣服有颜色,有大小,有款式....
看看一个设计,在不同的人手里是什么不同的方法.
现在服装厂要
生产一批
蓝色的,
小号的,
女款的...的衣服...
A
:
衣服
衣服1
=
new
衣服();
衣服1.颜色
=
兰;
衣服1.号码
=
小;
衣服1.款式
=
女式;
.....
然后new
出好多件来.赋值好多下....
现在问题是突然说不要兰的了要红的,
哎哟....改啊改....
当然你可以在循环里做这个,但是如果每件衣服除了颜色和款式一样,
大小是不同的又如何?
有个B想到了一个好的设计了.
定义一个衣服类,
然后把
大衣服
作为一个
子类,小衣服是另外一个子类.
那么方便了一些.
不过又有问题,如果再改需求.....要求大小跟男女固定的,颜色可以变,
难道又再定义出兰衣服类和红衣服类.....那还有完没完啊........依然不是好设计.
C
面向对象有一个利器叫
"设计模式 ",
学面向对象基本上这个是必修,
我们用用设计模式中的原型模式,构造出一个原型
假设是一件
大的男装,
然后通过Copy这个原型就可以
得到一批大的男装,然后给各种颜色就行.
如果是再变化,那么我们不需要什么变,只要再构造另外一个原型,然后用代码Copy这个原型,又可以试用新的变化了....
(今天的内容基本是面向新手级程序员和学校里的高手学生的,未完待续....接下来将会是一个完整的面向对象设计分析例子,真正体会面向对象的优势和如何面向对象分析设计)
|