面向对象:让软件开发变轻松的技术
预测未来最好的方法是创造它
面向对象是软件开发的综合技术
面向对象包括了各种技术,几乎涵盖了软件开发的所有领域:
Java,Python等编程语言
需求规格书
设计内容的图形表示
可重用的软件构建群
优秀设计的技术窍门
业务分析和需求定义的有效推进方法
顺利推进系统开发的开发方法等
以对象为中心编写软件的开发方法
目的:开发效率、软件质量、可维护性、重用性
基本思想:
重点关注各个构件,提高构件的独立性,将构件组合起来,实现系统整体的功能;
通过提高构件的独立性,当发生修改时,能够使影响范围最小,在其他系统中也可以重用。
从编程语言演化为综合技术
混乱之一:术语洪流
继承、泛化、特化、超类、子类、接口、多重继承、属性、关联、集合、委托、重写、重载、访问控制、构造函数、包、异常、垃圾回收机制、框架、类库、组件、设计模式、用例、建模、UML、重构、敏捷开发流程、RUP、XP...
混乱值二:比喻滥用
混乱之三:“一切都是对象”综合征
似是而非:面向对象与现实世界
关键词:面向对象的三大要素(类、多态、继承)、现实世界
如果只理解概念,就容易混乱
类指类型,实例指具体的物
类:属性、方法
实例
消息传递:调用方法
多态让消息的发送方法通用
继承对共同点和不同点进行系统的分类和管理
超类
子类
面向对象和现实世界是似是而非的
现实世界的人和物不是由类创建的
现实世界的人和物并不只是根据消息来行动
明确定义为编程结构
软件并不会直接表示现实世界
与现实世界的相似扩大了可能性
理解OOP:编程语言的历史
关键词:机器语言、汇编语言、高级语言、结构化语言、GOTO语句、全局变量、局部变量
“软件危机”是指人类的供给能力满足不了日益增长的软件开发需求的状况
OOP的出现具有必然性
最初使用机器语言编写程序
二进制数编写
编程语言的第一步
用人类易于理解的符号表示无含义的机器语言
高级语言的发明使程序接近人类
重视易懂性的结构化编程
废除难以理解的GOTO语句
采用顺序、选择和重复结构
提高子程序的独立性,强化可维护性
减少调用端和子程序之间的共享信息(变量中存储的数据)
减少全局变量
使用局部变量、按值传递
使用static限定作用域
实现无GOTO编程的结构化语言
编程所需要的全部功能并不是通过语言规范提供,而是由函数库构成
进化方向演变为重视可维护性和可重用性
汇编语言:使用二进制数/十六进制数记述计算机能直接识别的机器语言
汇编语言:使用符号记述机器语言
高级语言:使用汇总了多个机器语言的“高级”语法进行记述
结构化语言:提供三种基本机构,强化子程序独立性
没有解决全局变量问题和可重用性差的问题
面向对象编程技术:去除冗余、进行整理
关键词:类、实例、属性、方法、多态、继承、包、异常、垃圾回收
具有机构化语言所没有的三种结构
类
多态
继承
解决如下两个问题:
不使用全局变量
除公用子程序之外的可重用结构
类具有三种功能
“汇总”子程序和变量
“隐藏”只在内部使用的变量和子程序
从一个类“创建很多个”实例
汇总
能够将紧密联系的(多个)子程序和(多个)变量汇总到一个类中
优点:
构件的数量会减少
方法的命名变得轻松
方法变得容易查找(配合IDE的智能提示,极大提高效率)
隐藏
隐藏类中的定义的属性和方法
大大减少阅读和修改代码的心智负担
创建多个
实例表示类定义的属性所持有的内存区域
储存实例的变量名.方法名(属性)
一旦定义了类,在运行时就可以由此创建很多个实例
属性是限定访问范围的全局变量
特点:
能够隐藏,让其他类的方法无法访问
属性在被创建之后一直保留在内存中,直到不再需要
局部变量 | 全局变量 | 属性 | |
---|---|---|---|
多个子程序访问 | 不可以 | 可以 | 可以 |
限定可以访问的范围 | 一个子程序访问 | 程序任意位置都可以访问 | 一个类中的方法访问 |
存在期间 | 子程序调用时创建,退出时消除 | 应用程序运行期间 | 从实例被创建到不再需要 |
变量区域的复制 | 在一个时间点只可以创建一个 | 每个变量只可以创建一个 | 运行时可以创建多个 |
实现调用端公用化的多态
创建公用主程序
去除类的重复定义的继承
超类、子类
继承是将类定义的共同部分汇总到另一个类中,并去除重复代码的机制
三大要素的总结
类 | 多态 | 继承 | |
---|---|---|---|
说明 | 汇总子程序和变量,创建软件构件 | 实现调用端的公用化 | 实现重复的类定义的公用化 |
目的 | 整理 | 去除冗余 | 去除冗余 |
记法 | 汇总、隐藏和“创建很多个”的结构 | 创建公用主程序的结构 | 将类的共同部分汇总到另外一个类的结构 |
通过嵌入类型使程序员的工作变轻松
告诉编译器内存区域的大小
防止程序发生错误,例如整数与字符串相乘
将类作为类型使用
强类型
弱类型
其他OOP结构
包
异常
垃圾回收
对OOP进化的总结
切记
我们的目的是编写出高质量、易于维护和可重用的程序,面向对象只是实现该目的的手段
理解内存结构:程序员的基本素养
关键词
编译器、解释器、虚拟机、线程、静态区、栈区、堆区、指针、方法表
理解OOP程序的运行机制
汇编语言,需了解硬件寄存器的结构
C语言,需熟悉指针
OOP语言,需理解内存的使用
编译器与解释器
编译器方式
将程序中编写的命令转化为计算机能够理解的机器语言之后再执行
执行效率高,但编译费时
解释器方式
将源码中编写的程序命令边解释边执行
立即运行,但执行效率低
JAVA将源码编译为中间代码,运行时将中间代码翻译为机器指令后执行
虚拟机:解释中间代码并将其转化为机器指令
通过虚拟机,支持同一中间代码在多个操作系统和多种硬件上执行
使用静态区、栈区和堆区进行管理
线程是命令执行的最小单位
进程是资源分配的最小单位
三种内存区域
静态区 | 堆区 | 栈区 | |
---|---|---|---|
用法 | 在应用程序开始运行时配置 | 开始时分配一定的区域,之后会根据需要再分配 | 后入先出 |
存储的信息 | 全局变量、运行时代码 | 任意 | 调用的子程序的参数、局部变量和返回值 |
分配单位 | 为整个应用程序分配一个 | 为一个系统或应用程序分配一个 | 为每个线程分配一个 |
每个类只加载一个类信息
加载时间点分为:
预先整体加载所有类信息
需要时依次将类信息加载到内存中
JAVA采用后者
加载到静态区
在Java中,加载的类信息包括方法、类属性、常量信息及类名和方法名等符号信息
每次创建实例都会只用堆区
执行创建实例的命令(JAVA中是NEW)时,在堆中分配所需大小的内存,用于存储该类的实例变量
OOP中的内存使用方法的最大特征就是实例的创建方法
牢记“使用OOP编写的程序会大量使用有限的堆区来运行”。当编写应用程序要一下子读取大量信息并进行处理时,必须预先规划该处理会使用多少堆区
在变量中存储实例的指针
存储实例的变量中存储的并不是实例本身,而是实例的指针
指针就是“表示内存区域的位置的信息”
复制存储实例的变量时要多加注意
JAVA,变量中存储的是指针,当在方法的参数和返回值中指定对象时,实际传递的也是指针。
变量中存储的是实例的指针
当将存储实例的变量赋给其他变量时,只是复制指针,堆区中的实例本省并不会发生变化
需复制实例时,请使用clone
多态让不同的类看起来一样
多态是创建公用主程序的结构,即使替换被调用端的类,也不会影响调用端的类
方法表是让不同的类看起来都一样的方法
方法表比单纯的调用方法的做法效率低
根据继承的信息类型的不同,内存的配置也不同
子类直接使用超类中定义的方法
子类的所有属性都会复制超类中定义的属性,“private”属性也一样
孤立的实例由垃圾回收器处理
栈区和方法区中引用的实例不会成为垃圾回收的对象。
从栈区或静态区无法到达的实例,就是垃圾回收的对象
栈区和方法区不要一直引用不再需要的实例
类库、框架、组件、设计模式
重点:OOP带来的软件重用和思想重用
好莱坞原则,“Do not call us, we will call you”,用来形容所有的控制流程都由框架决定,应用程序的处理则使用多态,根据需要进行调用
OOP的优秀结构能够触进重用
OOP -> 可重用构件群:类库、框架、组件 -> 设计模式
类库是OOP的软件构件群
从库中的类创建实例,汇总方法和变量定义进行使用(利用类)
可以将库调用的逻辑替换为应用程序固有的处理(利用多态)
向库中的类添加方法和变量定义,来创建新类(利用继承)
标准类库是语言规范的一部分
在使用OOP进行编程,最重要的是熟练使用类库
将Object类作为祖先类的继承结构
框架存在各种含义
大致分为两种情况:
作为“总括性的应用程序基础”这种比较笼统的含义使用
指代针对特定目的编写的可重用构件群
例如:Spring、Spring MVC,MyBatis
框架是应用程序的半成品
类库,是指利用OOP结构创建的可重用构件,并不限制其目的和使用方法
框架,不只是指利用OOP创建的类库,还指用于特定目的的应用程序的半成品
所有控制流程由框架端提供,应用程序特有的处理则利用多态进行调用。关于应用程序特有的处理,还会利用继承结构预先设置默认的功能
如果各种框架完全适用,可轻松地创建复杂的应用程序。基本的使用方法就是继承框架提供的默认类或则接口,并编写一些方法
世界上可重用的软件构件群
独立性较高的构件:组件
粒度比OOP的类大
提供的形式是二进制形式,而不是源代码形式
提供时包含组件的定义信息
功能的独立性高,即使不了解内部的详细的内容,也可以使用
设计模式是优秀的设计思想集
不依赖编程语言和应用程序的应用领域,对在各种情况下反复出现的类结构进行命名,形成模式
设计模式是前人为了创建便于功能扩展和重用的软件而研究出的技术窍门,对典型的解决方法以及使用它们时需要考虑的要点等都进行了汇总
GoF设计模式
No. | 分类 | 模式名 | 目的 |
---|---|---|---|
1 | 适应设计模式 | Iterator | 逐个遍历 |
2 | Adapter | 加个“适配器”以进行重用 | |
3 | 交给子类 | Template Method | 将具体处理交个子类 |
4 | Factory Method | 将实例的生成交给子类 | |
5 | 生成实例 | Singleton | 只有一个实例 |
6 | Prototype | 通过复制生成实例 | |
7 | Builder | 组装复杂的实例 | |
8 | Abstract Factory | 将关联部件组装成产品 | |
9 | 分开考虑 | Bridge | 将功能层次与实例层次分离 |
10 | Strategy | 整体替换算法 | |
11 | 一致性 | Composite | 容器与内容的一致性 |
12 | Decorator | 装饰边框与被装饰物的一致性 | |
13 | 访问数据结构 | Visitor | 访问数据结构并处理数据 |
14 | Chain of Responsibility | 责任循环 | |
15 | 简单化 | Facade | 简单窗口 |
16 | Mediator | 只有一个仲裁者 | |
17 | 管理状态 | Observer | 发送状态变化的通知 |
8 | Memento | 保存状态 | |
19 | State | 用类表示状态 | |
20 | 避免浪费 | Flyweight | 共享对象,避免浪费 |
21 | Proxy | 只在必要时生成实例 | |
22 | 用类来表现 | Command | 命令也是类 |
23 | Interpreter | 语法规则也是类 |
设计模式是类库探险的路标
类库开发者为了传达设计意图,经常中在类名中加上设计模式的名称
扩展到各个领域的思想的重用
名称 | 说明 |
---|---|
其他设计模式 | 限定于特定运行环境和用途的技术窍门集(J2EE模式、EJB模式和面向多线程的设计模式等) |
分析模式 | 在业务分析和需求定义阶段编写的表示应用程序的问题领域的模式 |
架构模式 | 用于表示软件整体结构的模式 |
重构 | 在不修改既有程序功能的情况下改善内部结构的技术 |
流程模式 | 与系统开发的推进相关的模式 |
反面模式 | 汇总系统开发中经常出现的缺陷及相关的对策 |
化为通用的归纳整理法的面向对象
关键词:集合论、职责分配
软件不能直接表示现实世界
计算机只是承担了现实世界中的一部分工作
应用于集合论和职责分配
在上流工程中,面向对象会提供两种基本结构:
集合论
职责分配
在上流工程中化为通用的归纳整理法
面向对象将成为拥有对事物进行分类整理的结构和表示职责分配的归纳整理法
两种含义引起混乱
类在归纳整理法中表示“现实世界中存在的事物”,而在编程中是指“汇总子程序和变量的结构”
分为OOP的扩展和归纳整理法进行思考
应用技术 | 分类 |
---|---|
类库、框架、组件 | OOP的扩展 |
设计模式 | OOP的扩展 |
UML | OOP和归纳整理法 |
建模(业务分析、需求定义) | 归纳整理法 |
面向对象设计 | OOP的扩展 |
开发流程 | - |
上流工程是归纳整理法,设计之后的下流工程是编程技术
UML:查看无形软件的工具
关键词:UML、类图、时序图、通信图、用例图、活动图、状态图
使用UML从庞大的信息中提取重要的部分,表示为逻辑清晰且直观的形式
UML是表示软件功能和结构的图形的绘制方法
名称 | 用途 |
---|---|
类图 | 表示类的规格和类之间的关系 |
对象图 | 表示实例之间的关系 |
包图 | 表示包之间的关系 |
时序图 | 将实例之间的相互作用表示为时间序列 |
通信图 | 将实例之间的相互作用表示为组织结构 |
复合结构图 | 表示具有整体-部分结构的类的运行时结构 |
组件图 | 表示文件和数据库、进程和线程等软件的实现结构 |
部署图 | 表示硬件、网络等系统的物理结构 |
活动图 | 表示一系列处理中的控制流程 |
用例图 | 表示系统提供者和使用者之间的关系 |
状态图 | 表示实例的状态变化 |
交互概览图 | 将根据不同条件执行不同动作的时序图放到活动图中进行表示 |
定时图 | 采用带数字刻度的时间轴来表示实例之间的状态迁移和相互作用 |
UML的使用方法大致分为三种
表示OOP程序的结构和动作
表示归纳整理法的成果
表示面向对象无法表示的信息
表示程序结构和动作
类图表示以类为基本单位的OOP程序结构,通过将无形的软件表示为二维图形,帮助对整体进行理解和掌握
类图表示静态信息,时序图和通信图表示动态信息
时序图表示实例之间的相互作用,纵轴表示时间,长方形表示实例名称,横向箭头表示方法调用,箭头上面是调用的方法名称和参数
相对于时序图,通信图以实例的关系为中心
类图:类的定义信息和类之间的关系
时序图:将运行时的实例之间的方法调用表示为时间序列
通信图:将运行时的实例之间的方法调用以实例关系为中心进行表示
表示归纳整理法的成果
使用类图表示根据集合论进行整理的结果
类图中将集合表示为类,使用继承表示全集和子集的关系,使用属性表示集合中包含的元素的性质
表示职责分配的时序图和通信图
在用通信图表示程序结构的情况下,箭头表示消息调用,而在表示实现世界的情况下,箭头表示通过对话等进行沟通
类图:表示根据集合论进行分类整理的表示现实世界的事物之间的关系
时序图:将进行了职责分配的人或组织协作完成整个工作的情形表示为时间序列
通信图:将进行了职责分配的人或组织协作完成整个工作的情形以结构为中心进行表示
表示非面向对象
使用用例图表示交给计算机的工作
用例图用于明确表示计算机的工作范围,就是确定对象系统和外部的界限,大长方形表示系统边界,内部是用例,描述了系统提供的功能,外部是用户或其他关联系统
活动图表示现实世界的业务流程
状态图表示用OOP编写的实例的状态迁移、系统整体的状态迁移等各种对象
用例图:交给计算机的工作范围
活动图: 现实世界的工作流程
状态图:外部事件导致的状态变化
弥补自然语言和计算机语言缺点的语言
自然语言 | 计算机语言 | UML | |
---|---|---|---|
目的 | 人们之间的交流 | 向计算机指示作业 | 人们之间的交流 |
形式 | 声音、字符 | 字符 | 图形 |
特征 | 基本语法,但详细语法比较宽松 | 极其严格 | 重视直观理解 |
建模填补现实世界和软件之间的鸿沟
关键词:建模、业务分析、需求定义、业务应用程序、嵌入式软件
计算机擅长固定工作和记忆工作
通过业务分析、需求定义和设计来填补鸿沟
计算机承担了现实世界中的一部分工作,而管理计算机的是软件
业务分析:整理现实世界的工作的推进方法,整理为什么使用计算机(Why)
需求定义:确定交给计算机的作业范围,需求定义相当于让计算机干什么(What)
设计:确定软件的编写方法,相当于确定管理计算机的软件如何实现(How)
建模是顺利推进这3个阶段的工作的技术
建模的目的
业务分析:直接把我现实世界的情形
需求定义:考虑计算机的性质,确定让计算机承担的工作范围
设计:考虑硬件性能、操作系统和中间件的特性以及编程语言的表现能力等,确定软件结构
应用程序不同,建模的内容也不一样
业务应用程序:企业等的业务活动中使用的系统
嵌入式软件:管理电器及各种设备的软件
单机应用程序:在个人计算机或便携式终端等上面运行的软件
基础软件:操作系统、数据库、中间件
业务应用程序记录现实中的事情
业务应用程序中只有数据是无缝的
概念模型用来表示系统中应该记录的信息
用例图只是表示信息的输入和引用,而概念模型则能够表示现实世界中的人和物、发生的事情
当应用面向对象时,由于能够知道现实世界和软件结构的对应位置,所有在现实世界发生改变的事情下,能够立马确定软件修改位置
嵌入式软件替换现实世界的工作
嵌入式软件中设备的研究开发很重要
业务分析,就是整理计算机替换之前的现实世界中的工作的情形
实际开发嵌入式软件的情况下,基本上不进行业务分析
机器的发明和改良更重要
通常先对设备进行设计,再考虑交给嵌入式软件的工作
嵌入式软件中通常不会执行业务应用程序的业务分析工作,而是会对新设备的发明、既有设备的改良等加以研究
使用状态机图来表示全自动工作的情形
在需求定义中,确定在什么状态下执行什么动作非常重要,UML的状态机非常有用
嵌入式软件一直执行单调的工作
面向对象设计:拟人化和职责分析
关键词:内聚度、耦合度、依赖关系、拟人化
高内聚、低耦合
设计的目标范围很广
首先应该定义运行环境,也就是选择并确定采用的硬件和软件产品
然后应该定义软件整体的结构
逐个确定用于实现应用程序各个功能的软件结构的规格和接口
相比运行效率,现在更重视可维护性和可重用性
最重要的是正确运行
相比于运行效率,可维护性和可重用性变得更加重要
OOP的类、多态和继承等结构就是用来提高可维护性和可重用性的
设计模式是应用OOP结构时的技术窍门
为了提高软件的可维护性,需要让修改位置的定位变简单,使修改的影响变小
为了便于重用,需要能够以子系统或构件为单位对软件进行分割
设计目标一:去除重复
设计目标二:提高构件的独立性
内聚度、耦合度
起一个能用一句话表示的名称
创建许多秘密
创建得小一点
设计目标三:避免依赖关系发生循环
面向对象设计的“感觉”是拟人化和职责分配
衍生:敏捷开发和TDD
关键词:瀑布时开发、迭代式开发、迭代、RUP、XP、敏捷开发、TDD、重构、持续继承
敏捷开发是指通过不断进行较小的发布,阶段性地开发软件的软件开发方法
仅靠技术和技术窍门,软件开发并不会成功
系统地汇总了作业步骤和成果的开发流程
限制修改的瀑布式开发流程
瀑布式开发流程的目的是避免返工
瀑布式开发流程的基本前提是软件的修改会产生很大的成本,编写文档的成本要比软件低得多
瀑布式开发流程的极限
最开始阶段要定义系统要求的所有功能,但这不一定能实现
在计算机领域,技术革新非常快
灵活响应变化的迭代式开发流程
从需求定义到设计、编码和测试的一连串作业称为迭代
阶段性地编写软件,进行多次中间发布,并每次都获取用户的反馈
为了尽早发现技术文件,从工程的早期阶段就会编写并运行一部分实际的代码
RUP按时间分解和管理开发
初始阶段
定义系统的全貌
细化阶段
对采用的技术评价,创建应用程序的基本结构。为此要提出一部分极其重要的功能,并实际编写应用程序
构造阶段
开发应用程序的所有功能。建议按照应用程序功能的优先级顺序进行开发
交付阶段
为了实际运行而进行必要的工作
以客户化为前提,可以根据工程的性质和情况来选择所需的内容
打破诸多限制的XP
4个价值
沟通
重视与小组成员和客户的沟通
简单
不拘泥于设计,鼓励从最简单的解决方式入手并不断重构以实现在需要修改时能够随时改善既有程序的内容结构
反馈
立即运行编写的程序进行测绘,并反馈测试结果进行改善
勇气
有勇气改变设计
12个实践
计划博弈
隐喻
测试
结对编程(代码review)
持续继承
现场客户
小型发布
简单设计
重构
代码集体所有
每周40小时工作制
编码规范
快速编写优秀软件的敏捷宣言
个体和互动 高于 流程和工具
工作的软件 高于 详细的文档
客户合作 高于 合同谈判
响应变化 高于 遵循计划
也就是说,尽管右项有其自身的价值,但我们更重视左项的价值
先编写测试代码,一边运行一边开发的测试驱动开发
编写测试代码
编译通过
进行测试,确认失败情况
编写代码,使测试成功
去除重复的代码
在程序完成后改善运行代码的重构
重构是指不改变已完成的程序的外部规格,安全地改善其内部结构
在进行重构的情况下,为了保证代码的外部规格不发生改变,需要准备测试代码,在重构之前和之后运行一下
即使有大规模的修改,也不要一下子全修改,而是逐步进行晓得修改
经常进行系统整合的持续继承
在敏捷开发中,由于会以两周或一个月的较短单位频繁进行发布,所有代码需要保持随时可以交付的状态
持续继承在将代码保持在随时能够交付的状态的同时保持代码质量
准备一个CI服务器,使用工具每隔几个小时就自动运行一次从编译、构建、单元测试、冒烟测试、功能测试和性能测试
不存在最好的开发流程
熟练掌握面向对象
诸如日志记录和事务控制,有一些共同的处理对应用程序都起作用,在面向切面中,被称为横切点
面向切面通过将分散在程序各个部分的共同处理分离出来编写,从而进一步提高软件灵活性
面向服务将复杂、庞大的系统变为独立性较高的子系统的松散耦合结构
目的是编写质量高、可维护性强、易于重用的软件,不是面向对象编程