面向对象的特征

32 篇文章 0 订阅

 

特征

传统看法

新思维

对象

一堆数据和方法

具有方法的数据

拥有责任的实体,这些责任定义了对象的行为。(我们应该关注对象的意图行为,而不是对象如何实现。)

对象的设计多关注应该做什么,而不是如何实现它。

对象设计的基本观点:关注动机而不是实现。(关注对象要做什么,能帮助我们免于过早地操心实现细节,从而将这些实现细节隐藏起来。)

封装

隐藏数据

任何形式(数据隐藏、类隐藏、实现隐藏)的隐藏。

封装可以用于在行为中包含变化。

考虑设计中哪些点是可变的,然后封装变化点。(即:发现变化并将其封装)

继承

特化和复用

对象分类的一种方式。

封装变化的方法。

 

对象

对象真正的威力并不在于继承,而是来自封装行为。

面向对象范型以对象概念为中心,一切都集中在对象上。使用对象的优点在于可以定义自己负责自己的事物,确定自己的责任。理解对象的最佳方式,是将其看成具有责任的东西。有一条好的设计规则:对象应该自己负责自己,而且应该清楚地定义责任。从概念、规约、实现三个不同视角来观察对象:

1)   在概念层次上,对象是一组责任。或者说对象是具有责任的一个实体,这些责任定义了对象的行为。

2)   在规约层次上,对象是一组可以被其他对象或对象自己调用的方法(也称为行为)。

3)   在实现层次上,对象是代码和数据,以及他们之间的计算交互。

概念视角对对象的定义有助于使我们关注对象的意图行为,而不是对象如何实现。关注对象要做什么,还能帮助我们免于过早的操作实现细节,从而将这些实现细节隐藏起来。

关于动机而非实现,是设计模式中反复出现的主题。将实现隐藏在接口之后,实际上是将对象的实现与使用它们的对象解耦。

抽象

抽象是指强调实体的本质、内在的属性。在系统开发中,抽象指的是在决定如何实现对象之前的对象的意义和行为。使用抽象可以尽可能避免过早考虑一些细节。

类实现了对象的数据(即状态)和行为的抽象。

隐藏问题/实体的详细/具体部分,只提取最重要的部分。对Problem Space进行抽象,强调问题/实体的简单化。把现实世界的问题/实体抽象化,定义对象并面向这些对象模拟业务领域。依赖倒置原则(DIP):面向抽象编程,而不是面向具体编程。

JAVA的两种类:

抽象类:不可实例化,不能生成对象,但可以实现具体的方法。一定是用来继承的。

具体类:可以实例化,具体类,不是用来继承的。

抽象类与子类的关系,实际是模板方法的应用。在抽象类与具体类的树形结构中,树枝节点是抽象类,树叶节点是具体类。

代码重构建议(如果B是A的子类,建立C,抽象类或接口)

抽象类,应当拥有尽可能多的共同代码

在一个从抽象类到多个具体类的继承关系中,共同的代码应当尽量向上移动到位于金字塔顶端抽象类中。

这样,可以提供代码的复用率,由于代码在公共的超类,而不是子类中出现,在代码发生改变时,程序员只需修改一个地方,程序可维护性更好。

抽象类,应当包含尽可能少的数据

数据,会占用内存,而且复用率不高,所以尽量将数据向下移动,即尽量不要置于超类中,而是要置于具体类中。

针对抽象编程的核心思想

针对抽象的编码,主要针对超类,而不是具体类的编程。

重点,解决代码复用问题。

封装

封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义:

1)   把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位(即对象)。

2)   信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。

封装的原则在软件上的反映是:要求使对象以外的部分不能随意存取对象的内部数据(属性),从而有效的避免了外部错误对它的"交叉感染",使软件错误能够局部化,大大减少查错和排错的难度。

在程序设计中,封装是指将一个数据和与这个数据有关的操作集合在一起,形成一个能动的实体——对象,用户不必知道对象行为的实现细节,只需根据对象提供的外部接口访问对象即可。因此,从用户的观点来看,这些对象的行为就像包含在一个“黑匣子”里,是隐蔽的、看不见的。

封装有两个基本前提:一是对象必须是完备的,即必须能够表示整个概念,描述整个问题的各个方面;二是私有性。大多数对象都需要对其内部的数据和过程限制处理权限。私有性不但可以保证对对象的正确操作,而且有利于查错,使一些对象的成员函数私有化,减少它们被处理的机会,于是在追踪时许多地方都可以不必去查。

封装不是面向对象语言所独有的特性,但这种在单一实体中把数据结构和行为捆绑在一起的能力,使封装比传统的把数据结构和行为分离的语言更加清晰、更强有力。

对象是数据和操作的集合,良好的封装保证只有通过对象的操作才能修改对象的状态/数据。封装保护了对象的数据,防止对对象状态的误操作。封装不仅仅是简单的数据封装,而是将复杂的、可变的业务功能的具体实现通过模块/接口的形式来隐藏。把系统表现为有自身固有属性的对象和对象之间的相互关系。

另外还可以封装变化、封装对象的请求(Command模式)、封装对象的状态(State模式)等等。

封装应该被视为“任何形式的隐藏”。换句话说,可以是隐藏数据,但还可以是隐藏实现细节、派生类、设计细节、实例化规则等。封装做的是隐藏,而抽象做的是简化。

封装的目标

把对象的属性和服务结合成一个独立的系统单位。

尽可能隐蔽对象的内部细节,只向外部提供接口,降低对象间的耦合度。

对可变性的封装原则(Encapsulation of Variation Principle.  EVP)。

信息隐蔽

1)   可见性控制;

数据不能被对象的使用者直接访问,只允许通过由对象提供的方法或代码访问数据。

2)   对象将愿意提供给所有对象的公共服务公开化;

3)   对象也提供仅限于特定对象的其它服务(受保护的和私有的);

封装的重要意义

使对象能够集中而完整地描述并对应一个具体事物;

体现了事物的相对独立性,使对象外部不能随意存取对象的内部数据;

对象内部的修改对外部的影响很小,减少了修改引起的波动效应;

公开静态的、不变的服务,而把动态的、易变的服务隐藏起来;

封装变化

应对变化,使用功能分解,用模块化封装变化。我们虽然无法预测会发生什么变化,但是通常可以预期哪里会发生变化。面向对象的巨大优点之一,就是可以封装这些变化区域,从而更容易地将代码与变化产生的影响隔离开来。

继承

继承是代码复用的一种机制,使得很容易地定义具有相近功能的对象族。子类通过继承父类,以复用父类定义的数据和操作。当“新的类”属于“老的类”/“原有的类”的其中的一种时(a kind of),适合使用继承。

继承表明类之间的关系。类不仅仅说明一组对象上的约束,还说明与其他类之间的关系。两个类型可以有共同的特性和行为,但是,一个类型可能包括比另一个类型更多的特性,也可以处理更多的消息。继承表示了基类和派生类之间的相似性一个基类具有所有由它派生出来的类所共有的特性和行为,而派生类则通过继承重用了基类的特性和行为

抽象基类提供了公共接口,当希望通过公共接口操作一组(派生)类时就创建抽象基类。

继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。

继承性是面向对象程序设计语言不同于其它语言的最重要的特点,是其他语言所没有的。

在类层次中,子类只继承一个父类的数据结构和方法,则称为单重继承。

在类层次中,子类继承了多个父类的数据结构和方法,则称为多重继承。

在软件开发中,类的继承性使所建立的软件具有开放性、可扩充性,这是信息组织与分类的行之有效的方法,它简化了对象、类的创建工作量,增加了代码的可重性。

采用继承性,提供了类的规范的等级结构。通过类的继承关系,使公共的特性能够共享,提高了软件的重用性。

正确使用继承

里氏代换原则(LSP):一个软件实体如果使用的是一个基类的话,那么一定适用于其子类,而且它根本不能察觉出基类对象和子类对象的区别。只有衍生类替换基类的同时软件实体的功能没有发生变化,基类才能真正被复用。

继承分为两种:

类对接口的实现,接口继承;

类对类的继承,实现继承。实现继承,容易被滥用。

对于抽象类,尽量使用合成,而不是继承,来达到复用目的。

参考“组合/聚合复用原则”,举例

继承复用的使用场合

继承代表“一般化、特殊化”关系,抽象的基类代表一般,而衍生的具体类代表特殊。

Peter Coad条件,符合该条件所有内容,才可以使用抽象类:

1、子类是超类的一个特殊种类,而不是超类的一个角色。

Has-A关系应当使用聚合关系;Is-A关系符合继承关系。

Has-A :一个类是另一个类的角色;人---黑社会小弟,老大角色,可以互换:人,可以做黑社会小弟,某时,也可以做老大。

Is-A:一个类是另一个类的一种。鸟---麻雀、天鹅种类,不可以互换:鸟,不可以某时是麻雀,某时是天鹅。

2、永远不会出现需要将子类换成另一个类的子类的情况。

3、子类具有扩展超类的责任,而不具有置换掉、注销掉超类的责任。如果子类需要大量的置换掉超类的行为,则不适合。

4、只有在分类学角度上,才可以使用继承,不要从工具类继承

多态

动态绑定允许你在运行时刻彼此替换具有相同接口的对象,这种可替换性就称为多态。多态使得对象间彼此独立,并可以在运行时刻动态改变它们相互的关系。

依赖倒置原则(DIP):面向抽象编程,而不是面向具体编程。

多态的含义是相同的行为(在基类中定义)在不同的类中有着不同的实现(在子类中实现),或者更彻底的说,多态性就是以相同的指令唤起不同的函数。

当处理类型层次结构时,程序员常常希望不把对象看作是某一特殊类型的成员,而把它看作基本类型的成员,这样就可以编写不依耐于特殊类型的代码,在添加新的子类后也不影响原来的代码,这是扩展面向对象程序以处理新情况的最普通的方法——通过派生新的子类来扩展程序功能,这个能力极大地减少了软件维护的花费(。

多态性使指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。

在java语言中,多态性体现在两个方面:由方法重载实现的静态多态性(编译时多态)和方法重写实现的动态多态性(运行时多态)。

1) 编译时多态

在编译阶段,具体调用哪个被重载的方法,编译器会根据参数的不同来静态确定调用相应的方法。

2) 运行时多态

由于子类继承了父类所有的属性(私有的除外),所以子类对象可以作为父类对象使用。程序中凡是使用父类对象的地方,都可以用子类对象来代替。一个对象可以通过引用子类的实例来调用子类的方法。

重写方法的调用原则:java运行时系统根据调用该方法的实例,来决定调用哪个方法。对子类的一个实例,如果子类重写了父类的方法,则运行时系统调用子类的方法;如果子类继承了父类的方法(未重写),则运行时系统调用父类的方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值