1.概念解析
- 继承:"继承"是面向对象编程中的一个概念。
面向对象编程的单一职责原理(SPR-Single Responsibility Principle)规定对象只能有一个职责。就一个类而言,应该仅有一个引起它变化的原因。 为什么要把不同的职责分配到不同的类中呢?因为每一个职责都是变化的一个轴线,当需求变化时,该变化会反映为类的职责的变化。如果一个类承担了多于一个的职责,那么就意味着引起它的变化的原因会有多个。如果一个类承担的职责过多,那么就等同于把这些职责耦合在了一起。一个职责的变化可能会抑制到该类完成其他职责的能力,这样的耦合会导致脆弱的设计。当变化发生时,设计会受到意想不到的破坏。
这个原则的核心含意是:一个类应该有且仅有一个职责。关于职责的含意,面向对象大师Robert.C.Martin有一个著名的定义:所谓一个类的职责是指引起该类变化的原因,如果一个类具有一个以上的职责,那么就会有多个不同的原因引起该类变化,其实就是耦合了多个互不相关的职责,就会降低这个类的内聚性。
例如我们现在有两个类,D和C。那么类D的对象可以使用仅对类C对象有效的方法或属性,它使得这些方法和属性就像是由D定义的。这时,C是D的父类,D是C的子类。由于在继承过程中,父类的内部细节对子类完全可见,因此通过继承的代码复用被称为“白盒式代码复用”(white-box reuse)。
因此,当你通过从另一个对象继承的方式来创建一个新的对象时,目标就应该是使得新的对象有一个相对于原始对象更加特定的版本。
- 对象组合(组合对象):将多个元素作为一个对象来处理,需要将它们组合。
一个对象可以把对象作为自己的成员变量,如果用这样的类创建对象,那么该对象中就会有其它对象,也就是说该对象将其他对象作为自己的组成部分(这就是人们常说的Has-A),或者说该对象是由几个对象组合而成。
对象组合就是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能,而由于对象之间各自内部细节不对外可见,所以这种方式的代码复用被称为“黑盒式代码复用”(black-box reuse)。对象组合要求被组合的对象具有良好定义的接口。
举例:
<?php class person{ public $name; public $gender; public function say(){ echo $this->name," \tis ",$this->gender,"\r\n"; } } class family{ public $people; public $location; public function construct($p,$loc){ $this->people=$p; $this->location=$loc; } } $student=new person(); $student->name=‘Tom‘; $student->gender=‘male‘; $student->say(); $tom=new family($student,‘peking‘);
以上代码中,定义了两个类,一个是person,一个是family;在family类中创建person类中的对象,把这个对象视为family类的一个属性,并调用它的方法处理问题,这种复用方式就叫“组合”。
2、继承与组合区别
类继承是在编译时刻静态定义的,且可直接使用,因为程序设计语言直接支持类继承。类继承可以较方便地改变被复用的实现。当一个子类重定义一些而不是全部操作时,它也能影响它所继承的操作,只要在这些操作中调用了被重定义的操作。
但是类继承也有一些不足之处。首先,因为继承在编译时刻就定义了,所以无法在运行时刻改变从父类继承的实现。更糟的是,父类通常至少定义了部分子类的具体表示。因为继承对子类揭示了其父类的实现细节,所以继承常被认为“破坏了封装性” 。子类中的实现与它的父类有如此紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。当你需要复用子类时,实现上的依赖性就会产生一些问题。如果继承下来的实现不适合解决新的问题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性。一个可用的解决方法就是只继承抽象类,因为抽象类通常提供较少的实现。
对象组合是通过获得对其他对象的引用而在运行时刻动态定义的。组合要求对象遵守彼此的接口约定,进而要求更仔细地定义接口,而这些接口并不妨碍你将一个对象和其他对象一起使用。这还会产生良好的结果:因为对象只能通过接口访问,所以我们并不破坏封装性;只要类型一致,运行时刻还可以用一个对象来替代另一个对象;更进一步,因为对象的实现是基于接口写的,所以实现上存在较少的依赖关系。
对象组合对系统设计还有另一个作用,即优先使用对象组合有助于你保持每个类被封装,并被集中在单个任务上。这样类和类继承层次会保持较小规模,并且不太可能增长为不可控制的庞然大物。另一方面,基于对象组合的设计会有更多的对象 (而有较少的类),且系统的行为将依赖于对象间的关系而不是被定义在某个类中。
这导出了我们的面向对象设计的第二个原则:优先使用对象组合,而不是类继承。
推荐阅读》》
面向对象的编程概念:组合(Composition)和聚合(aggregation)