设计模式
面向对象程序设计简介
面向对象程序设计基础
面向对象程序设计(Object-Oriented Programming, 缩写为OOP)是一种范式,其基本理念是将数据块及与数据相关的行为封装成为特殊的、名为对象的实体,同时对象实体的生成工作则是基于程序员给出的一系列“蓝图”,这些“蓝图”就是类
对象和类
假如你有一只名为卡卡的猫。卡卡是一个对象,也是 猫 Cat 这个类的一个实例。每只猫都有许多基本属性: 名字name 、 性别 sex 、 年龄 age 、 体重 weight 、毛色 color 和最爱的食物等。这些都是该类的成员变量。
所有猫都有相似的行为: 它们会呼吸 breathe 、 进食eat 、 奔跑 run 、 睡觉 sleep 和 喵喵叫 meow 。这些都是该类的方法。 成员变量和方法可以统称为类的成员。存储在对象成员变量中的数据通常被称为状态,对象中的所有方法则定义了其行为。
1 | class Cat |
类层次结构
相信大家都已经了解单独的一个类的结构了,但一个实际的程序显然会包含不止一个类。一些类可能会组织起来形成类层次结构。
面向对象程序设计基础
面向对象程序设计的四个基本概念使其区别于其他程序设计范式
抽象
当使用面向对象程序设计的理念开发一款程序时,你会将大部分时间用于根据真实世界对象来设计程序中的对象。但是,程序中的对象并不需要能够百分之百准确地反映其原型(极少情况下才需要做到这一点)。实际上,你的对象只需模拟真实对象的特定属性和行为即可,其他内容可以忽略。
抽象是一种反映真实世界对象或现象中特定内容的模型,它能高精度地反映所有与特定内容相关的详细信息,同时忽略其他内容。
封装
封装是指一个对象对其他对象隐藏其部分状态和行为,而仅向程序其他部分暴露有限的接口的能力。
封装某个内容意味着使用关键字 private 私有 来对其进行修饰,这样仅有其所在类中的方法才能访问这些内容。还有一种限制程度较小的关键字 protected 保护 ,其所修饰的对象仅允许父类访问其类中的成员。
绝大部分编程语言的接口和抽象类(或方法)都基于抽象和封装的概念。在现代面向对象的编程语言中,接口机制(通常使用 interface 或 protocol 关键字来声明)允许你定义对象之间的交互协议。这也是接口仅关心对象行为,以及你不能在接口中声明成员变量的原因之一。
多态
多态是指程序能够检测对象所属的实际类,并在当前上下文不知道其真实类型的情况下调用其实现的能力。
你还可将多态看作是一个对象“假扮”为其他东西(通常是其扩展的类或实现的接口)的能力。在我们的示例中,袋中的狗和猫就相当于是假扮成了一般的动物。
继承
继承是指在根据已有类创建新类的能力。继承最主要的好处是代码复用。如果你想要创建的类与已有的类差异不大,那也没必要重复编写相同的代码。你只需扩展已有的类并将额外功能放入生成的子类(它会继承父类的成员变量和方法)中即可。
使用继承后,子类将拥有与其父类相同的接口。如果父类中声明了某个方法,那么你将无法在子类中隐藏该方法。你还必须实现所有的抽象方法,即使它们对于你的子类而言没有意义。
在绝大多数编程语言中,子类仅能对一个父类进行扩展。另一方面,任何类都可以同时实现多个接口。但是正如我之前提到的那样,如果父类实现了某个接口,那么其所有子类都必须实现该接口。
对象之间的关系
依赖
依赖是类之间最基础的、也是最微弱的关系类型。如果修改一个类的定义可能会造成另一个类的变化,那么这两个类之间就存在依赖关系。当你在代码中使用具体类的名称时,通常意味着存在依赖关系。例如在指定方法签名类型时,或是通过调用构造函数对对象进行初始化时等。通过让代码依赖接口或抽象类(而不是具体类),你可以降低其依赖程度。通常情况下,UML 图不会展示所有依赖——它们在真实代码中的数量太多了。为了不让依赖关系破坏 UML 图,你必须对其进行精心选择,仅展示那些对于沟通你的想法来说重要的依赖关系。
关联
关联是一个对象使用另一对象或与另一对象进行交互的关系。 在 UML 图中,关联关系用起始于一个对象并指向其所使用的对象的简单箭头来表示。顺带一提,双向关联也是完全正常的,这种情况就用双向箭头来表示。关联可视为一种特殊类型的依赖,即一个对象总是拥有访问与其交互的对象的权限,而简单的依赖关系并不会在对象间建立永久性的联系。一般来说,你可以使用关联关系来表示类似于类成员变量的东西。这个关系将一直存在,因此你总能通过“订单”来获取其“顾客”。但是它并非一定是成员变量。如果你根据接口来创建类,它也可以表示为一个可返回“订单”的“顾客”的方法。
• 依赖 对类 B 进行修改会影响到类 A 。
• 关联 对象 A 知道对象 B。类 A 依赖于类 B。
• 聚合 对象 A 知道对象 B 且由 B 构成。类 A 依赖于类 B。
• 组合 对象 A 知道对象 B、由 B 构成而且管理着 B 的生命周期。类 A 依赖于类 B。
• 实现 类 A 定义的方法由接口 B 声明。对象 A 可被视为对象B。类 A 依赖于类 B。
• 继承 类 A 继承类 B 的接口和实现, 但是可以对其进行扩展。对象 A 可被视为对象 B。类 A 依赖于类 B。
设计模式
创建型模式
这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性
- 工厂方法
Factory Method
在父类中提供一个创建对象的接口以允许子类决定实例化对象的类型 - 抽象工厂
Abstract Factory
让你能创建一系列相关的对象, 而无需指定其具体类 - 生成器
Builder
使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象 - 原型
Prototype
让你能够复制已有对象, 而又无需使代码依赖它们所属的类 - 单例
Singleton
让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点
结构型模式
这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效
- 适配器
Adapter
让接口不兼容的对象能够相互合作 - 桥接
Bridge
可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构, 从而能在开发时分别使用 - 组合
Composite
你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们 - 装饰
Decorator
允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为 - 外观
Facade
能为程序库、 框架或其他复杂类提供一个简单的接口 - 享元
Flyweight
摒弃了在每个对象中保存所有数据的方式, 通过共享多个对象所共有的相同状态, 让你能在有限的内存容量中载入更多对象 - 代理
Proxy
让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理
行为模式
这类模式负责对象间的高效沟通和职责委派
- 责任链
Chain of Responsibility
允许你将请求沿着处理者链进行发送。 收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者 - 命令
Command
它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其放入队列中, 且能实现可撤销操作 - 迭代器
Iterator
让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素 - 中介者
Mediator
能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作 - 备忘录
Memento
允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态 - 观察者
Observer
允许你定义一种订阅机制, 可在对象事件发生时通知多个 “观察” 该对象的其他对象 - 状态
State
让你能在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样 - 策略
Strategy
能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换 - 模板方法
Template Method
在超类中定义一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤 - 访问者
Visitor
将算法与其所作用的对象隔离开来