概述
参考书籍:《面向对象编程导论》
面向对象思想
类:具有相同属性和相同操作的对象的集合。
对象:独立存在的客观事物,由一组属性和一组操作构成。
方法:描述了对象执行的功能。
消息:对象之间通过消息进行通讯,以实现对象之间的动态联系。
继承:子类继承父类的特征和行为,使得子类具有父类相同的特征和行为。
多态:一般类中定义的属性和服务,在特殊类中不改变其名字,但通过各自不同的实现后,可以具有不同的数据类型或具有不同的行为。
改写:子类定义一个与父类有着相同名称且类型签名相同的方法。
抽象
抽象是指对于一个过程或者一件制品的某些细节有目的的隐藏,以便把其他方面、细节或者结构表达得更加清楚。(信息隐藏)
抽象是控制复杂性时最重要的工具。
抽象形式:分治法、特殊分层、不同视角、分类、组合、模式。
抽象机制发展:汇编语言;过程/函数;模块;抽象数据类型;以服务为中心;消息、继承和多态。
模块提供了将名称空间划分成两个部分的能力。
抽象数据类型思想的重要进展是把接口的概念和实现的概念分离开来。
类和方法
对象外部看,客户只能看到对象的行为;对象内部看,方法通过修改对象的状态,以及和其他对象的相互作用,提供了适当的行为。
静态成员:实现同类多个对象间的数据共享。
静态成员函数:不能访问非静态成员。
消息、实例和初始化
消息:对象间相互请求或相互协作的途径。
消息传递语法:消息->接收器,响应行为随接收器不同而不同。
静态类型语言:
- 类型和变量联系在一起;
- 编译时作出内存分配决定,不必运行时刻重新分配;
- 控制类型错误。
动态类型语言:变量看作名称标识,类型和数值联系在一起。
动态类型语言与静态类型语言之间的差异在于变量或数值是否具备类型这种特性。
指针引用堆分配内存。
堆分配的内存必须通过某种方式回收。
内存回收:在程序中显式制定不再使用的对象,将其使用内存回收;垃圾回收机制。
垃圾回收机制:
- 确保动态分配的内存对象都有一个指定的属主;
- 引用计数:引用共享对象的指针的计数值。
内存分配策略:
- 静态存储分配:在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间;
- 栈式存储分配:程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存;
- 堆式存储分配:专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例。
反射(reflection)和内省(introspection)是指程序在运行过程中“了解”自身的能力。
反射支持一个组件动态的加载和查询自身信息,因此反射为许多基于组件的编程工具建立了基础。
继承和替换
继承总是向下传递的,因此一个类可以从它上面的多个超类中继承各种属性 。
继承的作用:代码复用;概念复用,共享方法的定义。
替换原则:指如果类B是类A的子类,那么在任何情况下都可以用类B来替换类A,而外界毫无察觉。
子类有时为了避免继承父类的行为,需要对其进行改写。
与类一样,接口可以继承于其他接口,甚至可以继承于多个父接口。
抽象方法:介于类和接口之间的概念;定义方法但不实现;创建实例前,子类必须实现父类的抽象方法。
继承的形式:
- 特化子类化:新类是基类的一种特定类型,它能满足基类的所有规范。
- 规范子类化:派生类并没有重新定义已有的类型,而是去实现一个未完成的抽象规范。
- 构造子类化:继承的目的只是用于代码复用时,新创建的子类通常都不是子类型。
- 泛化子类化:派生类修改和扩展基类的行为,形成一种更泛化的抽象。
- 扩展子类化:派生类只是往基类中添加新行为,并不修改从基类继承来的任何属性,即是扩展继承。
- 限制子类化:派生类的行为比基类更少或是更严格。
- 变体子类化:两个或多个类需要实现类似的功能,但它们的抽象概念之间似乎并不存在层次关系。
- 结合子类化:通过合并两个或者更多的抽象特性来形成新的抽象。
可替换性:变量声明时指定的类型不必与它所容纳的值类型相一致。
静态行为和动态行为
静态类是指用于声明变量的类,在编译时就确定下来,并且再也不会改变。
动态类指与变量所表示的当前数值相关的类,在程序的执行过程中,当对变量赋新值时可以改变。
响应消息时对哪个方法进行绑定是由接收器当前所包含的动态数值来决定的。
如果方法所执行的消息绑定是由最近赋值给变量的数值的类型来决定的,那么就称这个变量是多态的。
替换的本质
内存布局:
- 最小静态空间分配:只分配基类所需的存储空间;
- 最大静态空间分配:无论基类还是派生类,都分配可用于所有合法数值的最大存储空间;
- 动态内存分配:只分配用于保存一个指针所需的存储空间。
浅复制(shallow copy):共享实例变量。
深复制(deep copy):建立实例变量的新的副本。
多重继承
多重继承:一个对象可以有两个或更多不同的父类,并可以继承每个父类的数据和行为。
问题:名称歧义、对替换有影响
名称歧义解决方法:使用全限定名、使用重定义和重命名的结合
对替换的影响解决方法:引入辅助类,用不同的方法名重定义某些操作。
接口的多重继承:对于子类来说,接口不为其提供任何代码,所以不会产生两部分继承代码之间的冲突。
内部类可以解决多重继承。
多态及软件复用
多态的形式:
- 重载:专用多态,类型签名区分;
- 改写:包含多态,层次关系中,相同类型签名
- 多态变量:赋值多态,声明与包含不同
- 泛型:模板,创建通用工具
最常用的软件复用机制:继承和组合。
组合:提供了一种利用已存在的软件组件来创建新的应用程序的方法。
继承:通过继承,新的类可以声明为已存在类的子类,与初始类相关的所有数据字段和函数都自动地与新的数据抽象建立联系。
重载
重载是在编译时执行的,而改写是在运行时选择的。
类型签名:函数类型签名是关于函数参数类型、参数顺序和返回值类型的描述。
范畴:范畴定义了能够使名称有效使用的一段程序,或者能够使名称有效使用的方式。
通过继承创建的新类将同时创建新的名称范畴,该范畴是对父类的名称范畴的扩展。
通过类型签名和范畴可以对重载进行两种分类:基于具有不同范畴的方法;基于具有不同类型签名的方法。
相同的名称可以在不引起歧义且不造成精度损失的情况下出现于多个不同的范畴。
基于类型签名的重载:关于重载的解析,是在编译时基于参数值的静态类型完成的,不涉及运行时机制。
强制是一种隐式的类型转换,它发生在无需显式引用的程序中。
转换表示程序员所进行的显式类型转换。在许多语言里这种转换操作称为“造型”。
重定义:当子类定义了一个与父类具有相同名称但类型签名不同的方法时,发生重定义。
改写
如果子类的方法具有与父类的方法相同的名称和类型签名,称子类的方法改写了父类的方法。
两种不同的关于改写的解释方式:代替、改进。
延迟方法:如果方法在父类中定义,但并没有对其进行实现,那么我们称这个方法为延迟方法。(抽象方法/纯虚方法)。
改写区别于遮蔽的最重要的特征就是,遮蔽是在编译时基于静态类型解析的,并且不需要运行时机制。
改写、遮蔽和重定义的区别:
- 改写:父类与子类的类型签名相同,并且在父类中将方法声明为虚拟的;
- 遮蔽:父类与子类的类型签名相同,但是在父类中并不将方法声明为虚拟的;
- 重定义:父类与子类的类型签名不同。
很少有改变类型签名的需求,通常将类型在其继承层次上提升或降低。
协方差:一个类型降低类型层次作为子类。
反协方差:一个类型由子类化反向提升类型层次。
多态变量
多态变量是指可以引用多种对象类型的变量,在程序执行过程可以包含不同类型的数值。
多态变量形式:简单变量、接收器变量、反多态、纯多态(多态方法)。
多态变量最常用的场合是作为一个数值,用来表示正在执行的方法内部的接收器。
该取消多态赋值的过程,也称为反多态。
纯多态支持可变参数的函数,支持代码只编写一次、高级别的抽象以及针对各种情况所需的代码裁剪。
纯多态通常是通过给方法的接收器发送延迟消息来实现这种代码裁剪的。
泛型
泛型通过类型的使用提供了一种将类或者函数参数化的方法。
与通常的函数参数一样,泛型提供了一种无需识别特定数值的定义算法的方法。
泛型将名称定义为类型参数,在编译器读取类描述时,无法知道类型的属性,但是该类型参数可以像类型一样用于类定义内部。
在将来的某一时刻,会通过具体的类型来匹配这一类型参数,这样就形成了类的完整声明。
模板类的使用:
- 为了创建模板实例,模板参数必须与具体类型联系起来;
- 参数必须与接收器的类型相匹配
模板类一般用于开发容器类。
框架
对于一类相似问题的骨架解决方案。
通过类的集合形成,类之间紧密结合,共同实现对问题的可复用解决方案。
继承和改写的强大能力体现。
继承允许进行高级别算法细节的封装还允许在不改变原始代码的情况下修改或特化这些细节。
框架改变了应用程序与库代码之间的关系。
在框架中控制流是由框架决定的,并且随应用程序的不同而不同。
新的应用程序的创建者只需改变供框架调用的例程即可,而无需改变总体结构。
框架占主导地位,而应用程序特定的代码处于次要位置。
对象互连
一种考虑对象互连的方式就是研究可视性和依赖性这两个概念。
可视性描述了关于名称的特性,通过该名称句柄可以存取对象,如果对象的名称是合法的且代表该对象,那么在这个特定环境下该对象就是可见的。
依赖性将两个对象或者类联系起来,在不存在另外一个对象的条件下,如果一个对象的存在无任何意义,就说该对象依赖于另外那个对象。
耦合(coupling)和内聚(cohesion)的思想提供了一个框架,用于评价对象和类的应用是否有效。
耦合描述类之间的关系,内聚描述类内部的关系。
从最差的耦合到较好的耦合:
- 内部数据耦合:发生在当一个类的实例直接修改另外一个类中的本地数据值(实例变量)时;
- 全局数据耦合:发生在两个或者更多个类型都依赖于公用的全局数据结构而绑定到一起的时候;
- 控制(或顺序)耦合:一个类必须以一种由任何位置控制的特定的顺序来执行操作;
- 组件耦合:组件耦合发生在一个类包含的数据字段或数值为另外一个类的实例时;
- 参数耦合:发生在一个类必须调用另外一个类的服务和例程时,此时两个类之间所发生的唯一关系就是一个类需要为另一个类提供参数数目、类型和返回值类型;
- 子类耦合:面向对象编程所特有的,描述了一个类与其父类之间的关系,通过继承,子类的实例可以被看成父类的实例。
类的内部内聚性是该结构中各个元素之间绑定程度的量度。
从最弱的内聚到最强的内聚:
- 随机内聚:对程序随意划分;
- 逻辑内聚:算术函数库;
- 时间内聚:如实现程序初始化的类;
- 通信内聚:数据或者设备的manager;
- 顺序内聚:避免顺序耦合;
- 功能内聚:类中元素通过执行特定功能关联起来;
- 数据内聚:数据结构。
设计原则
设计目标:
- 扩展性(Extensibility):新功能易加入系统;
- 灵活性(Flexibility):允许代码修改平稳发生,不会涉及很多其他模块;
- 可插入性(Pluggability):容易将一个类换为另一个具有同样接口的类。
软件复用重要性:
- 较高的生产率;
- 较高的软件质量;
- 恰当使用复用,可改善系统的可维护性。
面向对象设计原则:
- 开闭原则OCP:Open-Closed Principle,软件组成实体应该是对扩展开放的,但是对修改是关闭的;
- 里氏替换原则LSP:Liskov Substitution Principle,使用指向基类(超类)的引用的函数,必须能够在不知道具体派生类(子类)对象类型的情况下使用它们;
- 依赖倒转原则DIP:Dependency Inversion Principle,抽象不应当依赖于细节,细节应当依赖于抽象;
- 接口隔离原则ISP:Interface Segregation Principle,使用多个专门的接口比使用单一的总接口好;
- 组合复用原则CRP:Compositoin Resuse Principle,优先使用(对象)组合,而非(类)继承;
- 迪米特法则LoD:Law of Demeter:最少知识原则,一个对象应该对其他对象尽可能少的了解;
- 单一职责原则(SRP):不要存在多于一个导致类变更的原因。
在设计一个软件的时候,应当使这个软件可以在不被修改的前提下扩展。
开-闭法则认为应该试图去设计出永远也不需要改变的模块。
开-闭法则优点:可复用性好、可维护性好。
里氏替换原则:凡是父类适用的地方子类也适用。
依赖倒转原则:不将变量声明为某个特定的具体类的实例对象,而让其遵从抽象类定义的接口,实现类仅实现接口,不添加方法。
使用接口的优点:
- 一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换;
- 对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性;
- 松散耦合;
- 增加了重用的可能性;
- 提高了(对象)组合的机率,因为被包含对象可以是任何实现了一个指定接口的类。
组合优点:
- 容器类仅能通过被包含对象的接口来对其进行访问;
- “黑盒”复用,因为被包含对象的内部细节对外是不可见;
- 封装性好;
- 实现上的相互依赖性比较小;
- 每一个类只专注于一项任务;
- 通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合;
组合缺点:
- 系统中的对象过多;
- 为了能将多个不同的对象作为组合块(composition block)来使用,必须仔细地对接口进行定义。
继承优点:
- 容易进行新的实现,因为其大多数可继承而来;
- 易于修改或扩展那些被复用的实现。
继承缺点:
- 破坏了封装性,因为这会将父类的实现细节暴露给子类;
- “白盒”复用,因为父类的内部细节对于子类而言通常是可见的;
- 当父类的实现更改时,子类也不得不会随之更改;
- 从父类继承来的实现将不能在运行期间进行改变。
如果两个类不必彼此通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可通过第三者转发这个调用。
迪米特法则:
- 控制信息过载,提高封装能力;
- 创建弱耦合类,利于复用;
- 创建弱耦合类,利于复用;
- 设计不变类。
接口隔离原则:一个类对另一个类的依赖性应建立在最小的接口上。
单一职责原则的核心就是解耦和增强内聚性。
设计模式
模式就是对以解决问题的记录,使处理后面与之类似的问题时更容易。
设计模式使人们更加简单方便地复用成功的设计和体系结构。
设计模式要素:模式名称、问题、解决方案、效果。
设计模式:
- 创建型:对象创建;
- 结构型:处理类或对象组合;
- 行为型:对类或对象怎样交互和怎样分配职责进行描述。
简单工厂模式:
- 封装可变性,类的创建模式;
- 简单工厂模式是有一个工厂类根据传入的参量决定创建出哪一种产品类的实例;
- 核心是工厂类。这个类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例;
- 实现了对责任的分割;
- 扩展比较困难。
工厂方法模式:
- 类的创建模式,又叫做虚拟构造子模式或者多态性工厂模式;
- 定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中;
- 简单工厂模式的进一步抽象和推广。由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点;
- 核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。
抽象工厂模式:
- 是所有形态的工厂模式中最为抽象和最具一般性的一种形态;
- 向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族中的产品对象;
单例模式:
- 对象的创建模式,确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例;
- 饿汉式单例类,懒汉式单例类。
原型模式的创建模式,通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象。
结构模式描述如何将类和对象组合成一个更大的结构 可以分为类模式和对象模式。
类模式采用继承机制来组合接口和实现。
对象模式描述了如何对一些对象进行组合,实现新功能的一些方法。
适配器模式将一个类的接口转换成客户希望的另外一个接口。
代理模式为其他对象提供一种代理以控制对这个对象的访问。
Composite模式将对象组和成树型结构以表示“部分-整体”的层次结构。
Decorator模式动态地给一个对象添加一些额外的职责。
Bridge模式把抽象部分和行为部分分离,使它们都能独立变化。
FLYWEIGHT(享元)模式运用共享技术有效地支持大量细粒度的对象。
FACADE(外观)模式为子系统中的一组接口提供一个一致的界面。
行为型模式涉及到算法和对象间职责的分配,它不仅描述对象或类的模式,还描述它们之间的通信模式。
策略模式定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。
观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新。
责任链模式:很多的对象由每一个对象对其下家的引用而联接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。
ITERATOR模式:提供一种方法,顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
Command命令模式:将一个请求封装为一个对象(即我们创建的Command对象),从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作。
最后
以上就是热情白昼为你收集整理的【笔记】面向对象技术面向对象思想抽象类和方法消息、实例和初始化继承和替换静态行为和动态行为替换的本质多重继承多态及软件复用重载改写多态变量泛型框架对象互连设计原则设计模式的全部内容,希望文章能够帮你解决【笔记】面向对象技术面向对象思想抽象类和方法消息、实例和初始化继承和替换静态行为和动态行为替换的本质多重继承多态及软件复用重载改写多态变量泛型框架对象互连设计原则设计模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复