对“代码设计”的理解涉及系统的定义:确定系统的需求、作用域及目标。从底层上看,设计是定义系统组成并组织各组件间关系的过程。本章从另一个角度考虑:类和对象的定义和配置。
面向对象的系统由一系列的类组成。决定系统中这些类的角色是非常重要的,而类由方法组成,所以在定义类时,必须决定哪些方法应该放在一起。另外,类与类之间常常通过继承关系联系起来以便遵循公用的接口。因此,这些接口或类型应该是设计系统时首先要考虑的。
面向对象和过程式编程一个核心的区别是如何分配职责。过程式编程表现为一系列命令和方法的连续调用。控制代码根据不同的条件执行不同的职责。这种自顶向下的控制方式导致了重复和相互依赖遍布于整个项目。面向对象编程则将职责从客户端代码移专门的对象中,尽量减少互相依赖。
我们如何才能在代码中达到一个平衡?通常,首先考虑哪些代码应该存在于系统中。
我们应该如何定义类呢?最好的办法是让一个类只有一个主要的职责,并且任务要尽可能独立。你可以把类的职责用多个词来形容,最好不超过25个词,不要用到“且”或“或”。如果句子太长或者有复杂的子句,就应该考虑用你所描述的一部分任务来定义新类。
多态是指在一个公用接口后面维护多个实现。如果代码中存在大量的条件语句,就说明需要使用多态。要注意多态并没有消除条件语句,但多态可以把条件代码集中到一个地方。PHP强制接口由抽象类定义, 这非常有用,因为我们可以确定子类将会实现抽象父类中定义的所有方法,包括类类型提示和方法的访问控制。客户端代码因此可以使用一个公共父类的任意子类而不需要修改代码(只要客户端代码仅依赖与父类中定义的功能)。
这个规则唯一的缺憾是:无法强制规定类方法返回的数据类型,这意味着不同子类的方法可能返回不同类型的对象或基本数据类型,这会损害类型的互换性。你应该尝试使返回值保持一致。你可以在项目中靠人为的约定来使多个方法保持一致。你可以在源代码中使用注释来标明方法的返回值类型。
简单地说,封装就是对客户端代码隐藏数据和功能。
要实现封装,最简单的方法是将属性定义为private或protected。通过对客户端代码隐藏属性,我们创建了一个接口并防止在偶然情况下污染对象中的数据。
多态是另外一种封装。通过把不同的实现放在公共接口之后,我们对客户端代码隐藏了功能的实现。
在设计阶段,让大脑空白一段时间会给你带来意想不到的好处。请清空脑海中这些和细节相关的念头。你可以只考虑系统中的关键参与者:项目需要的对象类型和这些对象的接口。除此之外,请忽略具体的实现细节,而要让代码中的结构和关系来引导你,你会发现在一个定义良好的接口之后加入你的实现代码是很容易的。接着你可以灵活地选中、改进或货站一个可能需要的实现,而不会影响到外界的系统。
为了强调接口,我们按抽象基类而不是具体的子类来思考。用这种方法,我们一开始就在系统中使用了多态和封装。这个结构也具有类切换的能力。
“为接口而不是实现而编程”。
本节挑选出4个说明代码需要检查的“路标”。
条件语句
如果您发现在一个类中频繁地进行特定条件的判断,特别是当你发现这些条件判断在多个方法中出现时,就说明这个类需要拆分成两个或更多。拆分出来的几个类应该有一个共享的抽象基类,这时你需要知道如何传递正确的子类给客户端代码。
UML
UML是Unified Modeling Language(统一建模语言)的缩写,注意说这个词的时候要使用定冠词the。