继承

什么是继承

继承(Inheritance)是面向对象编程的四大特性之一,它允许一个类(子类)获得另一个类(父类)的属性和方法。通过继承,子类可以重用父类的代码,并在此基础上添加新的功能或修改现有功能。

继承的基本概念

  • 父类(Base Class/Super Class):被继承的类,也叫基类
  • 子类(Derived Class/Child Class):继承父类的类,也叫派生类
  • extends关键字:用于实现继承
  • IS-A关系:子类是父类的一种特殊类型

继承的基本语法

<?php
// 定义父类
class Animal {
    // 属性
    public $name;
    public $age;

    // 构造函数
    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }

    // 方法
    public function eat() {
        echo "{$this->name} 正在吃东西\n";
    }

    public function sleep() {
        echo "{$this->name} 正在睡觉\n";
    }

    public function getInfo() {
        return "这是一只{$this->age}岁的{$this->name}";
    }
}

// 定义子类
class Dog extends Animal {
    // 子类特有的属性
    public $breed;

    // 子类构造函数
    public function __construct($name, $age, $breed) {
        // 调用父类构造函数
        parent::__construct($name, $age);
        $this->breed = $breed;
    }

    // 子类特有的方法
    public function bark() {
        echo "{$this->name} 正在汪汪叫\n";
    }

    // 重写父类方法
    public function getInfo() {
        return "这是一只{$this->breed},名字叫{$this->name},今年{$this->age}岁";
    }
}

// 使用示例
$dog = new Dog("旺财", 3, "金毛");

// 调用继承的方法
$dog->eat();        // 输出:旺财 正在吃东西
$dog->sleep();      // 输出:旺财 正在睡觉

// 调用子类特有的方法
$dog->bark();       // 输出:旺财 正在汪汪叫

// 调用重写后的方法
echo $dog->getInfo(); // 输出:这是一只金毛,名字叫旺财,今年3岁
?>

parent关键字

parent关键字用于访问父类的属性和方法。

调用父类构造函数

<?php
class Vehicle {
    protected $brand;
    protected $model;

    public function __construct($brand, $model) {
        $this->brand = $brand;
        $this->model = $model;
        echo "车辆构造函数被调用\n";
    }

    public function start() {
        echo "启动 {$this->brand} {$this->model}\n";
    }
}

class Car extends Vehicle {
    private $doors;

    public function __construct($brand, $model, $doors) {
        // 调用父类构造函数
        parent::__construct($brand, $model);
        $this->doors = $doors;
        echo "汽车构造函数被调用\n";
    }

    public function openDoors() {
        echo "打开{$this->doors}扇车门\n";
    }

    // 重写父类方法
    public function start() {
        // 调用父类方法
        parent::start();
        echo "汽车启动,准备出发!\n";
    }
}

$car = new Car("丰田", "卡罗拉", 4);
// 输出:
// 车辆构造函数被调用
// 汽车构造函数被调用

$car->start();
// 输出:
// 启动 丰田 卡罗拉
// 汽车启动,准备出发!
?>

访问控制与继承

访问修饰符在继承中的表现:

<?php
class Person {
    public $publicVar = "公开属性";      // 公开的,子类可以直接访问
    protected $protectedVar = "受保护属性";  // 受保护的,子类可以访问
    private $privateVar = "私有属性";    // 私有的,子类不能直接访问

    public function showVars() {
        echo "公开属性:{$this->publicVar}\n";
        echo "受保护属性:{$this->protectedVar}\n";
        echo "私有属性:{$this->privateVar}\n";
    }

    protected function protectedMethod() {
        echo "这是父类的受保护方法\n";
    }

    private function privateMethod() {
        echo "这是父类的私有方法\n";
    }
}

class Student extends Person {
    public function accessParentVars() {
        echo "访问父类的公开属性:{$this->publicVar}\n";        // ✅ 可以访问
        echo "访问父类的受保护属性:{$this->protectedVar}\n";    // ✅ 可以访问
        // echo "访问父类的私有属性:{$this->privateVar}\n";     // ❌ 不能访问
    }

    public function accessParentMethods() {
        $this->protectedMethod();  // ✅ 可以调用
        // $this->privateMethod();   // ❌ 不能调用
    }

    public function showAllVars() {
        // 可以通过父类的公开方法间接访问私有属性
        $this->showVars();
    }
}

$student = new Student();
$student->accessParentVars();
$student->accessParentMethods();
$student->showAllVars();

// 直接访问
echo $student->publicVar;      // ✅ 可以访问
// echo $student->protectedVar;  // ❌ 不能访问
// echo $student->privateVar;    // ❌ 不能访问
?>

方法重写(Method Overriding)

子类可以重新定义父类的方法,这称为方法重写。

基本重写

<?php
class Shape {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function draw() {
        echo "绘制一个{$this->name}\n";
    }

    public function getArea() {
        echo "计算{$this->name}的面积\n";
        return 0;
    }
}

class Circle extends Shape {
    private $radius;

    public function __construct($radius) {
        parent::__construct("圆形");
        $this->radius = $radius;
    }

    // 重写draw方法
    public function draw() {
        echo "绘制一个半径为{$this->radius}的圆形\n";
    }

    // 重写getArea方法
    public function getArea() {
        $area = pi() * $this->radius * $this->radius;
        echo "圆形面积:{$area}\n";
        return $area;
    }
}

class Rectangle extends Shape {
    private $width;
    private $height;

    public function __construct($width, $height) {
        parent::__construct("矩形");
        $this->width = $width;
        $this->height = $height;
    }

    // 重写draw方法
    public function draw() {
        echo "绘制一个{$this->width}x{$this->height}的矩形\n";
    }

    // 重写getArea方法
    public function getArea() {
        $area = $this->width * $this->height;
        echo "矩形面积:{$area}\n";
        return $area;
    }
}

$circle = new Circle(5);
$circle->draw();      // 输出:绘制一个半径为5的圆形
$circle->getArea();   // 输出:圆形面积:78.539816339745

$rectangle = new Rectangle(4, 6);
$rectangle->draw();   // 输出:绘制一个4x6的矩形
$rectangle->getArea(); // 输出:矩形面积:24
?>

使用final关键字防止重写

<?php
class Calculator {
    // final方法不能被重写
    final public function add($a, $b) {
        return $a + $b;
    }

    public function subtract($a, $b) {
        return $a - $b;
    }
}

class AdvancedCalculator extends Calculator {
    // ✅ 可以重写普通方法
    public function subtract($a, $b) {
        return abs($a - $b);  // 返回绝对值
    }

    // ❌ 不能重写final方法
    // public function add($a, $b) {
    //     return $a + $b + 1;
    // }

    public function multiply($a, $b) {
        return $a * $b;
    }
}

// final类不能被继承
final class MathUtils {
    public static function factorial($n) {
        if ($n <= 1) return 1;
        return $n * self::factorial($n - 1);
    }
}

// ❌ 不能继承final类
// class MyMath extends MathUtils {
// }
?>

多层继承

<?php
// 祖父类
class LivingBeing {
    protected $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function breathe() {
        echo "{$this->name} 正在呼吸\n";
    }
}

// 父类
class Animal extends LivingBeing {
    protected $species;

    public function __construct($name, $species) {
        parent::__construct($name);
        $this->species = $species;
    }

    public function move() {
        echo "{$this->name} 正在移动\n";
    }

    public function eat() {
        echo "{$this->name} 正在吃东西\n";
    }
}

// 子类
class Mammal extends Animal {
    protected $furColor;

    public function __construct($name, $species, $furColor) {
        parent::__construct($name, $species);
        $this->furColor = $furColor;
    }

    public function produceMilk() {
        echo "{$this->name} 正在产奶\n";
    }

    public function showInfo() {
        echo "种类:{$this->species}\n";
        echo "毛色:{$this->furColor}\n";
    }
}

// 孙子类
class Dog extends Mammal {
    private $breed;

    public function __construct($name, $breed, $furColor) {
        parent::__construct($name, "哺乳动物", $furColor);
        $this->breed = $breed;
    }

    public function bark() {
        echo "{$this->name} 正在汪汪叫\n";
    }

    public function showAllInfo() {
        echo "名字:{$this->name}\n";
        echo "品种:{$this->breed}\n";
        $this->showInfo();
    }
}

// 使用示例
$dog = new Dog("小白", "贵宾犬", "白色");

$dog->breathe();      // 祖父类的方法
$dog->move();         // 父类的方法
$dog->eat();          // 父类的方法
$dog->produceMilk();  // 父类的方法
$dog->bark();         // 子类自己的方法

$dog->showAllInfo();
// 输出:
// 名字:小白
// 品种:贵宾犬
// 种类:哺乳动物
// 毛色:白色
?>

继承的实际应用:员工管理系统

<?php
// 基础员工类
class Employee {
    protected $id;
    protected $name;
    protected $baseSalary;

    public function __construct($id, $name, $baseSalary) {
        $this->id = $id;
        $this->name = $name;
        $this->baseSalary = $baseSalary;
    }

    // 计算工资(基础方法)
    public function calculateSalary() {
        return $this->baseSalary;
    }

    // 获取员工信息
    public function getEmployeeInfo() {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'type' => '普通员工',
            'salary' => $this->calculateSalary()
        ];
    }

    // 工作方法
    public function work() {
        echo "{$this->name} 正在工作\n";
    }
}

// 经理类
class Manager extends Employee {
    private $bonus;
    private $department;

    public function __construct($id, $name, $baseSalary, $bonus, $department) {
        parent::__construct($id, $name, $baseSalary);
        $this->bonus = $bonus;
        $this->department = $department;
    }

    // 重写计算工资方法
    public function calculateSalary() {
        return $this->baseSalary + $this->bonus;
    }

    // 重写员工信息方法
    public function getEmployeeInfo() {
        $info = parent::getEmployeeInfo();
        $info['type'] = '经理';
        $info['department'] = $this->department;
        return $info;
    }

    // 经理特有方法
    public function manageTeam() {
        echo "{$this->name} 正在管理{$this->department}部门\n";
    }

    public function approveRequest($request) {
        echo "经理{$this->name}批准了请求:{$request}\n";
    }

    public function work() {
        parent::work();
        echo "{$this->name} 正在制定部门计划\n";
    }
}

// 销售员类
class Salesperson extends Employee {
    private $commissionRate;
    private $salesAmount;

    public function __construct($id, $name, $baseSalary, $commissionRate) {
        parent::__construct($id, $name, $baseSalary);
        $this->commissionRate = $commissionRate;
        $this->salesAmount = 0;
    }

    // 重写计算工资方法
    public function calculateSalary() {
        $commission = $this->salesAmount * $this->commissionRate;
        return $this->baseSalary + $commission;
    }

    // 重写员工信息方法
    public function getEmployeeInfo() {
        $info = parent::getEmployeeInfo();
        $info['type'] = '销售员';
        $info['sales_amount'] = $this->salesAmount;
        $info['commission'] = $this->salesAmount * $this->commissionRate;
        return $info;
    }

    // 销售特有方法
    public function makeSale($amount) {
        $this->salesAmount += $amount;
        echo "{$this->name} 完成了 {$amount} 元的销售额\n";
    }

    public function work() {
        parent::work();
        echo "{$this->name} 正在联系客户\n";
    }
}

// 技术员类
class Technician extends Employee {
    private $skillLevel;
    private $overtimeHours;

    public function __construct($id, $name, $baseSalary, $skillLevel) {
        parent::__construct($id, $name, $baseSalary);
        $this->skillLevel = $skillLevel;
        $this->overtimeHours = 0;
    }

    // 重写计算工资方法
    public function calculateSalary() {
        $overtimePay = $this->overtimeHours * 100;  // 每小时加班费100元
        $skillBonus = $this->skillLevel * 500;      // 技能等级奖励
        return $this->baseSalary + $overtimePay + $skillBonus;
    }

    // 重写员工信息方法
    public function getEmployeeInfo() {
        $info = parent::getEmployeeInfo();
        $info['type'] = '技术员';
        $info['skill_level'] = $this->skillLevel;
        $info['overtime_hours'] = $this->overtimeHours;
        return $info;
    }

    // 技术特有方法
    public function addOvertime($hours) {
        $this->overtimeHours += $hours;
        echo "{$this->name} 加班了 {$hours} 小时\n";
    }

    public function fixBug($bugId) {
        echo "技术员{$this->name}修复了Bug #{$bugId}\n";
    }

    public function work() {
        parent::work();
        echo "{$this->name} 正在编写代码\n";
    }
}

// 使用示例
echo "=== 员工管理系统 ===\n\n";

// 创建员工
$manager = new Manager(1, "张经理", 10000, 3000, "技术部");
$salesperson = new Salesperson(2, "李销售", 5000, 0.05);
$technician = new Technician(3, "王技术", 8000, 3);

// 员工工作
$manager->work();
$salesperson->work();
$technician->work();

echo "\n";

// 特殊操作
$manager->manageTeam();
$manager->approveRequest("购买新服务器");

$salesperson->makeSale(50000);
$salesperson->makeSale(30000);

$technician->fixBug(1001);
$technician->addOvertime(10);

echo "\n=== 员工薪资信息 ===\n";

// 显示所有员工信息
$employees = [$manager, $salesperson, $technician];

foreach ($employees as $employee) {
    $info = $employee->getEmployeeInfo();
    echo "ID: {$info['id']}\n";
    echo "姓名: {$info['name']}\n";
    echo "类型: {$info['type']}\n";
    echo "基础工资: {$employee->baseSalary}\n";
    echo "实际工资: {$info['salary']}\n";

    if (isset($info['department'])) {
        echo "部门: {$info['department']}\n";
    }

    if (isset($info['sales_amount'])) {
        echo "销售额: {$info['sales_amount']}\n";
        echo "提成: {$info['commission']}\n";
    }

    if (isset($info['skill_level'])) {
        echo "技能等级: {$info['skill_level']}\n";
    }

    echo "----------------\n";
}
?>

继承的优点和注意事项

继承的优点

  1. 代码重用:子类可以重用父类的代码
  2. 逻辑清晰:建立清晰的类层次结构
  3. 易于维护:修改父类可以影响所有子类
  4. 扩展性好:可以轻松添加新的子类

继承的注意事项

<?php
// 1. 单继承限制:PHP只支持单继承
class A {}
class B {}
class C extends A {}  // ✅ 正确
// class D extends A, B {}  // ❌ 错误,不能多继承

// 2. 构造函数链:记得调用父类构造函数
class ParentClass {
    protected $value;
    public function __construct($value) {
        $this->value = $value;
    }
}

class ChildClass extends ParentClass {
    public function __construct($value) {
        parent::__construct($value);  // 不要忘记调用
    }
}

// 3. 方法重写时保持兼容性
class Bird {
    public function fly() {
        return "鸟在飞";
    }
}

class Penguin extends Bird {
    // 重写方法时要考虑逻辑一致性
    public function fly() {
        return "企鹅不能飞,但会游泳";
    }
}

// 4. 避免过深的继承层次
// 太深的继承会难以理解和维护
// 一般建议继承层次不超过3-4层
?>

继承与组合

有时候,使用组合比继承更合适:

<?php
// 继承的方式
class CarWithInheritance extends Vehicle {
    private $engine;

    public function __construct() {
        $this->engine = new Engine();  // 组合
    }

    public function start() {
        $this->engine->start();  // 使用组合对象
        parent::start();
    }
}

// 组合的方式
class CarWithComposition {
    private $vehicle;
    private $engine;

    public function __construct() {
        $this->vehicle = new Vehicle();
        $this->engine = new Engine();
    }

    public function start() {
        $this->engine->start();
        return $this->vehicle->start();
    }
}

// 选择原则:
// - IS-A关系(是一个):使用继承
// - HAS-A关系(有一个):使用组合
?>

通过本节的学习,你应该掌握了PHP中继承的基本概念和用法。继承是面向对象编程的重要特性,合理使用继承可以让代码更加清晰、可维护和可扩展。