面向对象(Object-Oriented, OO)方法是一种非常实用的系统软件开发方法,它以客观世界中的对象为中心,其分析和设计思想符合人们的思维方式,结果与客观世界的实际比较接近,容易被人们所接受。
大纲
目录
责任链模式(Chain of Responsibility Pattern)
模板方法模式(Template Method Pattern)
7.1 面向对象基础
7.1.1 相关概念
面向对象 = 对象(Object)+ 分类(Classification)+ 继承(Inheritance)+ 通过消息的通信(Communication withMessages)
面向对象的特点:封装、继承、多态。
封装
封装是一种信息隐蔽技术,目的是使对象的使用者和生产者分离,使开发人员无需了解所要使用的软件组件内部的工作机制,只需知道如何使用组件。(只关心结果,不关心过程。)
对象
基本的运行实体,既包括数据(属性),也包括作用于数据的操作(行为)。一个对象把属性和行为封装为一个整体。
e.g. 电视机有颜色、音量、亮度等属性,可以有切换频道、调整音量等操作。电视机的各组成部分(如显像管、电路板等)等都封装在电视机吉祥中,人们不知道也不关心电视机如何实现这些操作。
类
类是对象的抽象,定义了一组大体相似的对象结构,包含的方法和数据描述了一组对象的共同行为和属性。对类做出定义后,面对类的属性进行不同赋值即可得到该类的对象实例。一个类可以产生n多个对象,每个对象包括类里面共有的属性和方法。
类可分为三种:
- 实体类:用于对必须存储的信息和相关行为建模的类,是需要长久保存且一直存在的
- 边界类:系统内部与系统外部的业务主角之间进行交互建模的类
- 控制类:用于对一个或几个用例所特有的控制行为进行建模,在用例执行过程中被动出现的特定行为的类。
在领域类模型中会涉及描述类自身情况的属性与操作,还会有描述类与类之间的关联(通过消息),但不会有对象层次的内容。
消息
消息是对象之间进行通信的一种构造。当一个消息发送给某个对象时,接收到信息的对象经过解释,然后予以相应。
继承
父类和子类之间共享数据和方法的机制。是类之间的一种关系。
多态
继承为前提。不同的对象收到同一个消息时产生完全不同的反应。包括参数多态(不同类型参数多种结构类型)、包含多态(父子类型关系)、过载多态(类似于重载,一个名字不同含义)、强制多态(强制类型转换)四种类型。多态由继承机制支持。
覆盖(重写)
子类在原有父类接口的基础上,用适合于自己要求的实现去置换父类中的相应实现,即在子类中重定义一个与父类同名同参的方法。
重载
与覆盖要区分开,函数重载与子类父类无关,且函数是同名不同参数。好处是使用方法较为方便,不需要用不同参数色设计不同的方法名。
其它概念
静态类型:一个对象的类型在编译时就确定;动态类型:对象类型在运行时才能确定。
静态绑定:基于静态类型,在程序执行前方法已经被绑定。
动态绑定:基于动态类型,运行时根据变量实际引用的对象决定调用哪个方法,支持多态。
7.1.2 面向对象分析
面向对象分析:是为了确定问题域,理解问题。确定系统的功能、性能要求。
包含五个活动:认定对象(按自然存在的实体确定对象)、组织对象(分析对象关系,抽象成类)、对象间的相互作用(描述各对象在应用系统中的关系)、确定对象的操作(操作,如创建增加删除等)、定义对象的内部信息(属性)。
7.1.3 面向对象设计
面向对象设计:是设计分析模型和实现相应源代码,在目标代码环境中这种源代码可被执行。设计问题域的解决方案。
面向对象程序设计:用面向对象程序设计语言将程序组织为互相协作的对象集合,每个对象标识某个类的实例,类通过继承等关系进行组织。详见之后之后为下午考试的软件设计的资料。
面向对象测试:与普通测试步骤并无不同。可分为四个层次:算法层(测试类中定义的每个方法,类似单元测试),类层(测试同一个类中所有方法与属性的相互作用,特有的模块测试)、模板层(测试一组协同工作的类之间的相互作用,类似集成测试)、系统层(类似系统测试)。
面向对象的设计原则
(1)单一责任原则(Single Responsibility Principle, SRP):让一个类只做一件事情,不要把太多的任务放在一个类里。好处:当你需要修改某个功能时,只需要关注一个类,而不用担心影响其他功能。
举例:学生如果把所有课程的笔记、作业和书都放在一个文件夹里,当需要找到特定课程的资料时会变得非常混乱。相反,如果为每门课程都准备一个专用的文件夹你就能更轻松地管理和找到所需的信息。每个文件夹就代表了一个类,它们只负责一个特定的任务,即存储与该课程相关的资料。
(2)开放封闭原则(Open &Close Principle, OCP):软件实体(类、模块、函数等)应该是可以扩展的,即开放的;但是不可修改,即封闭的。允许新功能的添加,而不会影响到已经运行良好的功能。
举例:编写一个图形绘制软件,有一个Shape类,代表各种形状,如果想添加一个新的形状,比如三角形,可以创建一个新的类(如Triangle类),而不是修改已有的Shape类。
(3)里氏替换原则(LSP):强调子类应该能够替换父类而不会影响程序的正确性。换句话说,你应该能够使用子类的实例来替代父类的实例而不引发错误。
举例:有一个 Bird 类,代表鸟类,其中有一个fly方法。现在派生了一个企鹅类。根据里氏替换原则,应该能够在不引发错误的情况下使用企鹅对象来调用fly 方法,即使实际上企鹅不会飞
(4)依赖倒置原则(DIP):强调抽象应该依赖于细节,而不是相反。高层模块不应该直接依赖于低层模块的细节,而应该通过抽象进行交互。类与类之间的依赖尽量通过接口来实现,不要直接调用。
举例:假设正在开发一个电子商务平台。有一个 0rderProcessor类负责处理订单。而这个类不应该直接依赖于具体的支付方式,而是依赖于个抽象的 PaymentGateway 接口。这样,可以轻松地更改支付方式,而不必修改OrderProcessor。
(5)接口分离原则:强调客户端不应该被强制依赖它们不需要的方法。接口应该只包含客户端需要的方法,避免造成冗余和不必要的复杂性。
举例:设计一个媒体播放器,应该根据功能拆分成不同的接口,如 AudioPlayer和 VideoPlayer这样,如果你只需要一个音频播放器,就不会被迫实现视频播放相关的方法,从而遵循了接口分离原则。
除了上述五大基本原则,还包括(6)重用发布等价原则,(7)共同封闭原则,(8)共同重用原则,(9)无环依赖原则,(10)稳定依赖原则,(11)稳定抽象原则
7.2 UML
UML(Unified Modeling Language,统一建模语言)是面向对象软件的标准化建模语言,和程序设计语言并无关系。
UML三要素:基本构造块,支配这些构造块放置在一起的规则和运用于整个语言的公共机制。
基本构造块包括:事物、关系、图
7.2.1 事物
四种事物:结构事物(静态事物)、行为事物(动态事物)、分组事物和注释事物。
(1)结构事物:通常是模型的静态部分,描述概念或物理元素。结构事物包括类、接口、写作、用例、主动类、构件、制品和节点。
(2)行为事物:UML模型的动态部分。描述了跨越时间和空间的行为,包括交互、动态及和活动。
交互:由在特点语境下共同完成一定任务的一组对象之间交换的消息组成。
状态机:一个对象/交互对象在生命期内相应时间所经历的状态序列。
活动:描述计算机过程执行的步骤序列,活动的一个步骤称为一个动作。
(3)分组事物:UML模型中的组织部分,由模型分解成的“盒子”,如包。
(4)注释事物:UML模型的解释部分。主要是注解(Note)。
7.2.2 关系
4种关系:依赖、关联、繁华和实现
(1)依赖:两个事物间的语义关系,一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。
(2)关联:结构关系。描述一组链,链是对象之间的连接。分为组合(强关联)和聚合(弱关联)。都是整体和部分间的结构关系。
(3)泛化:一般/特殊关系,子类和父类间的关系。在图形上,一个泛化关系是一条带有空心箭头的实线,指向父元素。
(4)实现:一个类元指定了另一个类元,保证执行的契约。应用:接口和实现它们的类或构件之间;用例和实现它们的协作之间。
7.2.3 图
1. 类图
静态图,为系统的静态设计视图,展现一组对象、接口、协作和它们之间的关系。
包括:类、接口、协作、依赖、泛化和关联关系。
多重度:不同类之间的联系,类似于数据库设计的表与表的关系。
注意,下图第一个聚集关系出现错误,应该是空心菱形,不是实心
2. 对象图
静态图,展现某一时刻一组对象及它们之间的关系,为类图的某一快照。在没有类图的前提下,对象图就是静态设计试图。
3. 用例图
静态图,展现了一组用例、参与者以及它们之间的关系。
用例中的参与者是人、硬件或者其它系统可以扮演的角色;用例是参与者完成的一系列操作。
用例之间的关系:包含、扩展(可做可不做)、泛化(多个实现方式)
4. 序列图
动态图,是场景的图形化表示,描述了以时间顺序组织的对象之间的交互活动。
有同步消息(进行阻塞调用,调用者中止执行,等待控制权返回,需要等待返回消息,用实心三角箭头表示)、异步消息(发出消息后继续执行,不引起调用者阻塞,也不等待返回消息,由空心头表示)、返回消息(由从右到左的虚线箭头表示)三种。
5. 通信图
动态图,即协作图,是顺序图的另一种表示方法,也是由对象和消息组成的图,只不过不强调时间顺序,只强调事件之间的通信,而且也没有固定的画法规则。和序列图统称为交互图。
6. 状态图
展现了一个状态机,描述单个对象在多个用例中的行为,包括简单状态和组合状态,转换可以通过时间触发器触发,事件出发后相应的监护条件会进行检查。
状态途中转换和状态是两个独立的概念,方框代表状态,箭头上的文字代表出发时间,实心圆点为起点和终点。
7. 活动图
特殊的状态图,展现了在系统内从一个活动到另二个活动的流程。
活动的分叉和汇合线是一条水平粗线。
8. 构件图
静态图,展现了一组构件之间的组织和依赖。
9. 部署图
静态图,描述物理模块的节点分布。部署组件之间的依赖是单向的,类似于包含关系。
例题
UML图中,对象图展现了(1),(2)所示对象图与下列所示类图不一致。
(A)一组对象、接口、协作和它们之间的关系
(B)一组用例、参与者以及它们之间的关系
(C)某一时刻一组对象以及它们之间的关系、
(D)以时间顺序组织的对象之间的交互活动
解析:(1)选C。A选项对应类图,B选项对应用例图,D选项对应序列图。(2):在对象图表示一个A对应了n个B,ABC都能反映上述关系,D中两个A对应一个B,错误,选D。
【2019】采用面向对象方法进行软件开发时,将汽车作为一个系统,以下( )之间不属于组合(Composition)关系。
(A) 汽车和座位 (B) 汽车和车窗 (C) 汽车和发动机 (D) 汽车和音乐系统
解析:汽车和音乐系统属于弱关联,是组合关系,其它三项是强关联,是聚合关系,选D。
【2014】如下所示的UML序列图中,(42)表示返回消息,Account类必须实现的方法有(43)。
(42)A.tanslD B.balance C.withdraw D.deposit
(43)A. start B. checkBalance()和withdraw()
C. deposit D. checkBalance()、withdraw()和deposit()
答案:(42)C,(43)D。
7.3 设计模式
23种主要设计模式如下图所示:
架构模式:软件设计中的高层决策,例如C/S结构,架构模式反映了开发软件系统过程中所作的基本设计决策。
设计模式:每一个设计模式描述了一个在我们周围不断重复发生的问题,以及改问题的解决方案的核心。四个基本要素:模式名称、问题、解决方案、效果。
惯用法:是最低层的模式,关注软件系统的设计与实现,实现时通过某种特定的程序设计语言来描述构件与软件之间的关系。每种编程语言都有它自己特定的模式,即语言的惯用法。例如计数就是C++语言中的一种惯用法。
设计模式——创建型
创建型:通过各种方式构建出对象的过程
工厂模式(Factory Pattern)
在工厂模式中,对象的创建不是直接通过new关键字完成,而是通过调用工厂方法来创建对象。
例子: 购物时可以使用工厂模式,告诉工厂你需要哪种类型的衣服,然后工厂可以根据你的需求制作并提供相应的衣服。
三种主要类型:
- 简单工厂模式:通过一个工厂类来创建所有需要的对象,客户端通过调用工厂类的静态方法来创建对象。
- 工厂方法模式:定义了一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类。
- 抽象工厂模式:提供了一个创建一系列相关或相互依赖对象的接口或抽象类,而无需指定它们具体的类)。
单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例。例子:the Queen指伊丽莎白皇后;操作系统的任务管理器是一个单例模式的应用。无论打开多少个任务管理器窗口,它们都指向同一个任务管理器实例。
建造者模式(Builder Pattern)
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
例子:建造者模式像是建造一座房子。假设你是一位工程师,有一个木制房子构建者和一个砖瓦房子构建者。你的任务是根据客户的要求建造不同类型的房子。客户A需要一座木制房子,你将选择木制房子构建者,然后使用它的方法来逐步构建木制房子,包括设置木质墙壁、建造屋顶和安装窗户。通过构建者模式,你可以根据客户的需求,逐步构建不同类型的房子,而不必亲自处理所有细节。这使得构建过程灵活日可维护,同时允许创建不同风格和材质的房子。
原型模式(Prototype Pattern)
通过复制现有对象来创建新对象。
例子:在电影制作中,制作人可以使用原型模式来复制特效道具,以便在不同场景中使用多个相同的道具。
创建型设计模式总结
口诀:单抽元件厂
设计模式——结构型
结构型设计模式,关键如何将对象和类组合成更大的结构,以便更好地实现系统的整体结构,这些模式有助于解决对象之间的组合、接口和继承等关系,从而提高代码的可扩展性和可维护性。
适配器模式(Adapter Pattern)
适配器模式允许两个不兼容的接口协同工作。
生活例子:使用电源适配器将外国电器插头适配到国内插座,适配器模式可以使不同的类一起工作。
桥接模式(Bridge Pattern)
桥接模式分离了一个对象的抽象部分和具体部分,使它们可以独立地变化。
生活例子:汽车的品牌(如奥迪、宝马)和颜色(如红色、蓝色)是两个独立的变化维度。桥接模式允许你将品牌和颜色抽象出来,使你可以轻松地组合不同品牌和颜色,例如创建一个红色的奥迪或蓝色的宝马。
组合模式(Composition Pattern)
组合模式允许将对象组织成树状结构,使单个对象和组合对象都可以一致地对待。
生活例子:在操作系统中,文件夹可以包含文件和其他文件夹,创建了一个树状的组织结构,可以对文件夹和文件执行相似的操作,如复制、删除等。共享生命周期。
装饰器模式(Decorator Pattern)
装饰器模式允许动态地为对象添加新的功能,而无需修改其源代码。这就像在你的家中不断地添加新的家具或装饰来改善它的外观和功能。
生活例子:如果你有一台智能音响,可以通过添加新的语音助手技能(如天气查询或音乐播放)来扩展其功能,而不需要改变音响的核心设计。
外观模式(Facade Pattem)
外观模式提供了一个简化的接口,用于访问复杂系统中的一组接口。目的是降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
生活例子:在计算机操作系统中,使用一个图形用户界面(GUI),它提供了一个易于使用的外观,隐藏了底层操作系统的复杂性。你可以点击图标、打开应用程序,而无需了解操作系统的内部工作原理。
享元模式(Flyweight Pattern)
享元模式旨在最小化内存或计算开销,通过共享尽可能多的相似对象来实现。
生活例子:在图像编辑软件中,当多个图像元素共享相同的颜色或图案时,可以使用享元模式来减少内存占用。相同的颜色或图案可以被多个图像元素引用,而不是为每个元素都存储一份相同的数据。
代理模式(Proxy Pattern)
代理模式创建一个代理对象来控制对原始对象的访问。这样,当我们不方便或者不能直接访问真实对象时,可以通过代理对象来间接访问。
生活例子:在计算机网络中,代理服务器充当客户端和目标服务器之间的中间层,它去访问目标网站,并加载它的内容,然后再把这些加载过的内容传递到你的窗口上。这样就相当于你在浏览目标网站了。
结构性设计模式总结
速记口诀:外桥组员代配饰
设计模式——行为型
行为型模式:关注对象之间的通信、职责分配和算法的交互方式。它们有助于更好地管理对象之间的关系,是系统更具灵活性和可维护性。
责任链模式(Chain of Responsibility Pattern)
传递请求,多个对象以此尝试处理请求,直到有一个对象能够处理为止。
生活例子:在组织中,用的请假可能需要多个之间可能需要经过多个级别的审批,每个级别的主管可以决定是否批准请假。
命令模式(Command Pattern)
将一个命令封装为一个对象,从而可用不同的命令对客户进行参数化,将命令排队或记录请求日志,支持可撤销的操作。
生活例子:用电视遥控器上的按键打开电视、切换频道。
解释器模式(Interpreter Pattern)
用于处理语言解释和编译器等领域,它定义了一种语言的语法表示,并提供了解释器来解释这种语法。
选代器模式(lterator Pattern)
像遍历集合。它提供了一种顺序访问集合元素的方法,而无需暴露集合的内部结构。
生活例子:在编程中,你可以使用迭代器来遍历数组、列表或集合中的元素,而无需知道底层数据结构。
中介者模式(Mediator Pattern)
中介者对象邓庄一系列对象的交互,使得各对象不用显式互相引用,从而它减少对象之间的直接通信,降低了耦合度。
生活例子:在一个团队中,项目经理可以充当中介者,协调团队成员之间的合作和沟通,以确保项目的顺利进行。
备忘录模式(Memento Pattern)
在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,从而在以后将该对象恢复在原先保存的状态。
生活例子: 在文本编辑器中的撤销和重做功能。
观察者模式(Observer Pattern)
定义对象间的一对多的依赖关系。当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
生活例子:许多社交媒体平台使用观察者模式,当你关注某个用户或主题时,系统会将他们的更新信息发送给你。
状态模式(State Pattern)
允许一个对象在其内部状态改变时改变它的行为。
策略模式(Strategy Pattern)
定义一系列算法并封装,并且使他们质检科互相替换,从而让算法可以独立于使用它的用户二变化。
模板方法模式(Template Method Pattern)
定义一个算法的框架,允许子类实现其中的一些步骤。
访问者模式(Visitor Pattern)
访问不同类型的元素,定义不同的访问者来执行不同类型元素的操作。
行为型设计模式总结
速记口诀:观摩(模)对(迭)测,责令解放(访),戒(介)忘台(态)
例题
1. 下图所示为(1)模式,适用于(2)。
(1)A. 适配器 B. 责任链 C. 外观 D. 桥接
(2)
A. 有多个对象可以处理一个请求,在运行时刻自动确定由哪个对象处理。
B. 想使用一个已经存在的类,而其接口不符合要求。
C. 类的抽象和其实现之间不希望有一个固定的绑定关系
D. 需要为一个复杂子系统提供一个简单接口
解析(1)A,无适配器的转换的特点,排除。B,没有一个一个处理的表现 C,简化内部细节,提供接口能看出来。D,只有一个类,不符合桥接的条件。
(2)根据(1),选D。
2. 下图所示为()涉及模式,适用于()。
解析:看到builder--> 构建者builder模式。适用于创建复杂对象的算法应该独立于该对象的组成部分及其装配方式。
3. 想要使一个后端数据模型能够被多个前端用户界面链接,采用()模式最适合。
A. 装饰器 B. 享元 C. 观察者 D. 中介者
答案:D
4. 股票交易中,股票代理根据客户发出的股票操作指示进行股票的买卖操作,设计如下所示类图。该设计采用(1)模式将一个请求封装为一个对象,从而使得以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作,其中,(2)声明执行操作的接口。该模式属于(3)模式,该模式适用于(4)。
(1) A. 命令 B. 观察者 C. 状态 D. 中介者
(2) A. Operation B. SellOperation/Bay C. Broker D. Stock
(3) A. 结构类型 B. 创建类型 C. 行为型对象 D, 结构性对象
(4)A. 一个对象必须通知其他对象,而它又不能假定其他对象是谁。
B. 抽象出需要执行的动作以参数化某对象。
C. 一个对象的行为决定于其状态且必须在运行时刻根据状态改变行为。
D. 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
答案:(1)A (2) A. (3) C. (4) B