面向对象
设计模式是gof总结前人在书中or系统中大量反复使用过并取得成功的组织代码的方式.
设计模式的作者在保证内容准确的基础下尽可能简单的描述设计模式.设计模式整理出来的目的是便于理解
设计模式以名称
--> 问题
--> 解决方案
--> 效果
的方式来描述设计模式.
设计模式是驾驭抽象型的技术.很多时候,我们无法理解gof设计模式的原因是我们没有其中的抽象性.在gof的设计模式中,每个设计模式的描述中已经清楚的描述了各种抽象.
设计模式的本质是:用添加复杂性,获取到其中的灵活性.设计模型不实现现有的任何功能.很多时候会导致过渡设计,会导致额外添加了很多复杂性.
在适用设计模式之前需要考虑一下:
1.在抽象设计模式的时候,需要添加哪些新概念.概念越多,越复杂
2.设计模式本身的复制性
3.一旦引入设计模式,所带来的的灵活性,我们是否需要遵守.后来的人是否能够理解这些注意点
4.团队中其他同事是否熟悉这些设计模式
5.增加代码静态分析的难度,导致状态难以判断.
将我们日常解决的现实问题整理为易于理解的形式.
面向对象是如何工作的包括的内容的部分
1.面向对象的基本概念:面向对象为什么会出现;面向对象包括哪些部分内容;面向对象程序的运行的机制以及内存使用方式;面向对象的可重用技术-> 程序的可重用 + 设计模式的可重用
2.面向对象辅助技术:uml语言--> 通过将不可见的程序的结构和流程进行可视化的工具;适用UML进行建模的内容;面向对象设计的思想和窍门--> 实现软件的可维护性和可重用性,通过将软件拟人化然后为其分配职责;软件开发流程:统一开发流程和极限开发流程.
3.面向对象开发流程的未来是函数式编程.
面向对象的基础
1. 让编程变得轻松的技术
以对象为中心的编写软件的开发方法(面向对象为什么让编程变得容易)
面向过程的软件开发方法:是以整个系统的功能为目标,将这些目标按照流程分解为小的部分,然后逐步实现.
面向对象的软件开发方法:将这个系统分解为各种小的构建,同时将这些构建组合起来实现整个系统的功能.在这个过程中,提高构件的独立性.
当这个软件需要变更或者软件规模变大的时候,面向过程的软件将需要修改很多地方,同时软件的易用性也下降.而面向对象的软件由于各个构建相对独立,修改会降低.
面向对象技术演变为一组技术的合集
以面向对象技术为核心,演变出来的可重用的技术包括:类库+框架;提炼出来的常见的设计模式
在软件开发流程中:问题定义 + 需求分析 + 系统设计; --> 统一建模语言.
面向对象技术难以学习的原因
1.术语滥用
2.滥用比喻,在用比喻的时候,不会描述当前对象的结构和目的
3.一切都是对象的滥用.面向对象是以"对象为中心",但并不是认为所有的一切都是对象.
相对的我们需要通过一些机制来解决以上情况:
1.对于术语,尽可能小的范围使用术语,即使适用术语也解释清楚
2.在使用比喻的时候,也会明确主旨
3."以对象为中心"和编程结构来把握事务的事项作为不同的内容分开介绍.对象并不能完全描述整个现实世界.
2.事实而非的面向对象
面向对象中一切都是对象和现实中的对象并不相同.
- 在面向对象中,现有类,然后又实例.而在现实中,是现有对象,后有类的过程.
- 在现实中,一个具体的事物在不同场景下会有不同的定位.例如一个人可以是一个工人,也可以是一个父亲,也可以是一个丈夫.相对的在很对面向对象中并不支持多从继承.
- 同样一个事物,在现实中,会随着时间的变化而变化,例如:羊羔编程山羊.但是面向对象中是不会变的.
4.在现实世界中,有很多事情的发生并不是依赖于消息的传递而发生的.
明确定义为编程结构
面向对象三大要素中:
1.封装是将属性+操作封装在一个类中,实现独立性高的构件;>
2.继承和多态:能够帮助实现功能复用,创建通用性强的代码.
3.实例能够运行在内存的堆上.
面向对象不能表示整个世界,但是只能表示在某一些边界下的世界的一部分特性.
3.面向对象的历史
整个编程语言的历史是随着技术不断的发展,不断的提高编程语言的易理解性,可维护性,简单性和易复用性.
- 二进制的编程语言(16进制)
- 汇编语言(简单的具有语义信息的指令,相比于二进制,汇编变得可读了.通过汇编编译程序转变为二进制程序)
- 高级语言(提供了更加有描述性的语言,系统只提供一些列的规范,其中有变量和运算符号的概念Fortain,CoBol语言.其中支持goto,全局变量的使用)
- 结构化编程语言(以函数为基础,不允许适用goto,只使用 顺序,分支,循环三种结构化方式组织语言.同时强化函数作为共享的基础.同时使用局部变量+值传递的方式实现函数共享.)
5.面向对象语言(将全局边浪和)
4.面向对象的特点
面向对象的三大特点:封装,继承,多态.
1.封装
封装有三个特点:
1.聚集:将相关的状态和方法封装为一个称为类的组件.这样能够将一个复杂的系统进行分类,降低复杂度.
2.隐藏:提供了访问控制机制,只有在这个类中相关的方法才能访问当前类中的变量.同时当前的变量可以随意的更改,这里可以将影响面控制在类中
3.创建多个实例:一个类可以创建多个实例,这里的实例对应的是内存中的一个内存块,通过实例的名称+点来访问实例的方法.
变量有三种:全部变量 , 实例变量 ,局部变量.
这三种变量访问空间越来越小,访问控制越来越严格.这里应该优先适用访问空间小的变量.
2.多态
多态:统一调用子程序的逻辑结构,创建通用主程序的结构.
面向对象通过:多态实现可扩展性.
3.继承
继承是将多个子类中定义的相同的方法和变量抽象到同一个父类中.消除重复代码的结构.
面向对象类型的优点
明确类型的优点是:1.能够通知编译器分配足够的空间,2.通过编译器帮助检查不合格的操作,例如:数组加法等明确的错误
面向对象其他的高级功能
- 包
提供将多个类进行汇总的机制.同时也能够运行相同名称的类出现的可能. - 异常
异常是以一种特殊的方式返回函数调用错误的机制.
异常的优势:对于普通的错误码的方式,这种会出现错误传递,每一层都需要判断错误码,来中断当前系统调用.有了异常后:通过中断能够中断当前执行的流程,以及错误的上下文提示信息.让编码变得容易.
3.垃圾回收
对于创建的多个类的实例,内存空间由系统自己执行,而不用程序员自己来处理.
总结
面向对象的三大特点:封装,继承,多态
封装是面向对象的关键,将强相关的全局变量和方法汇聚到一个类中:这样能实现:分类汇聚的特性,隐藏访问控制特性,能够创建多个实例的特性.封装能有效管理系统复杂度
继承:将多个类的相同的属性和方法提取到一个类中,这样能减少系统的重复.
多态:建立统一调用子程序的逻辑.有效提升系统的复用和灵活性.
面向对象中也提供了类型的强制校验:
- 强类型能够在编译阶段就能够知道当前类型应该分配多少空间.
- 强类型校验帮助校验一些较低级别的错误,例如:数组相乘等错误.
面向对象的高级特性
包:在类型的基础上提供了更加高级的类的汇聚的机制,进一步降低代码管理复杂度
异常:异常是系统提供的一种返回函数错误结果的方式.相比于错误码的方式,一方面能够中断当前程序,然后自动往上传,而非错误码那种每一次调用都需要判断错误码?另一方面:异常能够提供更多发生异常的上下文信息.
垃圾回收:相对于普通的面向过程语言,需要每个函数 自己管理使用的内存空间,面向对象提供了自动回收的机制.
理解内存结构:程序员素养
内存结构对应了面向对象中的各个实例在运行期间的物理展示.
5.1 理解OOP程序的运行机制
不同的语言我们需要了解的运行机制是不同.
1.汇编语言,开发需要了解寄存器,
2.c语言,需要了解指针
3.java语言,需要了解 内存
5.2 两种运行方式:编译器与解释器
编译:直接将开发语言的代码编译为机器能够直接运行的机器码.编译的优点是一个代码需要在不同平台上进行编译.同时编译时需哟啊消耗额外的资源.
解释:能够变读源代码,边执行.这样能够在不同平台上执行,缺点是性能会略差,同时解释执行的时候如果出现错误会中断.
5.3 解释,运行中间代码的虚拟机
还有第三种实现方式 --> 先将代码编译为中间代码,然后通过虚拟机来进行解释执行.java中的jvm和C#的CLR.
5.4 CPU同时运行多个线程
线程是当前程序执行的基础单位,采用了分时操作系统的时钟中断,然后不同程序依次得到cpu进行执行.
5.5 适用静态区,堆区和栈区间进行管理
程序运行的区间:
1.静态区: 存放了代码和静态变量,一个进程有一个静态区
2.堆:在程序运行的过程中,分配的对象占有的空间.一个进程一个.
3.栈:每个线程分配一个栈,用来存放子程序调用的信息,以栈帧的方式存放,其中存放了函数的名称,本地变量,参数和返回地址.
5.6 OOP的特征在于内存的用法
oop和普通程序的内存适用方式有一些差别.具体会在后面以java为例进行描述.
5.7 每个类只加载一个类的信息
每个类只会加载一个类的信息,这些信息被加载到内存中的方法区中.其中类的加载有不同的方式:有的事在启动的时候,就将所有的类都加载进来,C++就是这样,还有一种是在运行的时候当运行到了某个业务的时候,就开始进行加载.全部加载的优点是在执行的时候会比较的快,不需要停下来加载其他的对象.缺点是会消耗额外的空间.使用到的时候再加载:优点是节省空间,缺点是在运行的过程中需要进行加载.
5.8 每次创建实例都会使用堆区
创建一个类的实例,类中包含的本地变量的地址存放在堆中,其中每个实例对应的存放在方法区的类信息存放在类头部.在执行的过程中,更具实例找到对应的方法的指令信息.
5.9 在变量中存储示例的指针
在java中只有指针,没有指针的指针.在java中名称是引用.
5.10 复制存储实例的变量时需要多加注意
在java中将一个变量赋值给另外一个变量,这个变量依然是相同的变量,都是指向了堆中一块空间.
5.11 多态让不同的类看起来一样
多态是实现了统一调用实例函数的机制.就是我们被调用的实例变了,也不影响调用.实现的机制是适用过了一个虚函数方法表(函数指针)所有的多态的类都有相同的虚函数方法表,让所有的类的方式都一样.这样就实现了调用方式的统一,让所有的类看起来都一样..
5.12 根据基础的信息不同,内存配置也不同
其中父类的方法表中信息也会被复制到子类中来.这个时候子类中的方发表中的对象是合适的.
对于变量,父类的变量也会被复制到本地来,但是本地优先.
在调用的时候,方法的调用会指向具体的子类.但是实例的调用会指向父类.
5.13 孤立的实例由来及回收处理
孤立的实例会被回收.这部分的对象标记出来的方式有两种,一种是线性地址再探测.一种是引用计数法..
总结
不同语言,其中存储关注的点会有些不同.对于汇编应该关注寄存器.c语言关注指针.面向对象关注于内存.
当前语言执行的方式有两种:编译型:直接将代码编译为机器执行代码.第二种是解释型代码:变解释变执行.
还有第三种:将代码编译为中间代码,然后使用虚拟机进行加载执行.这种语言有java+jvm.C#+CLR.
类中记录了对象的元数据,每一个对象只有一个类,加载到系统的方法区中,同时也会加载一些静态变量.
对象会被加载到堆上,堆中存放了当前对象的成员变量的值,方法是存放在类的方法区中,在对象的头部有一个引用指向了方法区中的class对象.
在java中所有的对象的引用都类似于指针.
面向对象的多态是:通过虚拟方法表来实现的.每一个类都会将继承的父类的虚拟方发表copy到本地,同时也会用父类的虚拟方发表覆盖(如果有).在方法执行的时候,会调用虚拟方法表指向的方法.但是成员变量并不会有这种动态的机制.
类库,框架,组件,设计模式
面向对象技术的出现是为了提高程序的可维护性和可重用性.这里的重用性有两种,一种是程序开发出来的类库,框架,组件.第二种是提炼出来的可重用的软件的思想.而实现可重用的基础是面向对象的三大特征:封装,继承,多态.
优秀的结构能够重用
对于遇到的很多问题,我们解决了这些问题后,需要进行归类和总结.总结的结果就是模式.然后在使用总结出来的模式再次用来解决新的问题.
类库
将多个基础的类打包成为一个组件,这个组件就是类库.类库分为语言自带的基础类库,完成某个目标的第三方类库.
我们需要关注的是使用类库:使用类库中的类创建对象;继承类库中的类;适用多态替换类库中的类.
面向对象中所有的类都有一个基础的对象Object.例如java中的Object,C#和SmallTalk中也是以Object为基类的.Object-C以NSObject为基类.
框架
框架是对于完成某个目标的通用主题逻辑的实现,是一个不完整的系统.其中会通过一些调用用户逻辑的方式,让用户将自己实现的功能添加到框架中,来完成业务的功能.
框架:类似于Spring,Mybatis,SpringMVC等.
设计模式
在设计软件的过程中,会解决很多问题,也会有很多的窍门.将这些解决问题过程的窍门和其中的注意点整理出来,并命名.一来便于理解,而来便于复用.
GOF设计模式(可复用的面向对象程序的要素):
编号 | 分类 | 命名 | 目的 |
---|---|---|---|
1 | 适应设计模式 | Iterator | 逐个遍历 |
2 | 适应设计模式 | Adapter | 加个“适配器”以进行重用 |
3 | 交给子类 | Template Method | 将具体处理交给子类 |
4 | 交给子类 | Factory Method | 将实例的生成交给子类 |
5 | 生成实例 | Singleton | 只有一个实例 |
6 | 生成实例 | Prototype | 通过复制生成实例 |
7 | 生成实例 | Builder | 组装复杂的实例 |
8 | 生成实例 | Builder | 组装复杂的实例 |
9 | 分开考虑 | Bridge | 将功能层次与实现层次分离 |
10 | 分开考虑 | Strategy | 整体替换算法 |
11 | 一致性 | Decorator | 容器与内容的一致性 |
12 | 一致性 | Strategy | 装饰边框与被装饰物的一致性 |
13 | 访问数据结构 | Visitor | 访问数据结构并处理数据 |
14 | 访问数据结构 | Chain of Responsibility | 责任循环 |
15 | 简单化 | Facade | 简单窗口 |
16 | 简单化 | Mediator | 只有一个仲裁者 |
17 | 管理状态 | Observer | 发送状态变化的通知 |
18 | 管理状态 | Memento | 保存状态 |
19 | 管理状态 | State | 用类表示状态 |
20 | 避免浪费 | Flyweight | 共享对象,避免浪费 |
21 | 避免浪费 | Proxy | 保存状态 |
22 | 用类来表现 | Command | 命令也是类 |
23 | 用类来表现 | Interpreter | 语法规则也是类 |
对于设计模式这种将遇到问题,常见,解决方案和命名这种方式可以使用到生活中的各个方面.
总结
在复用中,有两种方式,一种是我们开发出来的软件.以组件,框架,类库的方式提供给其他的系统来使用.第二种是设计模式,将遇到的常见问题的解决方案和窍门进行总结和抽象得到模式.然后将这些模式运用到构建系统系统的过程中,然后进一步进行总结和抽象.
7.抽象为通用的归纳整理法的面向对象法
面向对象的思考是对事物进行分类整理的结构和对职责进行归纳的结构.
面向对象试图模拟整个现实世界,面向对象只能完成现实世界的一部分.
总结
面向对象的有两种思想:是将事物分类整理的结构和对职责进行归纳的结构.
面向对象语言中并不能描述所有的现实世界.只是整个世界完成的功能的一部分.
8.统一建模语言
统一建模语言将不可见的软件功能和内部结构图形化和可见化.UML是查看无形软件的工具.
8.1uml是表示软件功能和内部结构的图形的绘制方法
uml除了能表示静态特征的类,继承的面向对象的图形.同时也有表示动态特征的流程图,状态转换图,时序图等.
8.2 uml有十三中图
整体包括如下:
需要 | 阶段 | 名称 | 功能 | 描述 |
1 | 需求 | 用例图 | 包括角色+功能 | |
2 | 需求 | 流程图 | 业务执行的流程 | |
3 | 设计 | 组件图 | 表示进程数据库等实现结构和关系 | |
4 | 设计 | 部署结构图 | 用例图 | 系统中的进程和数据库之间的关系 |
//todo 对于这些图的适用还是不不熟悉呀?其实从来都没有熟悉过.
8.3 UML的使用方法大致分为三种
1.使用类图,时序图和通信图表示程序的结构和动作
使用类图来表示当前程序的结构.
使用时序图表示各个类之间动作发生的时间和交互顺序
使用通信图表示各个类之间动作发生的先后顺序.
2.使用类图,时序图和通信图表示归纳整理的结果.
使用类图,时序图,通信图表示系统分析完成后的成果.确定系统的关键状态和角色,同时通过将角色之间是如何通信的.
3.使用其他的图表示非面向对象的成果
使用时序图(泳道图更好)表示整个业务的执行流程
使用用例图表示在整个业务流程中需要交给计算机做的工作
使用状态图表示核心对象的状态流转.
总结
uml语言在使用图形化的方式来规划或表示程序的功能和结构.程序相对不可见.但是图形化更容易被人们给记住.
通过uml图我们第一方面来表示程序的结构和功能.另一方面我们可以展示需求分析的各个模块的功能和交互.同时uml也帮助我们梳理业务流程,确定需要交给计算机系统完成的工作,以及核心对象的状态流转.
uml中主要的使用的图:
1.用例图:表示需要交给计算机系统完成的工作
2.时序图:确定各个模块(类)以后,各个模块之间交互的时间顺序
3.通信图:确定各个实例之间相互调用的先后顺序.
4.类图:记录了程序的结构(成员变量+方法)和类之间的关系(继承+引用).
5.流程图:确定当前系统业务逻辑的实现.不用关注这些业务流程是如何划分的.泳道图其实更好一些.
第9章 建模:填补现实世界和软件之间的沟壑
由于现实世界和计算机世界存在差异,所以我们在做系统之间需要三步:1.业务分析,2.需求定义,3.系统设计.
9.1 现实世界和计算机和软甲之间存在沟壑
1.面向对象并不反应现实世界的全部的内容,我们只提取其中需要的部分.2.业务中只有部分内容由计算机来实现,其他的部分是由于人来实现的.
管理计算机的软件需要以某种方式反应现实世界的部分情形.
管理计算机的软件需要以某种方式反应现实世界的部分情形. 其他的部分就是沟壑
9.2计算机擅长固定工作和记忆工作
计算机非常擅长规则固定的计算和记忆的工作,相对的人并不擅长.这样可以将这些固定的和记忆的工作交给计算机来处理.
9.3 通过 业务分析,去修定义,系统设计来填补这种沟壑
在解决现实世界和软件系统之间的鸿沟有三个步骤:
1.业务分析:分析出当前业务的实现的逻辑和步骤.一起这个业务的合理性.同时需要分析的这些业务中哪些步骤可以交给计算机来帮助实现.(why)
2.需求定义:在业务的部分中,交给计算机软件实现的部分是哪些.需要做什么.(what) 需求规格说明书.
3.软件设计:从需求规格说明书开始,设计这个系统是如何做的.
9.4 应用程序的不同类别
不同的应用程序,系统建模的方式不同.软件分为:
1.业务系统:描述现实世界的运行.
2.嵌入式系统:
3.单机系统:例如运行在单机中的软件.
4.基础软件:操作系统,数据库等.
不同的软件:建模会有一些差异.
业务系统
业务系统主要描述了世界中正在运行的系统,将其中部分的功能交给计算机来实现.其中的步骤有三部分:
1.使用泳道图来描述现实世界中的业务运行的情况.
2.从用到图中找出能够交给计算机程序执行的部分.这部分抽象为用例图.从而进入需求定义阶段.
3.找出当前业务系统中可以映射到计算机中的数据.抽象出概念模型.
计算机只是将业务中的用到的各种数据记录在计算机中,同时提供查询的功能.但是真正的的判断,交涉,决策以及业务的完成依然是在真实世界中完成的.
嵌入式软件设计
嵌入式软件和普通的业务软件会有一些差异.嵌入式软件执行的是单调的逻辑完整的事情.
我们可能不太能从业务出发.
我们从电梯的状态出发来理解这个系统.同时从不同位置的角色来响应对应的指令.
建模蕴含这软件开发的乐趣
建模是人们整理思想的技术.这个过程也挺有意思的.
总结
软件建模是用来解决现实世界的业务和计算机系统之间的的差异步骤,是对思想的整理的技术.
软件建模的过程分为三个步骤:1.业务分析 2. 需求定义,3.系统分析
其中业务分析是现实世界中的软件是如何推进的步骤进行分析.适用的工具是泳道图.
2.需求定义:计算机适合完成规则确定的计算和记忆的功能.从业务流程中找出适合交给计算机执行的工作.将这些工作抽象为用例.这个过程出现用例图,和模型图(类图).
3.系统分析:下面对这些用例进行分析.
4.建模是一种整理思想的游戏.
面向对象的设计
- 进入设计阶段分为三个步骤:
1.确定系统的运行环境,使用的技术组件.包括操作系统,数据库,通信组件等
2.定义系统整体结构,将系统分割为不同的子系统
3.设计各个子系统的软件结构.确定类,方法的规格和接口.
- 如何分割子系统.
对于一个系统,我们需要对这个系统流程进行梳理,适用泳道图来捋清楚.然后按照其中步骤进行切分为各个子系统. - 系统设计的目标:
早期的时候,系统功能较简单且运行的硬件资源匮乏.设计的更加重视软件的高效.随着系统规模变大,逻辑变复杂,同时硬件资源的迅速丰富.这个时候软件的可重用性和可维护性成为第一重要的. - 进一步细分,又分为三个目标
1.消除重复
2.提高独立性
3.消除循环依赖 - 消除重复
1.打代码的时候,将重复的代码抽象出一个类或者方法,避免粘贴复制
2.适用面向对象的继承,来复用方法. - 提高类的独立性
类的独立性主要是高内聚,低耦合.内聚性是指一个类,一个组件中提供的职责是内聚的.耦合性:是指当前模块内部不影响到其他的模块.
如何保证这种独立性呢:
外在技巧:1.起一个一句话说明的名称
2.类需要足够的小.
内部思考:
1.计算机提供了记录和查询的状态以及计算这些状态的功能.
2.每一个类到每一个包,每一个子系统,他们都维护了一系列状态.这些状态就是当前模块的资源,当我们需要分配职责的时候就需要判断一下,完成当前职责的锁需要的资源有哪些,然后哪个模块中有这些资源,就由哪些模块负责这个模块. - 消除循环依赖
- 将循环依赖的几个模块合并为一.
- 将循环依赖的一笑部分抽出来,合并为单独的一个模块,然后让原来的吗模块依赖这个新模块
面向对象的原则:
1.单一职责:一个类只完成一个逻辑完整的功能.
2.接口隔离:将内聚的一系列功能抽象为一个接口,这样可以在不同的场合下
3.依赖倒置:父类依赖于子类.
4.里斯替换:子类可以在任何场合下替换父类
5.开放闭合原则:对扩展开发,对修改闭合. 如果想添加一个功能,只需需要添加类,而不需要修改现有代码.
6.组合继承原则:尽可能使用组合,少适用继承.
7.迪米特法则:最少知道