面向对象设计原则概述
请简述面向对象设计的启发规则
请简述面向对象设计的启发规则在设计中,我们往往会根据自己已有的经验进行面向对象程序的编写。
但是实际上我们不知道为什么要这样做?我们可以怎样更好地改进面向对象设计呢?这个问题值得我们深入思考,也是我们本节课要解决的内容。
在开始之前,先给大家简单介绍一下面向对象程序设计的基本概念和几条设计规则。
1、主体是能被用户直接使用的工具,它对系统资源有使用权。
2、主体应该提供完成特定功能的信息,而不是说明完成某些特定功能所需要的过程和逻辑。
3、主体应该是对现实世界的抽象,而不是对人类经验的简单总结。
4、从属关系应该明确,如果两个主体间没有从属关系,那么从属关系就失去了意义。
5、复杂性应该控制在一定范围内。
6、如果存在可由子类扩充的方法,它也应该可以被子类继承。
7、通常情况下,应该为不同类型的方法分别提供相应的接口,方便它们互相调用。
8、面向对象的程序设计方法要求软件系统必须具备三个基本特性:封装性、继承性和多态性。
下面我们来看一个简单的例子,加深一下对这些概念的理解。
请读者帮助我们完善这个示例。
从上面的示例可以看出面向对象的程序设计方法适用于开发大规模软件系统,但是对于我们日常开发小规模程序来说还不够合适,所以我们在这里再来简化一下,并且通过进一步思考和分析,让学生自己来归纳和总结这些设计原则。
请同学们观察上面的例子,哪些符合了面向对象程序设计的启发规则?哪些又不符合?为什么?启发规则在各种各样的软件开发中都有存在,请大家想一想你在学习软件工程的时候,是否也曾经犯过上面的错误呢?在以后的软件工程教学过程中,我们可以利用各种各样的事物作为载体,把这些启发规则作为设计的准则贯穿于整个软件开发的过程当中,让学生能够轻松地掌握这些原则,真正做到举一反三,触类旁通。
3、对象只需要在主体中出现一次,而不需要其它任何操作。
4、主体可以被组合和重用。
5、如果程序中需要用到类或抽象类的话,那么应该尽量不重复使用。
6、如果子类可以扩展父类的功能,那么应该尽量少的使用重复的功能。
面向对象程序设计
1.1 面向对象程序设计概述
•在程序的实施过程中,由于大型系统必须由 多人合作完成,各人或各小组完成程序中的 一个或几个模块,如何划分任务、估计和分 配资源、掌握每个程序员的进度等就构成了 进行大型系统设计时管理的复杂性,另外, 即使每个人或小组所设计的模块都无可挑剔, 但将这些模块整合在一起时未必不会出现问 题,因此如何权衡模块内聚度和模块间的耦 合度,也是编程人员必须面对的问题之一。
1.1 面向对象程序设计概述
简单的说,通过使用面向对象的方法可以把 一组看似相互之间毫无联系的对象有效地集成在 一起,这些对象都包含代表各自特征的数据以及 对数据的操作即对象的行为。通过面向对象方法, 可以把与该对象相关的数据和操作封装在一个相 对独立的实体中,该实体有自己的功能和特征, 它也可以采用某种机制与外界交互。
1.1 面向对象程序设计概述
面向对象设计方法追求的是现实问题空 间与软件系统解空间的近似和直接模拟。它 希望用户用最小的力气,最大限度地利用软 件系统进行求解。
在具体学习OOP之前,请先注意一下两 点:
记忆的特征或数据,这些特征或数据能反映对象的基本性质。 找出对象和类中存在的各种整体-部分结构和一般-特殊结构,并进一步确定这些结构组合而成的多重结构。
的处理这些系统状态以及中间状态、组织高 这种环境最主要的特征就是以对象作为程序的主要单位,它将数据及对这些数据的操作功能以及与其他对象的通信的能力都封装在了
软件工程第十一章面向对象设计
THANKS
感谢观看
01
抽象类是一种不能被实例化的 类,它只能被其他类继承。
02
抽象类可以包含抽象方法和具 体方法。抽象方法是没有具体 实现的方法,需要在继承抽象 类的子类中实现。
03
通过继承抽象类,子类可以继 承抽象类的属性和方法,并且 可以重写或实现抽象类中的方 法。
接口与抽象类的选择
在设计软件时,选择使用接口还是抽象类取决于具体需求和设计目标。
关系
关系描述了对象之间的交互和联系。 常见的关系包括关联、聚合和继承。
继承与多态的设计
继承
继承是一种实现代码重用的方式,子类可以继承父类的属性和方法,并可以扩展或覆盖它们。通过继承,可以建 立类之间的层次结构,使得代码更加清晰和易于维护。
多态
多态是指一个接口可以有多种实现方式,或者一个对象可以有多种形态。多态可以提高代码的灵活性和可扩展性, 使得程序更加易于维护和修改。
02
类与对象的设计
类的定义与属性
类的定义
类是对象的抽象,它描述了一组具有相同属性和行为的对象。类定义了对象的结构、行为和关系。
属性
属性是类中用于描述对象状态的变量。每个对象都有其自己的属性值,这些属性值决定了对象的状态 。
对象的行为与关系
行为
行为是类中定义的方法,用于描述对 象可以执行的操作。方法定义了对象 的行为和功能。
高层模块不应该依赖于低层模块,它们都应 该依赖于抽象。
面向对象设计的优势
提高代码可重用性
通过类和继承实现代码重用,减少重 复代码。
提高代码可维护性
面向对象设计使得代码结构更加清晰, 易于理解和维护。
提高开发效率
通过快速原型开发,快速构建软件系 统。
《实用软件工程》第9章 面向对象设计
• 信息隐藏:对于类而言,其内部信息如属性的表示方法和操作的实现算法,对 外界是隐藏的。外界通过有限的接口来访问类的内部信息。
17
9.3.2 面向对象设计的原则
• 低耦合:在面向对象设计中,耦合主要指对象之间相互关联的紧密程度,低耦 合有利于降低一个模块改变对其他模块的影响。
• 高内聚:内聚与耦合密切相关,低耦合往往意味着高内聚,高内聚有助于提高 系统独立性。
但随着需求理解的加深,以及对系统认识程度的逐步 提高,设计人员还要对模型进行修正和完善。 • 设计任务管理子系统包括确定任务,分配任务,还包 括权衡一致性、成本、性能等因素以及未来可扩充性。 • 设计数据管理子系统,需要设计数据格式以及相应的 服务,设计数据格式的方法与所用的数据存储管理模 式密切相关,不同数据存储管理模式时,属性和服务 的设计方法是不同的。
9.2 面向对象设计与面向对象分析的关系
• 设计阶段的任务是及时把分析阶段得到的需求转变成符合各项要求的 系统实现方案。与传统的软件工程方法不同的是,面向对象的方法不强调 需求分析和软件设计的严格区分。实际上,面向对象的需求分析和面向对 象的设计活动是一个反复迭代的过程,从分析到设计的过渡,是一个逐渐 扩充、细化和完善分析阶段所得到的各种模型的过程。严格的意义上来讲, 从面向对象分析到面向对象设计不存在转换问题,而是同一种表示方法在 不同范围的运用。面向对象设计也不仅仅是对面向对象分析模型进行细化。
• (2)人机交互子系统包括有效的人机交互所需的显示和输入,这些类在很大程度上 依赖于所用的图形用户界面环境,例如Windows、Delphi、C++,而且可能包括“窗 口”、“菜单”、“滚动条”、“按钮”等针对项目的特殊类。
25
9.5.1 系统分解
软件工程 第三部分 软件设计与建模--面向对象设计
(2) 块状组织 把系统垂直地分解成若干个相对独立的、弱耦 合的子系统/块,每块提供一种类型的服务。
采用层次与块状的混合结构
3. 设计(分布式)系统的拓扑结构 由子系统组成完整的系统时,典型的拓扑结构 有管道形、树形、星形等。设计者应该采用与问题 结构相适应的、尽可能简单的拓扑结构,以减少子 系统之间的交互数量。
3. 设计简单的类 小而简单的类便于开发和管理(高内聚)。为使 类保持简单,应该注意以下几点。 (1) 避免包含过多的属性:完成的功能可能太多了。 (2) 有明确的定义:任务应该简单。 (3) 简化对象之间的合作关系。如果需要多个对象 协同配合才能做好一件事,则破坏了类的简明性和 清晰性。 (4) 不要提供太多服务。典型地,一个类提供的公 共服务不超过7个。
层次图、结 构图、流程 图、N-S图、 PAD图、判 定表、判定 树等
面向对象设计方法比结构化设计方法更具有优势, 9/71 使用范围更广。
内容摘要
• • • • • 面向对象设计方法概述 面向对象设计原则 面向对象设计的启发规则 系统设计 对象设计
10/237
面向对象设计原则
• 强调模块化、抽象化、信息隐蔽、高内聚低耦合、复用性等 设计准则。
4/71
什么是面向对象设计(OOD)?
(二)现今(90年代后)的OOD • 面向对象的设计(OOD)就是在OOA模型的基础上运 用面向对象方法进行系统设计,目标是产生一个符合具 体实现条件的OOD模型。
OOD的特点: • 1、以面向对象的分析为基础,一般不依赖结构化分析。 • 2、与相应的OOA方法共同构成一种OOA&D方法体系。 OOA和OOD采用一致的概念和原则,但属于软件生命 周期的不同阶段,有不同的目标及策略。 • 3、较全面地体现面向对象方法的概念与原则。 • 4、大多数方法独立于编程语言,通过OOA&D所得到 的系统模型可以由不同的编程语言实现。
面向对象设计的三大原则,理解并能举例
面向对象设计的三大原则,理解并能举例
面向对象编程设计有三大原则,分别是封装(Encapsulation)、继承(Inheritance)和多态(Polymorphism)。
1. 封装(Encapsulation):封装是将数据和相关行为(方法)
组合在一个类中,以实现隐藏内部实现细节的原则。
通过封装,可以将一组数据和对它们的操作封装在一个类中,对外部只暴露必要的接口,隐藏了实现的细节,提高了代码的安全性和可维护性。
例如,一个汽车类可以封装了颜色、品牌、速度等变量和加速、刹车等方法,对外只提供加速和刹车的接口,而隐藏了内部细节。
2. 继承(Inheritance):继承是指创建一个新类(子类)从已
有的类(父类)中继承属性和方法的过程。
子类可以通过继承父类的特性来扩展和增强功能,并且可以重用已有的代码。
例如,有一个动物类,定义了一些公共属性和方法,然后创建了狗类和猫类继承动物类,狗类和猫类就可以共享动物类的一些功能,同时可以根据需要添加自己的特定功能。
3. 多态(Polymorphism):多态是指同一类对象在不同情况下
可以表现出不同的行为。
对象多态性使用继承和接口实现,通过动态绑定和方法重写,允许不同的对象对同一个方法做出不同的响应。
例如,一个动物类中有一个叫声的方法,猫类和狗类都继承了动物类,并重写了叫声的方法,当通过调用叫声方法时,猫和狗的叫声不同,实现了多态性。
这三个原则是面向对象设计的基石,有助于实现代码的可重用性、可扩展性和灵活性。
面向对象设计六大原则
面向对象设计六大原则面向对象设计的原则是面向对象思想的提炼,它比面向对象思想的核心要素更具可操作性,但与设计模式相比,却又更加的抽象,是设计精神要义的抽象概括。
形象地将,面向对象思想像法理的精神,设计原则则相对于基本宪法,而设计模式就好比各式各样的具体法律条文了。
面向对象设计原则有6个:开放封闭原则,单一职责原则,依赖倒置原则,Liskov替换原则,迪米特法则和接口隔离原则或合成/聚合复用原则(不同资料略有不同,这里对7个都做了整理)。
1单一职责原则(Single Responsibility Principle SRP)There should never be more than one reason for a class to change. 什么意思呢?所谓单一职责原则就是一个类只负责一个职责,只有一个引起变化的原因。
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化会削弱或抑制这个类完成其他职责的能力,这个耦合会导致脆弱的设计。
软件设计真正要做的许多内容,就是发现职责并把这些职责相互分离;如果能够想到多于一个动机去改变一个类,那么这个类就具有多于一个职责,就应该考虑类的分离。
以调制解调器为例如下图:从上述类图里面我们发现有四个方法Dial(拨通电话),Hangup(挂电话),Receive(收到信息),Send(发送信息),经过分析不难判断出,实际上Dial(拨通电话)和Hangup(挂电话)是属于连接的范畴,而Receive(收到信息)和Send(发送信息)是属于数据传送的范畴。
这里类包括两个职责,显然违反了SRP。
这样做有潜在的隐患,如果要改变连接的方式,势必要修改Modem,而修改Modem 类的结果导致凡事依赖Modem类可能都需要修改,这样就需要重新编译和部署,不管数据传输这部分是否需要修改。
因此要重构Modem类,从中抽象出两个接口,一个专门负责连接,另一个专门负责数据传送。
面向对象程序设计概述
1.什么是结构化程序设计方法?这种方法有哪些优点和缺点?【解答】结构化程序设计方法是指20世纪60年代开始出现的高级语言程序设计方法,由于采用了数据结构化、语句结构化、数据抽象和过程抽象等概念,使程序设计在符合客观事物与逻辑的基础上更进了一步。
结构化程序设计的思路是:自顶向下、逐步求精。
程序结构由具有一定功能的若干独立的基本模块(单元)组成,各模块之间形成一个树状结构,模块之间的关系比较简单,其功能相对独立,模块化通过子程序的方式实现。
结构化程序设计方法使高级语言程序设计开始变得普及,并促进了计算机技术的深入应用。
虽然结构化程序设计方法采用了功能抽象、模块分解与组合,以及自顶向下、逐步求精的方法,能有效地将各种复杂的任务分解为一系列相对容易实现的子任务,有利于软件开发和维护;但与面向对象程序设计方法相比,结构化程序设计存在的主要问题是,程序的数据和对数据的操作相互分离,若数据结构改变,程序的大部分甚至所有相关的处理过程都要进行修改。
因此,对于开发大型程序具有一定的难度,软件的可重用性差,维护工作量大,不完全符合人类认识世界的客观规律。
2.面向对象程序设计有哪些重要特点?【解答】软件设计的目的是为了解决日常生活中存在的各种实际问题,面向对象程序设计与以往各种程序设计方法的根本区别是程序设计的思维方法的不同。
它主要具有如下重要特点:(1)面向对象程序设计实现了较直接地描述客观世界中存在的事物(即对象)及事物之间的相互关系,它所强调的基本原则是直接面对客观事物本身进行抽象,并在此基础上进行软件开发,将人类的思维方式与表达方式直接应用在软件设计中。
(2)面向对象的程序设计将客观事物看作具有属性和行为的对象,通过对客观事物进行抽象来寻找同一类对象的共同属性(静态特征)和行为(动态特征),并在此基础上形成类。
(3)面向对象的程序设计将数据和对数据的操作封装在一起,提高了数据的安全性和隐蔽性。
第1章面向对象程序设计概述3(4)面向对象的程序设计通过类的继承与派生机制以及多态性特性,提高了软件代码的可重用性,因而大大缩减了软件开发的相关费用及软件开发周期,并有效地提高了软件产品的质量。
面向对象设计七大原则
⾯向对象设计七⼤原则1. 单⼀职责原则(Single Responsibility Principle)每⼀个类应该专注于做⼀件事情。
2. ⾥⽒替换原则(Liskov Substitution Principle)超类存在的地⽅,⼦类是可以替换的。
3. 依赖倒置原则(Dependence Inversion Principle)实现尽量依赖抽象,不依赖具体实现。
4. 接⼝隔离原则(Interface Segregation Principle)应当为客户端提供尽可能⼩的单独的接⼝,⽽不是提供⼤的总的接⼝。
5. 迪⽶特法则(Law Of Demeter)⼜叫最少知识原则,⼀个软件实体应当尽可能少的与其他实体发⽣相互作⽤。
6. 开闭原则(Open Close Principle)⾯向扩展开放,⾯向修改关闭。
7. 组合/聚合复⽤原则(Composite/Aggregate Reuse Principle CARP)尽量使⽤合成/聚合达到复⽤,尽量少⽤继承。
原则:⼀个类中有另⼀个类的对象。
细则单⼀职责原则(Single Responsibility Principle)因为:可以降低类的复杂度,⼀个类只负责⼀项职责,其逻辑肯定要⽐负责多项职责简单的多;提⾼类的可读性,提⾼系统的可维护性;变更引起的风险降低,变更是必然的,如果单⼀职责原则遵守的好,当修改⼀个功能时,可以显著降低对其他功能的影响。
需要说明的⼀点是单⼀职责原则不只是⾯向对象编程思想所特有的,只要是模块化的程序设计,都适⽤单⼀职责原则。
所以:从⼤局上看Android中的Paint和Canvas等类都遵守单⼀职责原则,Paint和Canvas各司其职。
⾥⽒替换原则(Liskov Substitution Principle)因为:⾥⽒替换原则告诉我们,在软件中将⼀个基类对象替换成它的⼦类对象,程序将不会产⽣任何错误和异常,反过来则不成⽴,如果⼀个软件实体使⽤的是⼀个⼦类对象的话,那么它不⼀定能够使⽤基类对象。
面向对象七大基本设计原则
面向对象七大基本设计原则面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心。
在设计面向对象的程序的时,模式不是一定要套的,但是有一些原则最好是遵守。
这些原则已知的有七个,包括:单一职责原则、开闭原则、里氏代换原则、依赖注入(倒转)原则、接口分离原则、迪米特原则、合成聚合复用原则。
原则一单一职责原则单一职责原则(SRP:Single responsibility principle)又称单一功能原则核心:解耦和增强内聚性(高内聚,低耦合)。
描述:类被修改的几率很大,因此应该专注于单一的功能。
如果你把多个功能放在同一个类中,功能之间就形成了关联,改变其中一个功能,有可能中止另一个功能,这时就需要新一轮的测试来避免可能出现的问题。
原则二里氏替换原则里氏替换原则(LSP:Liskov Substitution Principle)核心:在任何父类出现的地方都可以用他的子类来替代(子类应当可以替换父类并出现在父类能够出现的任何地方)四层含义:(1)子类必须完全实现父类的方法。
在类中调用其他类是务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。
(2)子类可以有自己的个性。
子类当然可以有自己的行为和外观了,也就是方法和属性(3)覆盖或实现父类的方法时输入参数可以被放大。
即子类可以重载父类的方法,但输入参数应比父类方法中的大,这样在子类代替父类的时候,调用的仍然是父类的方法。
即以子类中方法的前置条件必须与超类中被覆盖的方法的前置条件相同或者更宽松。
(4)覆盖或实现父类的方法时输出结果可以被缩小。
原则三依赖注入原则依赖注入原则(DIP:Dependence Inversion Principle)别名:依赖倒置原则或依赖反转原则核心:要依赖于抽象,不要依赖于具体的实现三层含义:(1)高层模块不应该依赖低层模块,两者都应该依赖其抽象(抽象类或接口);(2)抽象不应该依赖细节(具体实现);(3)细节(具体实现)应该依赖抽象。
面向对象六大设计原则
面向对象六大设计原则面向对象编程(Object Oriented Programming,OOP)是一种编程范式,它将代码组织成一系列相互关联的对象,每个对象代表现实世界中的一个实体或概念。
在面向对象编程中,有六大设计原则被广泛认可和遵循,它们是:1. 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起它变化的原因。
即一个类只负责一个功能领域的相关职责,避免将多个职责集中在一个类中,以提高代码的可维护性和可读性。
2. 开闭原则(Open-Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
即在不修改现有代码的情况下,可以通过扩展来增加新功能,以提高代码的可扩展性和可复用性。
3. 里氏替换原则(Liskov Substitution Principle,LSP):子类型必须能够替换它们的父类型。
即在继承关系中,子类可以替换父类在代码中的位置,而不会导致程序出现错误。
4. 接口隔离原则(Interface Segregation Principle,ISP):不应该强迫客户依赖于他们不使用的接口。
即在设计接口时,应该将不同的功能隔离开来,避免接口过于庞大和复杂。
5. 依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于底层模块,二者都应该依赖于抽象。
即在代码设计中,应该依赖于抽象而不是具体实现,以提高代码的灵活性和可复用性。
6. 迪米特法则(Law of Demeter,LoD):一个对象应该对其他对象保持最少的了解。
即在代码设计中,应该尽量减少对象之间的耦合,只暴露必要的接口和方法,以提高代码的可维护性和可复用性。
这些设计原则旨在帮助开发者编写高质量、可维护、可扩展的代码。
在实际编程中,遵循这些原则可以提高代码的可读性、可复用性和可维护性,降低代码的耦合度,提高系统的灵活性和可扩展性。
面向对象技术
二面向对象技术的基本概念
1.对象 对象是客观世界中的一个实体,它具有以下特性: (1)有一个名字以区别其它对象; (2)有一个状态用来描述它的某些特征; (3)有一组操作,每个操作决定对象的一种功能或行为; (4)对象的操作可分为两类:一类是自身所承受的操作, 一类是施加于其它对象的操作。 简而言之,对象是其自身具有的状态特征及可以对这 些状态施加的操作结合在一起所所构成的独立实体。 在面向对象技术中的“对象”术语,既可能泛指客观 世界中的某些事物,也可能专指它们在系统中的抽象。 在系统中,对象是系统中用来描述客观事物的一个实 体,它是构成系统的一个基本单位。一个对象有一组 属性和对属性进行操作的一组方法构成。
(3)面向对象的实现 面向对象的实现(编程)是要在面向对象设计的 基础上,选择一种面向对象的编程语言(c++,Java 等),把设计作适当映射并改写成相应的程序。 (4)面向对象的测试 面向对象的测试是指在测试过程中运用面向对象 技术,进行以对象(或类)为核心的测试。 (5)面向对象的维护 面向对象的维护是指在软件运行之后的正常活动, 软件维护不是一种“简单”的修理,而是一种复杂的 开发活动,它同样需要经过分析、设计、实现和测试, 因此是一种系统更新的迭代过程。
(1)一个清楚的边界,所有对象内部组件的范围被限定 在这个边界内; (2)一个接口,这个接口描述这个对象和其它对象之间 的相互作用; (3)受保护的内部实现,这个实现给出了由软件对象提 供的功能的细节,实现细节不能在这个的范围被限定 在定义这个对象的类的外面访问。 面向对象系统中的封装单位是对象,即主要是对象的 封装。 6.继承 继承:继承所表达的就是一种对象类之间的相互关系, 它使得某类对象可以继承另外一类对象的特征和能力。 若类间具有继承关系,则它们之间应具有下列几个特 征:
Java面向对象设计的六大原则
Java⾯向对象设计的六⼤原则这是设计模式系列开篇的第⼀篇⽂章。
也是我学习设计模式过程中的总结。
这篇⽂章主要讲的是⾯向对象设计中,我们应该遵循的六⼤原则。
只有掌握了这些原则,我们才能更好的理解设计模式。
我们接下来要介绍以下6个内容。
单⼀职责原则——SRP开闭原则——OCP⾥式替换原则——LSP依赖倒置原则——DIP接⼝隔离原则——ISP迪⽶特原则——LOD单⼀职责原则单⼀职责原则的定义是就⼀个类⽽⾔,应该仅有⼀个引起他变化的原因。
也就是说⼀个类应该只负责⼀件事情。
如果⼀个类负责了⽅法M1,⽅法M2两个不同的事情,当M1⽅法发⽣变化的时候,我们需要修改这个类的M1⽅法,但是这个时候就有可能导致M2⽅法不能⼯作。
这个不是我们期待的,但是由于这种设计却很有可能发⽣。
所以这个时候,我们需要把M1⽅法,M2⽅法单独分离成两个类。
让每个类只专⼼处理⾃⼰的⽅法。
单⼀职责原则的好处如下:可以降低类的复杂度,⼀个类只负责⼀项职责,这样逻辑也简单很多提⾼类的可读性,和系统的维护性,因为不会有其他奇怪的⽅法来⼲扰我们理解这个类的含义当发⽣变化的时候,能将变化的影响降到最⼩,因为只会在这个类中做出修改。
开闭原则开闭原则和单⼀职责原则⼀样,是⾮常基础⽽且⼀般是常识的原则。
开闭原则的定义是软件中的对象(类,模块,函数等)应该对于扩展是开放的,但是对于修改是关闭的。
当需求发⽣改变的时候,我们需要对代码进⾏修改,这个时候我们应该尽量去扩展原来的代码,⽽不是去修改原来的代码,因为这样可能会引起更多的问题。
这个准则和单⼀职责原则⼀样,是⼀个⼤家都这样去认为但是⼜没规定具体该如何去做的⼀种原则。
开闭原则我们可以⽤⼀种⽅式来确保他,我们⽤抽象去构建框架,⽤实现扩展细节。
这样当发⽣修改的时候,我们就直接⽤抽象了派⽣⼀个具体类去实现修改。
⾥⽒替换原则⾥⽒替换原则是⼀个⾮常有⽤的⼀个概念。
他的定义如果对每⼀个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都替换成o2的时候,程序P的⾏为都没有发⽣变化,那么类型T2是类型T1的⼦类型。
面向对象的设计原则
面向对象的设计原则面向对象的设计原则(OODP),又称软件工程基本原则,是一组遵循的准则,它使构建和维护高质量,灵活,容易调整复杂软件应用程序变得更容易、可靠和有效。
首先,“单一职责原则”(SRP)指出,每个类,函数,模块等应当负责一个单一任务:它们应该完成核心动机,而不是执行其他可能是更大的团队动机的其他事情。
也就是说,单一职责原则要求每个对象尽可能少地做更多的事情,从而减少复杂性,增加模块的复用以及允许有效的灵活性和松耦合。
其次,“开放封闭原则”(OCP)定义了一种设计问题可以解决,而实施解决方案时不必修改该设计问题。
使软件系统处于“开放”状态,以便可以操作它,而封闭状态则可以防止对系统的非零售改动。
随后,“接口隔离原则”(ISP)要求所有客户端程序使用接口,而不是具体类。
这种原则有助于确保客户端代码不依赖它不使用的功能,从而获得较好的内聚,更可靠的模块结构。
此外,“依赖反转原则”(DIP)指出,对象应该依赖于抽象而不是具体实现。
它有助于更抽象模块的可重用性和可测试性,从而避免了大量硬编码依赖项和不可管控结果,从而简化了代码维护和测试。
另一方面,“迪米特法则”通常被简称为“最少知识原则”(LKP),它要求一个对象应该尽可能少地知道其他对象的内容,以及它们的交互。
意思是说,它们既不应该依赖于内部的实现细节,也不应该被任何其他对象控制,从而实现良好的封装和高内聚,并改善软件的可维护性和可测试性。
最后,“合成复用原则”(CRP)指出,应该尽量组合构件,而不是继承构件,以建立可重用性和高内聚性,从而减少代码复杂性。
例如,如果某个构件有两个分解部分,而基本构件只是实现其中一个部分,则可以使用组合而不是继承来实现构件的全部功能。
面向对象编程的五大原则
面向对象编程的五大原则单一职责原则开放封闭原则里氏替换原则依赖倒置原则接口隔离原则高层的实现不应该依赖底层,(父类可以替换掉任何子类),具体说就是我们要针对接口抽象来编程,不要针对实现来编程,这样程序才能解耦。
一、"开-闭"原则(Open-Closed Principle,OCP)1.1"开-闭"原则的定义及优点1)定义:一个软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification.)。
即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。
2)满足"开-闭"原则的系统的优点a)通过扩展已有的软件系统,可以提供新的行为,以满足对软件的新需求,使变化中的软件系统有一定的适应性和灵活性。
b)已有的软件模块,特别是最重要的抽象层模块不能再修改,这就使变化中的软件系统有一定的稳定性和延续性。
c)这样的系统同时满足了可复用性与可维护性。
1.2如何实现"开-闭"原则在面向对象设计中,不允许更改的是系统的抽象层,而允许扩展的是系统的实现层。
换言之,定义一个一劳永逸的抽象设计层,允许尽可能多的行为在实现层被实现。
解决问题关键在于抽象化,抽象化是面向对象设计的第一个核心本质。
对一个事物抽象化,实质上是在概括归纳总结它的本质。
抽象让我们抓住最最重要的东西,从更高一层去思考。
这降低了思考的复杂度,我们不用同时考虑那么多的东西。
换言之,我们封装了事物的本质,看不到任何细节。
在面向对象编程中,通过抽象类及接口,规定了具体类的特征作为抽象层,相对稳定,不需更改,从而满足"对修改关闭";而从抽象类导出的具体类可以改变系统的行为,从而满足"对扩展开放"。
对实体进行扩展时,不必改动软件的源代码或者二进制代码。
软件工程第11章面向对象设计
2. 重用已有的类
重用已有类(代码重用)实现分析模型;若没有可以重用类而需要创建新 类时,则在设计这些新类时需要考虑其可重用性。
对于已有的可重用类,典型重用方法和过程如下: 1)选择可能被重用的已有类,标出类中对本问题无用的属性和服务,选 择那些能使无用的属性和服务最少的类; 2)从被重用的已有类派生出问题域类(继承重用类而产生问题域类); 3)标出从已有类继承来的属性和服务,而无须在分析类内定义;
6. 可重用
软件重用是提高软件开发生产率和目标系统质量的重要途径。 重用有两方面的含义: 一是尽量使用已有的类(类库或已建立的类), 二是如果确实需要创建新类,则在设计这些新类的协议时,应该考虑将 来的可重复使用性。
11.2
启发规则
与结构设计规则类似,通过OOD实践也总结了一些设计规则: 1. 设计结果应该清晰易懂 设计结果清晰、易读、易懂,是提高软件可维护性和可重用性的重要 措施。保证设计结果清晰易懂的主要因素为:用词一致;使用已有的 协议;避免模糊的定义等。
1)层次组织:这种组织方案把软件系统组织成一个层次系统,每层是一 个子系统。上层和下层自系统形成C/S结构 层次结构的两种模式:封闭式和开放式:封闭式,每层子系统仅仅使用其 直接下层提供的服务;开放式,任一层次可以向下跨层次调用。 2)块状组织:把软件系统垂直地分解成若干个相对独立的、松耦合的子 系统,一个子系统相当于一块,每块提供一种类型的服务。
第11章
11.1 11.2 11.3 11.4 11.5 11.6 11.7 11.8 11.9 11.10 11.11
面向对象设计
面向对象设计的准则 启发规则 软件重用 系统分解 设计问题域子系统 设计人机交互子系统 设计任务管理子系统 设计数据管理子系统 设计类中的服务 设计关联 设计优化
Chap.1面向对象程序设计概述
[例1.1]输入一个日期并输出。 1.1]输入一个日期并输出。 输入一个日期并输出 #include<iostream.h> //定义一个Date结构体类型 定义一个Date struct Date //定义一个Date结构体类型 //成员有 成员有: { int year,month,day; //成员有:年、月、日 }; //输入过程的定义 void Input(Date *pd) //输入过程的定义 { cout<<"Please input year,month,day: "; cin>>pd->year>>pd->month>>pdcin>>pd->year>>pd->month>>pd->day; } //输出过程的定义 void Output(Date *pd) //输出过程的定义 cout<<pd->year<<"年"<<pd->month<<"月 { cout<<pd->year<<"年"<<pd->month<<"月“ <<pd->day<<"日 <<pd->day<<"日"<<endl; } void main() //定义一个结构体变量 { Date d; //定义一个结构体变量 //输入过程的调用 Input(&d); //输入过程的调用 //输出过程的调用 Output(&d); //输出过程的调用 }
开闭原则例子-概述说明以及解释
开闭原则例子-概述说明以及解释1.引言1.1 概述开闭原则是面向对象设计中的一条重要原则,它强调软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
也就是说,当我们需要更改一个软件实体的行为时,应尽量通过扩展而不是修改源代码来实现。
这一原则的提出是为了解决软件系统在面临需求变化时引起的问题。
传统的软件设计往往采用的是紧耦合的方式,即各个组件之间存在较高的依赖性。
这样一旦需求发生变化,就需要修改原有的代码,以适应新的需求。
但是随着项目规模的增大和复杂性的提高,频繁的代码修改将会带来诸多问题,如引入新的错误、增加项目维护成本等。
而开闭原则的存在就是为了解决这个问题。
它鼓励我们设计可扩展的软件结构,通过添加新的代码来满足新的需求,而不是去修改已有的代码。
这样一来,原有的代码不会受到影响,系统的稳定性和可维护性也能够得到保证。
开闭原则的实际运用可以通过一些例子来说明。
在接下来的章节中,我们将介绍两个具体的开闭原则例子,以帮助读者更好地理解和应用这一原则。
总之,开闭原则是一条非常重要的软件设计原则,它能够有效地提高系统的灵活性和可扩展性。
通过遵循该原则,我们可以更好地应对需求变化,减少代码修改带来的风险,为项目的开发和维护带来便利。
在接下来的章节中,我们将详细介绍开闭原则的应用和意义。
1.2文章结构文章结构部分的内容可以包括以下内容:在文章结构部分,我们将介绍本篇文章的整体布局和章节安排。
通过清晰的结构,读者可以更加方便地理解本文内容的组织和发展。
本文共分为引言、正文和结论三个部分。
引言部分将在开篇对开闭原则进行概述,简要介绍开闭原则的定义、目的和作用,为读者提供了解这一原则的基础知识。
正文部分包括了两个开闭原则的例子,通过具体的案例来说明开闭原则的应用和实践。
第一个例子将阐述一个在软件开发中遵循开闭原则的实际情况,通过代码示例和具体分析,展示开闭原则带来的好处。
第二个例子将进一步探讨另一个领域中开闭原则的应用,通过实际案例来说明开闭原则对系统的扩展性和维护性的重要性。
面向对象设计的七大原则
⾯向对象设计的七⼤原则在上⼀篇⾥我们谈了谈为何设计模式,那接下来我们再浅谈⼀下在⾯向对象设计中我们常常要遵循的⼀些原则。
这些原则是经过⽆数的前⼈总结出来的经验的结晶。
仅仅有遵循这些原则。
你才有可能涉及出优秀的代码。
今天我们要谈的原则有七⼤原则,即:单⼀职责。
⾥⽒替换。
迪⽶特法则,依赖倒转,接⼝隔离,合成/聚合原则。
开放-封闭。
1. 开闭原则定义:软件实体应当对扩展开放,对改动关闭。
这句话说得有点专业。
更通俗⼀点讲,也就是:软件系统中包括的各种组件,⽐如模块(Modules)、类(Classes)以及功能(Functions)等等。
应该在不改动现有代码的基础上。
去扩展新功能。
开闭原则中“开”。
是指对于组件功能的扩展是开放的。
是同意对其进⾏功能扩展的。
开闭原则中“闭”。
是指对于原有代码的改动是封闭的,即不应该改动原有的代码。
问题由来:凡事的产⽣都有缘由。
我们来看看。
开闭原则的产⽣缘由。
在软件的⽣命周期内,由于变化、升级和维护等原因须要对软件原有代码进⾏改动时。
可能会给旧代码中引⼊错误,也可能会使我们不得不正确整个功能进⾏重构,⽽且须要原有代码经过⼜⼀次測试。
这就对我们的整个系统的影响特别⼤。
这也充分展现出了系统的耦合性假设太⾼,会⼤⼤的添加后期的扩展。
维护。
为了解决问题,故⼈们总结出了开闭原则。
解决开闭原则的根本事实上还是在解耦合。
所以。
我们⾯向对象的开发,我们最根本的任务就是解耦合。
解决⽅法:当软件须要变化时。
尽量通过扩展软件实体的⾏为来实现变化。
⽽不是通过改动已有的代码来实现变化。
⼩结:开闭原则具有理想主义的⾊彩。
说的⾮常抽象,它是⾯向对象设计的终极⽬标。
其它⼏条原则,则能够看做是开闭原则的实现。
我们要⽤抽象构建框架,⽤实现扩展细节。
2. 单⼀职责原则(Single Responsibility Principle)定义:⼀个类。
仅仅有⼀个引起它变化的原因。
即:应该仅仅有⼀个职责。
每个职责都是变化的⼀个轴线。
面向对象设计的三个基本要素
面向对象设计的三个基本要素面向对象的三个基本特征是:封装、继承、多态。
1·封装性封装性是一种信息隐蔽技术,他体现于类的说明,是对象重要的特性。
封装使得数据和操作数据的方法封装为一个整体,想成独立性很强的模块,使得用户只能看到对象的外部特性(对象可以接受拿些信息,可以进行何种处理),而对象的内部特性(内部私有属性和实现处理能力的算法)用户是看不到的。
简而言之就是说,封装使对象的设计者与对象的使用者分开,使用者只要知道对象可以做什么就可以了,无需知道具体是怎么实现的。
借助封装有助于提高类和系统的安全性。
2·继承性继承是一种由已有类创建子类的机制,利用继承,可以先创建一个共有属性的一般类,根据这个类再创建具有特殊属性的子类,被继承的类成为父类,当然子类也可以成为父类来继续向下扩展。
3·多态性同一个信息被不同的对象接收到时可能产生不同的行为,这就是多态性。
有继承(接口)有重写,父类引用指向子类对象,就会产生多态。
多态可以改善程序的组织架构,提高程序的可扩展性。
二、面向对象设计的五个基本设计原则面向对象设计的五个基本设计原则是:单一职责原则(SRP)、开放封闭原则(OCP)、Liskov替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)1·单一职责原则(Single-Responsibility Principle)其核心思想为:一个类只做一件事情,只有一个引起他的变化。
单一职责原则可以看做是低耦合,高内聚在面向对象原则上的隐身,将职责定义为引起变化的原因,以提高内举行来减少引起变化的原因。
职责过多可能引起他变化的原因也就越多,这将导致职责依赖,相互之间产生影响,从而大大损伤内聚性和耦合度。
单一职责就是指,只有一种单一的功能,不要为类实现过多的功能点,有些功能可以定义为接口来实现,以保证只有一个引起他变化的原因。
专注是一个人优良的品质。
同样的,单一也是一个类的优良设计,杂交不清的职责将使得代码看起来特别别扭,牵一发动全身,有失没敢和必然导致丑陋的系统错误风险。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
面向对象设计原则概述对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一。
在面向对象设计中,可维护性的复用是以设计原则为基础的。
每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。
面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则。
面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一,在设计模式的学习中,大家经常会看到诸如“XXX模式符合XXX原则”、“XXX模式违反了XXX原则”这样的语句。
最常见的7种面向对象设计原则如下表所示:表1 7种常用的面向对象设计原则面向对象设计原则之单一职责原则单一职责原则是最简单的面向对象设计原则,它用于控制类的粒度大小。
单一职责原则定义如下:单一职责原则告诉我们:一个类不能太“累”!在软件系统中,一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。
单一职责原则是实现高内聚、低耦合的指导方针,它是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,而发现类的多重职责需要设计人员具有较强的分析设计能力和相关实践经验。
下面通过一个简单实例来进一步分析单一职责原则:在图1中,CustomerDataChart类承担了太多的职责,既包含与数据库相关的方法,又包含与图表生成和显示相关的方法。
如果在其他类中也需要连接数据库或者使用findCustomers()方法查询客户信息,则难以实现代码的重用。
无论是修改数据库连接方式还是修改图表显示方式都需要修改该类,它不止一个引起它变化的原因,违背了单一职责原则。
因此需要对该类进行拆分,使其满足单一职责原则,类CustomerDataChart 可拆分为如下三个类:(1) DBUtil:负责连接数据库,包含数据库连接方法getConnection();(2) CustomerDAO:负责操作数据库中的Customer表,包含对Customer表的增删改查等方法,如findCustomers();(3) CustomerDataChart:负责图表的生成和显示,包含方法createChart()和displayChart()。
使用单一职责原则重构后的结构如图2所示:图2 重构后的结构图面向对象设计原则之开闭原则开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。
开闭原则由Bertrand Meyer于1988年提出,其定义如下:在开闭原则的定义中,软件实体可以指一个软件模块、一个由多个类组成的局部结构或一个独立的类。
任何软件都需要面临一个很重要的问题,即它们的需求会随时间的推移而发生变化。
当软件系统需要面对新的需求时,我们应该尽量保证系统的设计框架是稳定的。
如果一个软件设计符合开闭原则,那么可以非常方便地对系统进行扩展,而且在扩展时无须修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。
随着软件规模越来越大,软件寿命越来越长,软件维护成本越来越高,设计满足开闭原则的软件系统也变得越来越重要。
为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。
在Java、C#等编程语言中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为移至具体的实现层中完成。
在很多面向对象编程语言中都提供了接口、抽象类等机制,可以通过它们定义系统的抽象层,再通过具体类来进行扩展。
如果需要修改系统的行为,无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。
图1 初始设计方案结构图在本实例中,由于在ChartDisplay类的display()方法中针对每一个图表类编程,因此增加新的图表类不得不修改源代码。
可以通过抽象化的方式对系统进行重构,使之增加新的图表类时无须修改源代码,满足开闭原则。
具体做法如下:(1) 增加一个抽象图表类AbstractChart,将各种具体图表类作为其子类;(2) ChartDisplay类针对抽象图表类进行编程,由客户端来决定使用哪种具体图表。
重构后结构如图2所示:图2 重构后的结构图在图2中,我们引入了抽象图表类AbstractChart,且ChartDisplay针对抽象图表类进行编程,并通过setChart()方法由客户端来设置实例化的具体图表对象,在ChartDisplay的display()方法中调用chart对象的display()方法显示图表。
如果需要增加一种新的图表,如折线图LineChart,只需要将LineChart也作为AbstractChart的子类,在客户端向ChartDisplay中注入一个LineChart对象即可,无须修改现有类库的源代码。
注意:因为xml和properties等格式的配置文件是纯文本文件,可以直接通过VI编辑器或记事本进行编辑,且无须编译,因此在软件开发中,一般不把对配置文件的修改认为是对系统源代码的修改。
如果一个系统在扩展时只涉及到修改配置文件,而原有的Java代码或C#代码没有做任何修改,该系统即可认为是一个符合开闭原则的系统。
面向对象设计原则之里氏代换原则里氏代换原则由2008年图灵奖得主、美国第一位计算机科学女博士Barbara Liskov教授和卡内基·梅隆大学Jeannette Wing教授于1994年提出。
其严格表述如下:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有变化,那么类型S是类型T的子类型。
这个定义比较拗口且难以理解,因此我们一般使用它的另一个通俗版定义:里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。
例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。
例如有两个类,一个类为BaseClass,另一个是SubClass类,并且SubClass类是BaseClass类的子类,那么一个方法如果可以接受一个BaseClass类型的基类对象base 的话,如:method1(base),那么它必然可以接受一个BaseClass类型的子类对象sub,method1(sub)能够正常运行。
反过来的代换不成立,如一个方法method2接受BaseClass 类型的子类对象sub为参数:method2(sub),那么一般而言不可以有method2(base),除非是重载方法。
里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
在使用里氏代换原则时需要注意如下几个问题:(1)子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。
根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
(2) 我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。
里氏代换原则是开闭原则的具体实现手段之一。
(3) Java语言中,在编译阶段,Java编译器会检查一个程序是否符合里氏代换原则,这是一个与实现无关的、纯语法意义上的检查,但Java编译器的检查是有局限的。
图1原始结构图在本实例中,可以考虑增加一个新的抽象客户类Customer,而将CommonCustomer 和VIPCustomer类作为其子类,邮件发送类EmailSender类针对抽象客户类Customer编程,根据里氏代换原则,能够接受基类对象的地方必然能够接受子类对象,因此将EmailSender中的send()方法的参数类型改为Customer,如果需要增加新类型的客户,只需将其作为Customer类的子类即可。
重构后的结构如图2所示:图2重构后的结构图里氏代换原则是实现开闭原则的重要方式之一。
在本实例中,在传递参数时使用基类对象,除此以外,在定义成员变量、定义局部变量、确定方法返回类型时都可使用里氏代换原则。
针对基类编程,在程序运行时再确定具体子类。
利利斯科夫:2004年约翰·冯诺依曼奖得主,美国工程院院士,麻省理工学院电子电气与计算机科学系教如果说开闭原则是面向对象设计的目标的话,那么依赖倒转原则就是面向对象设计的主要实现机制之一,它是系统抽象化的具体实现。
依赖倒转原则是Robert C. Martin在1996年为“C++Reporter”所写的专栏Engineering Notebook的第三篇,后来加入到他在2002年出版的经典著作“Agile Software Development, Principles, Patterns, and Practices”一书中。
依赖倒转原则定义如下:依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。
为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。
在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。
在实现依赖倒转原则时,我们需要针对抽象层编程,而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中,依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。