四. 编程的目的
在前面, 什么是程序那部分, 我们强调过: 程序是用计算机解决问题的. 所以说编程是一件目的性很强的事情, 因此我们在要编程之前, 我们要把问题搞清楚.
你现在作为一个在校的学生, 我们来看一下如果没有计算机的话你们的日常生活:
一所初中老师在学校讲课, 学生在学校学习, 每过一定的阶段教务组织考试, 各学科老师出卷子, 印刷卷子, 发卷子, 学生答卷, 阅卷老师批卷, 然后把全校这一科的卷子按分数排序, 抄到一个表格上形成单科分数校次表给学科负责人, 再把这排好序的卷子, 按照班级分开, 每个班级抄一份表, 形成单科班次表给班级的学科老师做分析用, 最后把每个班的卷子按学号排好序交给教务, 教务收齐各科卷子(每班一组, 按学号排序), 用算盘或者计算器计算每个学生的总分, 把班级学号姓名各科成绩总成绩抄写到一个纸条上, 按照总成绩把纸条排序,把这个排序的序号写到纸条上,这个序号就是总成绩的校次, 把抄写到一张表上, 形成学校排名表给校领导; 接下来把纸条排序不动, 按班级分开, 分开后的每班的纸条顺序就是班次,把班次也写到纸条上, 然后把每张纸条抄到一个表, 发给班主任, 最后班主任公布成绩给学生(剪成小纸条,一人一张), 这需要很多人好多天的时间,中间还有很多出错的风险, 合分算错数, 排序漏掉谁…等等好多问题
是不是很复杂? 全是手动的, 一个学校如此, 那么一个区呢? 一个市呢? 别笑这个原始, 我像你现在这么大的时候(1991-1994), 就经历过全过程的, 甚至是帮老师出卷子印卷子(用前端是钢针的笔在蜡纸上写题, 把蜡纸放到一摞纸上, 用滚筒刷沾油墨刷一遍, 印一张…), 还有合分, 排序… 在没有计算机的时候, 真就是这么干的!
好了, 随着科技进步, 学校终于重金买了一台计算机, 一个键盘有个显示器, 只能显示字符(屏幕显示的就是你打开的终端窗口的样子), 还能在白纸上打印. 想用这台计算机帮学校解决手动算分排名的效率和正确率问题, 为了解决这个问题, 我们的目标: “程序"要来了«学生成绩管理系统»
学校买电脑这件事怎么知道的? 又是怎么说服学校要做个程序的? 记得前面比尔盖茨卖DOS的故事吗…
往往对用户(这里就是学校)来讲,他们不会清楚地表达需求, 至少不是程序员想要的那种描述, 这个时候, 通常的做法就像最上面那段文字那样, 把原始的所谓的业务流程详细地记录下来, 再搞清楚这些"业务"中的问题和最终目的, 解决问题达到目的就是__“需求”__
还有, 在讲13章数据库之前, 学校就这么一台计算机… (这算是约束条件)
软件开发中的角色
在现在的软件产品开发过程中, 有很多角色, 这里把几个有代表性的稍微给你说一下
- 销售(日语称为营业, 我认为也很恰当), 一个软件公司的销售是和客户进行商务沟通的, 可能是客户找上他, 或者他从某种渠道找到客户, 在销售嘴里, 我们什么都能做, 所以, 行业内有个说法, 一个销售人说的话, 你最多信三成…
- 产品经理, 这个角色不一定懂技术, 但是一定要非常理解业务, 通常由产品经理去进行需求调研(就是下面我们首先做的分解), 此外, 产品经理还要画出产品原型图(手画, 用工具软件如Axure PR画都行) 因此产品经理需要理解什么是好的用户交互体验;
- UI设计师, 一般是美术相关专业的, 他们根据产品经理的原型设计用工具软件(PS,AI,Axure,Sketch等)画出经过美化的界面效果图, 一边给产品经理用户和客户确认, 另一边给程序员素材资源用于程序开发(我们这个例子现在不需要, 因为我们还处在字符界面时代, 那时候软件开发没有这个角色)
- 架构师, 负责软件的整体业务架构和基础技术架构设计(技术选型非常重要); 这个角色一般是公司中技术最好的经验最多的人来担任, 是最重要的技术角色, 关系着整个项目的成败;
- 软件开发工程师(我们通常说的程序员), 根据产品经理的设计, 在架构师确定的技术框架下, 实现需求里的功能, 工作就是写代码…
- 测试工程师, 一般就叫测试或者QA, 他们根据设计和需求写测试用例, 然后测试程序员写完的程序, 发现问题(BUG)后, 返回给程序员修改, bug数直接体现代码的品质;
- 实施和运维, 说白了就是到客户现场安装软件(上线)以及日常使用的维护
很多小公司上述角色除了销售外有可能是1个人, 如果销售也是这个人, 那么这个公司可能就是他开的… (又可以拿比尔盖茨来说事了…)
现在有种开发模式是基于UI指导开发,根据产品经理(有时候和UI设计师)设计的界面(UI)进行数据结构设计、代码编写…这种开发模式被称之为 “急功近利式” 开发模式, 但这种模式确实是非常快见到成效的…
现在, 我们用软件开发的思维去解决学校的问题, 现在开始你的角色扮演…
需求分析
需求分析是软件计划阶段的重要活动,也是软件开发中的一个重要环节,该阶段是分析系统在功能上需要“实现什么”,而不是考虑如何去“实现”。需求分析的目标是把用户提出的“要求”或“需要”进行__分析__与__整理__,确认后形成描述完整、清晰与规范的文档,确定软件需要实现哪些功能,完成哪些工作。此外,软件的一些非功能性需求_(如软件性能、可靠性、响应时间、可扩展性等)_,软件设计的约束条件,运行时与其他软件的关系等也是软件需求分析的目标。
所以需求分析实际上是__业务分析__,也就是选择一种业务导向的线索将零散的需求串起来,形成一个体系完整、内容清晰的框架,用来指导后续的设计、开发工作。需求分析就是先分解,再提炼,在这个过程中消除矛盾。要清楚地认识需求分析的任务并不是分析系统如何实现用户的需要!
在"分析需求"这个事上, 最牛的就是Jobs, 他说: “我不做用户调研, 因为用户根本不知道他们需要什么! "
嗯, 就是这么狂, 但, 他是对的… 他改变了世界, 如果你也能改变世界, 你也可以这么干, 可惜, 绝大多数人不是, 我们还是来看看正常人是怎样干的…
需求分析 - 分解: 业务导向的分解
现在, 角色是产品经理(以前, 叫做售前工程师, 或者叫售前咨询顾问… 为什么叫顾问呢? 可能在一群工程师里面, 这个角色真算不上工程师, 一个不懂球(技术)的胖子…但他却懂且会梳理业务 )
分解是人类控制复杂性,认知复杂事物的最佳实践,不管是采用结构化分析方法还是面向对象分析方法,分解是必然的手段。现代需求工程理论更建议采用业务导向的分解,而非传统的系统导向的分解。说白了就是__按“事”的角度进行分解__。它对于联机事务处理系统、管理信息系统而言都是非常适用的方法。
通过沟通和理解上面学校原始做法那段话, 作为产品经理可以明确**总体目标 : 解决初中考试手动算分排名的效率和正确率问题. **
现在开始分解一下学校的业务, 这里的分解意思是把有多少"事"要做, 一件一件列出来, 分解的过程就好像做阅读理解,一句一句读开始的那段话并写下中心思想…
-
一所初中
约束条件, 限定了范围
-
老师在学校讲课, 学生在学校学习
这个我们解决不了, 也不是我们的目标范围,
-
每过一定的阶段教务组织考试, 各学科老师出卷子, 印刷卷子, 发卷子, 学生答卷
这里, 有个和我们程序有关的角色出现了: 教务, 我们把他标记为Role1((教务)), “组织考试”, 组织谁?各科老师, 又一个角色出现了:“各科老师"我们标记为Role2((各科老师)), 因为前面有限定是初中, 那么这里的"各科"就可以明确得出2个信息: 一是指语数英物化五科, 二是他们的操作是基本一样的;
所以完善了约束条件: 语数英物化五科考试
然后看考试, 考什么试? 既然教务是主语,那教务要确定考试名称, 至于出卷/印卷/发卷/答卷现阶段我们系统解决不了,跟我们系统没关系, 所以通过分析得出下面的**“事”**:
graph LR Role1((教务))--确定-->考试--记录-->考试名称--组织-->Role2((各科<br>老师))-.->出题,印卷
-
阅卷老师批卷, 然后把全校这一科的卷子按分数排序, 抄到一个表格上形成单科分数校次表给学科负责人, 再把这排好序的卷子, 按照班级分开, 每个班级抄一份表, 形成单科班次表给班级的学科老师做分析用, 最后把每个班的卷子按学号排好序交给教务
理解这段, 首先阅卷老师是新角色, 我们标记为Role3((阅卷老师)), 批卷意味着要记录分数,什么分数, 每个学生的单科分数;
接下来后面话的意思是: 阅卷老师要计算单科全校排名, 并记录单科学校次表, 提交给新的角色Role4((学科负责人)); 还要计算单科班级排名, 记录单科班次表, 提交给前面出现过的Role2((各科老师))
那么总结出如下的**“事”**:
graph LR Role3((阅卷<br>老师))--记录-->学生单科分数 Role3--计算-->Result1[单科全校排名]--记录-->Report1[单科校次表]--提交-->Role4((学科<br>负责人))-.->年级成绩分析 Role3--计算-->Result2[单科班级排名]--记录-->Report2[单科班次表]--提交-->Role2((学科<br>老师))-.->班级成绩分析
我们最后再分析后面的话:
- 教务收齐各科卷子(每班一组, 按学号排序)
Role1((教务)) -> 记录各科分数
- 用算盘或者计算器计算每个学生的总分,
Role1((教务)) -> 记算总成绩
- 把班级学号姓名各科成绩总成绩抄写到一个纸条上, 按照总成绩把纸条排序,把这个排序的序号写到纸条上,这个序号就是总成绩的校次, 把抄写到一张表上, 形成学校排名表给校领导;
Role1((教务)) -> 计算总成绩全校排名 -> 总成绩学校排名表格 (成果物: 打印报表,给Role5((校领导)) )
- 接下来把纸条排序不动, 按班级分开, 分开后的每班的纸条顺序就是班次,把班次也写到纸条上, 然后把每张纸条抄到一个表, 发给班主任
Role1((教务)) -> 计算班级排名 -> 总成绩班级排名表格 (成果物: 打印报表, 给Role6((班主任))用)
- 主任公布成绩给学生(剪成小纸条,一人一张)
班主任 -> 发布成绩给学生 …. 全校就一台电脑… 所以也不是系统管的事了, 等将来吧
综上, 梳理了需求把业务分解成下面这些事:
graph LR; Role1((教务))--记录-->各科分数--计算-->总成绩--计算-->总成绩校排名--记录-->总成绩校次表--提交-->Role5((校领导)) 总成绩--计算-->总成绩班排名--记录-->总成绩班次表--提交-->Role6((班主任)) Role5-.->学校成绩分析 Role6-.->班级成绩分析 Role6-.剪成.->小纸条-.发给.->Role7((学生))
到这里, 是一个产品经理(“售前”)做的工作, 把需求统过分解, 形成明确清晰的用例, 形成文档以后会给UI以及架构师去进行后续工作, 产品经理接下来还会和UI设计师一起去做原型设计, 反复跟客户进行确认, 例如最终表格的具体形式, 成绩分析具体会分析什么之类的事情… 下面轮到架构师出场了…
需求分析 - 提炼: 建立全局领域模型
分解会破坏其他线索的完整性。如果以“事”为线索,会发现数据需求分解后就会出现相互交叠的情况,也就是在多个业务事件中都涉及相同的类。因此,需要采用自底向上的方法进行提炼。如将每个业务事件中的类进行提炼,抽取出共性的部分,建立针对整个系统的全局领域模型。
通过前面对**“事”的分解, 已经确定了业务, 我们通过业务中的概念以及关系整理出领域模型**,让团队中的成员(虽然现在就我们自己)对业务的理解保持一致;往后可以指导数据库设计、系统功能设计、指导开发。
领域模型是对领域内的概念类或现实世界中对象(例如我们这里的"考试”, “学科”, “学生”,“成绩"是什么意思,都包含什么等)的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。概念比较深奥,其实说白了就是我们把基于对业务的理解画成一个类图(什么是类图以后再说, 先看下面的例子),并画出这些类之间的关系,下面我们一步一步来做提炼。
考试:
graph TB; exm(考试)--1-->考试名称
学生:
graph TB; stu[学生信息] stu-->班级 stu-->学号 stu-->姓名
各科成绩:
graph TB; chn[语文] mat[数学] eng[英语] phy[物理] che[化学] chn-->chnscore[分数] chn-->chncrank[班次] chn-->chnsrank[校次] mat-->matscore[分数] mat-->matcrank[班次] mat-->matsrank[校次] eng-->engscore[分数] eng-->engcrank[班次] eng-->engsrank[校次] phy-->physcore[分数] phy-->phycrank[班次] phy-->physrank[校次] che-->chescore[分数] che-->checrank[班次] che-->chesrank[校次]
总成绩:
graph TB; total[总成绩] total-->totalscore[分数] total-->totalcrank[班次] total-->totalsrank[校次]
- 不难看出, 一次考试有一个名称, 一次考试有n个学生的信息, 一个学生信息对应一个成绩信息, 成绩信息包含各科成绩和一个总成绩, 所以我们把这些提炼到一起,如下图, 把所有叶子节点集合到一起,就是能够覆盖我们全域的模型,各个事件过程用到的要么是它的子集,要么是它的全集:
graph TB; exm(考试)--1-->考试名称--n-->成绩 成绩-->stu[学生信息] stu-->班级 stu-->学号 stu-->姓名 成绩-->si[成绩信息] si-->chn[语文] si-->mat[数学] si-->eng[英语] si-->phy[物理] si-->che[化学] si-->total[总成绩] chn-->chnscore[分数] chn-->chncrank[班次] chn-->chnsrank[校次] mat-->matscore[分数] mat-->matcrank[班次] mat-->matsrank[校次] eng-->engscore[分数] eng-->engcrank[班次] eng-->engsrank[校次] phy-->physcore[分数] phy-->phycrank[班次] phy-->physrank[校次] che-->chescore[分数] che-->checrank[班次] che-->chesrank[校次] total-->totalscore[分数] total-->totalcrank[班次] total-->totalsrank[校次]
到这儿领域模型梳理得差不多了, 我们关系图把它画出来(将来在学面向对象编程的时候, 你会发现这里每一个方块就是一个"类”, 因为它代表了一类事物):
classDiagram 考试 o-- 成绩 成绩 o-- 学生信息 成绩 o-- 成绩信息 成绩信息 o-- 科目成绩 成绩信息 o-- 总成绩
需求分析 - 消除矛盾
在前面的分析过程中,会发现有些需求是相互矛盾、相互冲突的。由于是把收集的信息放在一个预先定义的结构中发现这些矛盾的,因此对矛盾的影响范围会有直观的了解,也知道它影响到哪些层面。这样,就可以很快地找到相应的人员,通过进一步的需求捕获来消除矛盾。
在我们要实现的这个程序的需求分析过程中, 发现这样一个矛盾, 就是碍于技术现状(现阶段) 同一时间只能一个用户来使用, 这个就是单机程序的弊病, 是计算机初期阶段的主要矛盾, 为了解决这个矛盾, 出现了数据库以及CS的程序结构, (这里简单讲讲CS) 随后BS的弊端又随之而来, 程序的改动发布困难, DB资源紧张昂贵, 于是伴随着网络的普及和发展又出现了BS的程序结构,(这里简单讲讲BS)
现阶段, 我们消除矛盾的办法就是, 不用各科老师分别录入成绩和统计成绩了, 全部由教务的一个老师来完成, 既解决了人力(阅卷老师只阅卷,不再需要干其他事情了)又解决了效率还满足只有一台计算机这个约束, 所以这个阶段我们的学生管理系统不区分用户(意味着不用输入用户名密码);
再通过对上面那些事的梳理, 我们完全可以把各科分数的录入和排名都交由教务通过程序来完成, 能达到同样结果,减少了工作量, 最后的结果就是下面这样的:
考试业务流程
graph LR; Role((教务))--记录-->考试名称-.组织.->考试-.->Role3((阅卷<br>老师))-.批阅上分.->科目卷子--提交-->Role((教务))
成绩统计业务流程
graph LR; Role1((教务))--根据-->各科卷子--记录-->各科分数--计算-->总成绩--计算-->总成绩校排名--记录-->总成绩校次表--提交-->Role5((校领导)) 总成绩--计算-->总成绩班排名--记录-->总成绩班次表--提交-->Role6((班主任)) Role5-.->学校成绩分析 Role6-.->班级成绩分析 Role6-.剪成.->小纸条-.发给.->Role7((学生)) 各科分数--计算-->单科校排名--记录-->单科校次表--提交-->Role4((学科<br>负责人)) 各科分数--计算-->单科班排名--记录-->单科班次表--提交-->Role2((学科<br>老师))
阅卷老师: 批完卷子按照班级学号排好序(懒一点就让教务排[阴险]), 交给教务就OK了, 要干的活儿少了很多准时下班…
他们表示很高兴…
教务: 还要录入成绩, 但是以前要用手写, 现在敲敲键盘,相比工作量变化不大还可以接受, 但是不用算总分了, 又不用排序了, 也不用抄到表上了, 直接可以打印, 虽然得替各科分别打印, 但工作量依然少了很多很多…打印完了就下班…
他们也很高兴…
各科老师: 我要及格率和平均成绩…
班主任: 我要各科优秀率和名次分布…
校领导: 这么快就出来结果了? 好! 钱花的值, 多组织几次考试吧…
教育局: 保护学生隐私!不许公布成绩,用ABCD代替…
学生: ……
概要设计
有了前面的需求分析, 在主体业务上程序要做什么的目标已经基本清楚, 但也能看出来和具体编程(用什么编, 怎么样编, 编些什么)还扯不上关系, 因此,需要把这些确定了才行, 这时候就需要架构师这个角色出现, 需要他把飘在空中的需求"落地”…
对于一位架构师而言,对于一个软件系统架构设计工作的内容可以大体分为两块,一块是业务架构, 一块是基础技术架构.
业务架构
业务架构简单来说就是对系统要实现的目标任务进行分解, 形成相对独立的业务模块。画业务架构图实际上就是对业务的一种收集、提炼、拆解、归纳、分类的一个过程。
简单来说可以分为三个核心步骤:分层、分模块、分功能。
- 分层,指的是将业务按照层级区分,每个层级都属于独立的版块。
- 分模块,是指在同一个层级中,有哪些独立模块,可以代表一个完整的产品或是同类型的业务聚合。
- 分功能,是指在同一个模块中,将独立的功能划分出来,该功能可以代表一个业务入口。简单理解就是将一个模块体系中的功能,比较具有代表性的,客户比较关注的,拎出来。
上面是一些人的说法, 另外一些人(倒不能说是杠精)不认可, 我却认为总结的不错, 因为在我的工作中这一步经常画这些图, 所以就按照这个讲了, 多说一句, 这属于软件工程的内容, 软件工程的理论随着时代技术的发展尤其是软件行业的发展太快, 也是在不断变化的, 这在我前前后后的讲解中经常会提到, 总之, 不管黑猫还是白猫找到老鼠就是好猫, 不要特别纠结于形式, 不管画什么图还是做什么描述, 只要能把重要的信息传达出来,就可以嘞, 这才是本质, 所以, 在我们这个程序中, 业务架构的划分, 只要把功能模块划分出来就OK了
功能模块图
通过经验(多数MIS系统都有的共通内容)和对业务流程的分析, 我们为SSMS划分出下面的功能模块
graph TB; SSMS[学生成绩管理系统] --> 用户登录 SSMS --> 主界面菜单 SSMS --> 考试管理 SSMS --> 成绩管理 SSMS --> 学生管理 SSMS --> 用户管理 考试管理 --> 新增考试 考试管理 --> 考试查询 考试管理 --> 修改考试 考试管理 --> 删除考试 成绩管理 --> 录入成绩 成绩管理 --> 查询成绩 成绩管理 --> 修改成绩 成绩管理 --> 成绩统计 成绩统计 --> 单科统计 --> 单科校次表 单科统计 --> 单科班次表 成绩统计 --> 总分统计 --> 总分校次表 总分统计 --> 总分班次表 学生管理 --> 添加学生 学生管理 --> 查询学生 学生管理 --> 修改学生 学生管理 --> 删除学生 学生管理 --> 年级维护 用户管理 --> 添加用户 用户管理 --> 查询用户 用户管理 --> 修改用户 用户管理 --> 删除用户 SSMS --> 系统设置 --> 修改用户名 系统设置 --> 修改密码
技术架构
基础技术架构, 简单来说就是做技术选型。选择要支持的操作系统、选择编程语言、选择技术框架、选择第三方库,这些都可以归结为基础架构方面的工作。
基础架构的设计,考验的是选择能力。背后靠的是技术前瞻性和判断力。这并不简单。大部分架构师往往更容易把关注点放到业务架构上,但实际上基础架构的影响面更广,选错产生的代价更高。架构师之间的差距,更大的是体现在其对待基础架构的态度和能力构建上。真正牛的架构师,一定会重视技术选型,重视基础平台的建设。
现阶段我们假设的是计算机普及前的初期阶段, 受硬件设备约束条件限制, 没有更多的选择, ,后面慢慢的有了局域网,广域网,云服务等,技术架构会变得越来越重要…
关于数学建模
数学建模,就是根据实际问题来建立数学模型,对数学模型来进行求解,然后根据结果去解决实际问题。
数学模型是针对参照某种事物系统的特征或数量依存关系,采用数学语言,概括地或近似地表述出的一种数学结构,这种数学结构是借助于数学符号刻划出来的某种系统的纯关系结构。从广义理解,数学模型包括数学中的各种概念,各种公式和各种理论。因为它们都是由现实世界的原型抽象出来的,从这意义上讲,整个数学也可以说是一门关于数学模型的科学。从狭义理解,数学模型只指那些反映了特定问题或特定的具体事物系统的数学关系结构,这个意义上也可理解为联系一个系统中各变量间内的关系的数学表达。
很多问题需要复杂的分析, 通过建立数学模型, 转化成数学问题来解决, 我们这个程序太简单了, 但是也有几个数学问题, 例如:求和(呵呵), 排序, 求平均值等等…
如果再简单一点说, 数学建模很像解小学时候的应用题, 有一群鸡和一群兔子在一个笼子里, 上面有35个头,下面有94只脚,几只兔子几只鸡? 你是怎么建模的?
假设 有x只兔子,y只鸡, 头的数量m = x + y 脚的数量n = 4x + 2y
由题干知, m = 35, n = 94…
是不是就是数学建模? 2) 是不是就是用数学模型求解具体问题?
数学建模是指利用数学方法和技术来描述和分析现实世界中的问题,并通过建立数学模型来解决这些问题的过程。数学建模通常涉及以下几个步骤:
-
问题定义:明确问题的背景和目标,确定需要解决的具体问题。
-
建立数学模型:根据问题的特点和需求,选择合适的数学工具和方法,建立描述问题的数学模型。这可以涉及数学方程、统计模型、优化问题等等。
-
模型验证与分析:对建立的数学模型进行验证和评估,检查其是否能够准确地反映实际问题,并且对模型进行分析以获取有关问题的信息。
-
解决问题:利用建立的数学模型,通过数学计算、模拟实验等方法,求解模型,得出对应于实际问题的解决方案。
-
模型评价与优化:对解决方案进行评价,检查其是否满足实际需求,并对模型和方法进行优化,以改进模型的准确性和解决问题的效率。
数学建模不仅在工程、物理、经济等应用领域中具有重要作用,也在科学研究中发挥着重要的推动作用。通过数学建模,我们可以更好地理解和解决复杂问题,并指导实际应用。