《面向对象葵花宝典》阅读笔记

时间:2022-06-07 13:05:43

满满的干货!推荐大家购买的一本书,里面很多的内容,都是我编程过程经历过的困惑(相信大家都会遇到),如果早点看到这本书,相信当时我也不会困惑那么久了~所以记录总结一下。
PS.欲看此书,不必自宫……

面向对象理论

面向过程与面向对象

面向过程是一种以“过程”为中心的变成思想,就是典型的“计算机思维”,其实就是完成一件事情的步骤,就像生成流水线,它是计算机的基石,本身计算机就是按照步骤进行计算的。

面向对象是一种以“对象”为中心的变成思想,开发更贴近人类的思维特点,更加脱离机器思维,这些对象不需要指定严格的操作顺序,而是指定了这些对象需要完成的任务,以及这些对象如果对外界的的刺激做出反应。我就是我,是颜色不一样的烟火,世界上每个人都是不一样的,但是我们都是人类,按照面向对象的思想,我们都是人类实例化的对象,我们有着不同的处世方法,语言,肤色等等,这才造就了这样丰富的世界,而人类又是继承自哺乳动物的…等等。

PS:不要从代码的角度去思考面向对象,因为代码到头来也是过程执行的,应该从设计的角度去考虑面向对象,提高软件扩展性。

为什么要面向对象

面向对象的特性:抽象、封装、继承、多态。这些特性的核心就是“可扩展性”。有了面向对象,我们可以将变化带来的风险控制在有限的范围内,避免产生全流程或者更大范围的影响,从而降低风险。所以面向对象并不是瑞士军刀,只是一个普通的锤子而已,千万不要拿着锤子到处敲……(多数情况适用于大型软件,就别拿他来写算法什么的了)。

站在你的观察角度,具有相似点的事物就是同一类。
例如:
人和猪是同一类么?从哺乳动物的角度,是的。
人和树是同一类么?从生物的角度,是的。

所以具体的情况要具体分析

类=方法+属性

设计原则:
1、属性最小化,不可再分(适应性最强)。
2、方法单一原则,一个方法只做一件事

对象

真实存在的就是对象,我就是对象,而人是一个类,真正在软件运行中的是”对象“,而不是”类“,类只是程序员抽象出来的东西。
程序员就是软件世界的上帝~这也是为什么那么多人痴迷的原因。

接口

interface,是一组相关的交互功能定义集合。它只能声明方法,不能直接实例化,需要类继承之后,再通过实现类进行实例化,实例化之后也只能调用接口方法。

从软件交互上看,API接口,可以让我们的程序与其他程序交互,但是也要符合API文档的接口规范和数据定义~

从硬件交互上看,USB接口,可以接入很多种设备,但是他们都要遵循USB接口协议标准。

从软件设计上看,更趋近于类中的“行为”规范,例如:有鲨鱼,鲤鱼这2种鱼类的子类,他们肯定只会在水里游吧?不会飞吧?,所以在设计鱼类的时候,我需要定义鱼的接口,让子类都强制实现接口,只能游,不能飞,要是谁能飞,这就违反了鱼类的常理了!(当然,飞鱼是可以飞的,它已经进化了…但是它依旧还是会在水里游的,这个飞的技能,其他鱼类不会,是他自己实现功能),这时候,程序员就是上帝,我要下发个命令让鱼儿们都游起来,生命在于运动~,我不管是鲨鱼还是鲤鱼,你们肯定都会在水里游,这时我只需要调用游的接口。

抽象类

有接口了,我还要抽象类干什么?他们好像都一样啊?

同样,抽象类只能继承,不能直接实例化,但是,抽象类相对于接口,可以有自己的属性、方法,其中还可以声明抽象方法(只有声明,没有定义,子类必须实现定义)
如果说类是从现实对象抽象出来的,那么抽象类就是从类抽象出来的。有什么用?抽象类本质还是类,强调一组事物的相似性,包括属性和方法的相似性,接口只强调方法相似性,用抽象类可以大大减少工作量和代码量!

例如:
设计网站注册的类,有用户名、密码这两个属性,有验证、注册两个方法。先验证,再注册,但是这个时候,用户名被分为手机号,昵称,邮箱三种,所以验证的方式都不同,而其他方法属性都相同,怎么办?将这个网站注册类再抽象出来一个抽象类,仅仅将验证方法设计成抽象方法,这样,在继承这个抽象类的时候,我们只需要实现抽象方法即可,其他内容都继承父类。什么?你说把验证方法弄成接口?那你实例化的对象,只能调用验证方法,最后怎么设置属性,怎么注册成功?

我们可以看出,抽象类看起来是介于类和接口直接的概念,同时具备类和接口的部分特性。

抽象

抽取多个对象中或者类中比较像的部分,抽象最主要的作用是“划分类别”,而划分类别主要目的是“隔离关注点,降低复杂度”。因为这个世界太复杂了……。
第一层:对象抽象成类,例如黑人、白人抽象出人类,野猪、土猪抽象出猪类。
第二层:类抽象成超类,人类和猪类再抽象出一层,即动物类。

在实际设计中,抽象的层次不限,根据业务需要来定。

封装

为什么要封装?世界上每个人都有隐私,苹果也不会把他的IOS系统源码公开。
对于属性来说主要原因就是保护隐私!
对于方法来说,就是隔离复杂度,每个类只关心自己的功能即可,就像空调一样,我按开关,你出冷风就行了,我才不管你内部是怎么运行的,不然哪里还有人会用空调?

面向对象封装分为三种:public(公开)、protected(子类公开)、private(私有)

继承

某种意义上就是遗传。抽象和继承是前后衔接的关系,先有抽象,后有继承。有了继承,我们可以实现很强的扩展,并且可以减少很多代码量,只不过调试,或者看代码的时候,非常的繁琐,经常要跑到父类查看相关定义。

多态

即多种形态,面向对象中可以理解为“子类的调用,可以用父类代替”(因为子类都实现了父类的方法呀)
例如:人、猪、狗,都是动物,都会跑,这个是有用个方法,可以直接用动物类定义对象参数类型
run(Animal obj)
这个时候,我们不管传入那个动物的参数,都可以跑起来,因为他们都是动物的不同形态。

多态屏蔽了子类对象的差异,使得调用者可以写出通用代码,而无需对每个子类来写不同代码。

需求模型

需求!=功能
需求:对客户来说有价值的事情。
功能:系统为了实现客户价值而提供的能力。
需求与功能的区别:只要判断是否对客户有价值。

例如:POS机,“买单”是需求,“商品扫描”、“金额汇总”、“收银”等是功能,买完单,客户就能带走商品,他不会管你这些功能步骤。
打印机,“打印”是需求,“进纸”、“设定”、“与电脑连接”是功能。

需求是项目的开始,是非常重要的,不要到时候你把一坨屎(错误的需求),放到饼干生产线,最后生产出来的还是像饼干一样的屎。

如果需求错了,那么几乎是要把软件重做一遍。需求很重要

客户:“我要一只羊”
记录员:只是记录客户需求,要一只什么样的羊
分析员:要羊来干什么?哦,拿来烤着吃,客户不会烤羊,需要一只烤好的羊。
引导页:目前是夏天,火气重,烤羊不太好,换成冰镇啤酒海鲜大餐如何?

需求分析518方法,我要发~

5W:when(时间相关),where(地点相关),who(参与者),what(客户想要的输出),why(客户遇到的问题,也是客户提出需求的驱动力)
其中,why很重要,只有真正理解了客户的需求驱动力,才能解决客户的问题—-“挖掘客户的问题,实现客户价值!”。

1H:how
注意,分析需求阶段,不要考虑如何实现需求,how就是用来描述整个需求本身的流程是如何运作的。(用例方法)

8C:约束与限制
性能Performance、成本Cost、时间Time、可靠性Reliability、安全性Security、合规性Compliance、技术性Technology、兼容性Compatible

5W+1H关注功能属性,8C关注质量属性。

用例方法

Use Case是用来描述需求的流程。

写法示例:
【用例名称】
【场景】
Who、Where、When
【用例描述】
What、How
【用例价值】
Why
【约束和限制】
8C

第一遍写正常处理
第二遍增加相关步骤的异常情况说明和处理
第三遍增加替代处理。

要画UML图吗?

在学习UML的时候,我也有过疑问,学这些图,好繁琐的样子,到时候做分析的时候,真的有必要画出来吗?,答案是否定的,UML其实就是一种建模语言。你会汉语,不代表你就能写中文小说,你会UML也不能说你一定会做需求分析,不能本末倒置,比如用例图,只是用例的图像化而已,并不能取代用例,我们使用用例图,是为了更好的描述用例。

更重要的是,用例是客户和公司关于产品的共同认识,需求分析过程中,很少有研发人员参与,对于客户来说,他肯定以自然语言来描述用例,他不懂也不会用UML来描述,所以是否采取画图?那个方式能够更好的服务于需求分析,就用哪种,通常情况都是用纯文本描述。

PS:用例图其实是用来描述系统用例的集合,并不会详细描述每个用例的具体步骤,活动图和顺序图才是描述步骤的。

功能提取

有了用例,都好办,功能提取,仅仅需要把动词提取出来。
例如:

功能编号 功能描述 涉及用例
001 银行卡验证 取款、存款、余额查询

领域模型

领域模型是需求分析到面向对象设计的桥梁。
是需求所涉及的领域的一个建模,通俗讲也就是业务模型。主要有2个重要的作用
1、挖掘重要的业务领域概念;
2、建立业务领域之间的关系。

在用例中建立领域模型。
方法:找名称(识别名词、删除不是领域对象的名词)、加属性(属性并没有在用例中明确给出)、连关系(领域模型直接的关系)
领域模型和类图很像。

设计模型

类模型

照葫芦画瓢
1、领域类映射
2、类筛选
3、名称映射
4、属性映射
5、提炼方法
领域模型中没有方法,方法需要从用例中提炼,即找动词,再进行一次筛选提炼
6、分配方法

应用设计原则和设计模式
依赖倒置
接口单一
里氏替换
迪米特原则(最少知识原则)
开闭原则

单抽建工原
适桥装配外享代
模命迭观中备解狀策职访

PS:这里都是需要经验来权衡的

拆分辅助类
一般不需要将拆分的辅助类体现在类模型中,仅在编码的时候拆分即可。

动态模型

1、状态模型
主要用于描述对象生命周期的状态变化,通过状态图,可以了解有哪些状态、状态之间如何转换、转换触发条件等。例如电梯状态图。

2、活动模型
主要描述一个工作流程或者计算流程,当一个处理流程比较复杂的时候,就需要设计活动模型。即活动图

3、序列模型
主要用于描述对象按照时间顺序组织的消息交互过程,即序列图,也称为时序图、顺序图

4、协作模型
主要用来描述按照对象之间的关联来组织消息交互过程,和序列模型类似,只是强调点不同,大多数情况使用序列模型

建模技巧
不需要面面俱到:我们只需要针对一些关键的、核心的、复杂的业务或者功能进行动态建模。动态模型有多种,每种都有不同的使用场景,并不意味着每个业务或者功能每种模型都要涉及,哪种能够将处理过程描述清楚就用哪种。

模型!=代码:模型不等于伪码,更不等于代码,主要是指导代码编写

实现模型

每种语言的面向对象实现特性也不同,但是编程思想都是一样的,至于具体的实现过程,多动手吧。

设计原则

内聚

指一个模块内部元素彼此结合的紧密程度。

以类设计为例,假如一个类里面的函数都只依赖本类的其他函数,那内聚性肯定是最好的。

当模块的元素全部都专注于模块的职责的时候,即使元素间的结合不是很紧密,也符合内聚性的要求。

内聚分类
1、偶然内聚
模块的元素之所以被划分到相同模块,仅仅因为巧合,例如Utils包里面,有很多不同功能的Utils类

2、逻辑内聚
元素逻辑上属于一个比较宽泛的类别。例如,鼠标,键盘,都是输入设备,但是本身的职责又不同。

3、时间内聚
元素在时间上是相近的。例如一个日志记录里,包含释放资源,记录日志,通知用户,这几个处理在同一个函数里,他们就是时间内聚

4、过程内聚
元素必须按照固定“过程顺序”进行处理,与时间内聚相似,只不过时间内聚可以随意调换顺序。

5、信息内聚
元素都操作相同的数据,例如增删查改的方法,都操作同一个数据,所以内聚在一起了

6、顺序内聚
某些元素的输出是另外元素的输入。就像流水线,上一个环节输出,是下一个环节输入

7、功能内聚
元素都是为了完成同一个单一任务,例如,读取文件,其中包括文件存在检查,权限检查,这些方法都是为了读取文件存在的,所以内聚在了一起。

耦合

程序模块之间的依赖程度,内聚和耦合是相反的。

耦合分类
1、无耦合
其实就是高内聚的体现,这样效率非常低下,什么东西都要自给自足,不能利用分工合作

2、消息耦合
类的方法也就是“消息”,A类调用B类方法,这就是消息耦合

3、数据耦合
两个模块之间通过参数传递基本数据(int,string等),例如在教师类中,通过学生ID来查询学生的排名。

4、数据结构耦合
上面的int学生ID变成了StudentInfo类(即数据结构)。

5、控制耦合
当一个模块可以通过某种方式控制另一个模块。例如通过传入控制参数来控制函数的处理流程或者输出,switch case

6、外部耦合
两个模块依赖相同的外部数据格式,通讯协议,设备接口。例如USB,系统与鼠标就形成了外部耦合

7、全局耦合
两个模块共享相同的全局数据时,称为全局耦合,例如全局变量。

8、内容耦合
当一个模块依赖另一个模块的内容(主要是数据成员),称为内容耦合,这种是“病态耦合”,完全破坏了模块的封装性,例如public成员变量

高内聚低耦合

为的就是使类稳定,即变化的风险降到最低。
内聚与耦合是冲突的,需要在这俩者之间进行平衡。

类设计原则

职责单一SRP原则用于指导类的设计 
开闭原则OCP原则是总的设计指导思想 
里氏替换LSP原则用以指导类继承的设计 
接口隔离ISP原则用于指导接口的设计 
依赖倒置DIP用于指导类的抽象的设计 
不要过度设计

设计模式

PS:对于设计模式的书,推荐《设计模式之禅》
设计模式解决的是“可复用”的设计问题,对于性能,可靠性,安全性都不是设计模式能解决的。
设计模式应用的领域是“面向对象”,C语言写设计模式,折腾死你。

所以不要拿着这把锤子到处敲。

找到变化、封装变化、举一反三。

面向对象架构设计

架构是系统的结构和组织,子系统也是系统,也有自己的架构

架构设计师为了隔离关注点,降低复杂度,为了分工合作。

架构=模块+交互。

面向对象的架构设计流程:
业务架构->领域架构->软件架构

业务架构

问客户,客户比你更清楚希望的系统,理想的情况下,需求分析人员可以很好的解答这个问题。

例如,铁路售票,至少包括订票,查票,支付,取票等几个部分。

架构的最雏形就来源于客户的业务系统,是对客户业务系统的模拟。

业务架构和用例模型一样,其实也是文字描述,比用例模型更抽象,只需要描述业务的整体结构即可,也需要包含约束和限制。

领域架构

只需稍微提炼一下业务架构。
1、提取业务模块
2、确定业务模块之间的关系。

然后画图

软件建构

1、根据领域架构,将模块增加“子系统”三个字,大致就出现了软件架构图
2、根据约束和限制,优化系统架构,这需要靠架构师的经验和积累
3、深思熟虑,全面评估各个备选方案的优劣点,挑选最优的方案。

面向对象架构设计技巧

原则

1、客户需求优先
例如,老板要你成本控制在1000元,1000元不是扯淡么,那么你可以对老板说:产品无法满足客户需求。
尚方宝剑,百试百灵。

2、适当超前
唯一不变的是变化,首先满足客户需求,再超越客户需求,不要一上来就要设计一个举世无双的系统。

拆与合

不管是性能,可用性,复杂新,可扩展性,拆都能搞定,然而难点在于“合”,如何整合?
拆:拆硬件、拆地点、拆功能
合:客户端合(客户端将拆分的系统合起来),网络合(通过网络将系统合起来,例如反向代理),中间和(中间件),子系统和(通过接口互相调用)