类的组合与继承

定义两个类,一个是person,一个是family;在family类中创建person类中的对象,把这个对象视为family类的一个属性,并调用它的方法处理问题,这种复用方式叫:“组合”。

 

类与类之间有一种父与子的关系,子类继承父类的属性和方法,称为继承。

在继承里,子类拥有父类的方法和属性,同时子类也可以有自己的方法和属性。

<?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($stduent,'peking');
print_r($tom);

/*
 family object
 {
    [people] => person object
        {
            [name] => Tom
            [gender] => male
        }

    [location] => peking
 }
 
*/
组合示例
<?php
class person{
    public $name = 'Tom';
    public $gender;
    static $momery = 10000;
    public function __construct(){
        echo '这里是父类';
    }
    public function say(){
        echo $this->name,"\tis",$this->gender."\r\n";
    }
}
class family extends person{
    public $name;
    public $gender;
    public $age;
    static $monery = 10000;
    public function __construct(){
        parent::__construct();    // 调用父类构造方法
        echo '这里是子类',PHP_EOL;
    }
    public function say(){
        parent::say();
        echo $this->name,"\title\t",$this->gender,",and is\t",$this->age,PHP_EOL;
    }
    public function cry(){
        echo parent::$momery,PHP_EOL;
        echo self::$momery,PHP_EOL;    //调用自身构造方法
    }
}
$poor = new family();
$poor->name = 'Lee';
$poor->gender = 'female';
$poor->age = 25;
$poor->say();
$poor->cry();
继承示例

在继承中,用 parent 指代父类,用 self 指代自身。使用“::”运算符(范围解析操作符)调用父类的方法。“::”操作符还用来作为类常量和静态方法的调用,不要把这两种应用混淆。

 

组合与继承都是提高代码可重用性的手段。在设计对象模型时,可以按照语义识别类之间的组合关系和继承关系。

 

通过一些总结,得出了继承是一种“是,像”的关系,而组合是一种“需要”的关系。

利用这条规律,就可以很简单地判断出父类与儿子应该是继承关系,而父亲与家庭应该是组合关系。还可以从另一个角度看,组合偏重整体与局部的关系,而继承偏重父与子的关系。

 

然而在编程中,继承与组合的取舍往往并不是这么直接明了,很难说出二者是“像”的关系还是“需要”的关系,甚至把它拿到现实世界中建模,还是无法决定应该是继承还是组合。

那应该怎么办呢?有标准吗?有的,这个标准就是“低耦合”。

 

耦合是一个软件结构内不同模块之间互连程度的度量,也就是不同模块之间的依赖关系。

 

低耦合指模块与模块之间,尽可能地使模块间独立存在;模块与模块之间的接口尽量少而简单。

 

解耦是要解除模块与模块之间的依赖。

 

按照这个思想,继承与组合二者语义上难以区分,在二者均可使用的情况下,更倾向于使用组合。为什么呢?继承存在什么问题吗?

  1.继承破坏封装性

    比如,定义鸟类为父类,具有羽毛属性和飞翔方法,其子类天鹅、鸭子、鸵鸟等继承鸟这个类。显然,鸭子和鸵鸟不需要飞翔这个方法,但作为子类,它们却可以无区别的使用飞翔这个方法,这显然破坏了类的封装性。而组合,从语义上来说,要优于继承。

  2.继承是紧耦合的

    继承使得子类和父类捆绑在一起。组合仅通过唯一接口和外部进行通信,耦合度低于继承。

  3.继承扩展复杂

    随着继承层数的增加和子类的增加,将涉及大量方法重写。使用组合,可以根据类型约束,实现动态组合,减少代码。

  4.不恰当地使用继承可能违反现实世界中的逻辑

    比如人类作为父类,雇员、经理、学生作为子类,可能存在这样的问题,经理一定是雇员,学生也可能是雇员,而是用继承的话一个人就无法拥有多个角色。这种问题归结起来就是“角色”和“权限”问题。在权限系统中可能存在这样的问题,经理权利和职位大于主管,但出于分工和安全的考虑,经理没有权利直接操作主管所负责的资源,技术部经理也没权限直接命令市场部主管。这就要求角色和权限系统的设计要更灵活。不恰当的继承可能导致逻辑混乱,而实用组合就可以较好地解决这个问题。

 

当然,组合并非没有缺点。在创建组合对象时,组合需要一一创建局部对象,这一定程度上增加了一些代码,而继承则不需要这一步,因为子类自动有了父类的方法。

 

继承最大的优点就是扩展简单,但是其缺点大于优点,所以在设计时,需要慎重考虑。

  • 精心设计专门用于被继承的类,继承树的抽象层应该比较稳定,一般不要多于三层。
  • 对于不是专门用于被继承的类,禁止其被继承,也就是使用 final 修饰符。使用 final 修饰符既可防止重要方法被非法覆写,又能给编辑器寻找优化的机会。
  • 优先考虑用组合关系提高代码的可重用性。
  • 子类是一种特殊的类型,而不只是父类的一个角色。
  • 子类扩展,而不是覆盖或者使父类的功能失效。
  • 底层代码多用组合,顶层/业务层代码多用继承。底层用组合可以提高效率,避免对象臃肿。顶层代码用继承可以提高灵活性,让业务使用更方便。

 

继承并非一无是处,而组合也不是完美无缺的。如果既要组合的灵活,又要继承的代码简洁,能做到吗?

这是可以做到的,譬如多重继承,就具有这个特性。多重继承里一个类可以同时继承多个父类,组合两个父类的功能。

可是多重继承太复杂了,那么还有其他方式比较好地解决这个问题吗?PHP5.4引入的新的语法结构Traits,可以方便我们实现对象的扩展,是除 extend、implements 外的另外一种扩展对象方式。

Traits 既可以使单继承模式的语言获得多重继承的灵活,又可以避免多重继承带来的问题。

posted @ 2019-05-12 21:23  牛奔  阅读(663)  评论(0编辑  收藏  举报