抽象类与接口

抽象类(Abstract Class)

什么是抽象类

抽象类是一种不能被实例化的类,它专门用作父类来定义子类的通用结构。抽象类可以包含抽象方法(没有具体实现的方法)和具体方法(有实现的方法)。

抽象类的特点

  • 不能实例化:不能使用 new 关键字创建抽象类的对象
  • 包含抽象方法:使用 abstract 关键字定义的方法,没有方法体
  • 包含具体方法:可以有完整实现的方法
  • 必须被继承:子类必须实现所有抽象方法
  • 可以有属性:可以定义各种访问级别的属性
  • 可以有构造函数:子类可以调用父类的构造函数

定义抽象类

<?php
// 定义抽象类
abstract class Animal {
    // 属性
    protected $name;
    protected $age;
    protected $species;

    // 构造函数
    public function __construct($name, $age, $species) {
        $this->name = $name;
        $this->age = $age;
        $this->species = $species;
        echo "创建了一个{$this->species}:{$this->name}\n";
    }

    // 具体方法(有实现)
    public function eat() {
        return "{$this->name} 正在吃东西";
    }

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

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

    // 抽象方法(没有实现,必须在子类中实现)
    abstract public function makeSound();
    abstract public function move();

    // 可以有静态方法
    public static function getKingdom() {
        return "动物界";
    }
}
?>

继承抽象类

<?php
// 继承抽象类并实现所有抽象方法
class Dog extends Animal {
    private $breed;

    public function __construct($name, $age, $breed) {
        parent::__construct($name, $age, "狗");
        $this->breed = $breed;
    }

    // 实现抽象方法
    public function makeSound() {
        return "{$this->name} 汪汪叫";
    }

    public function move() {
        return "{$this->name} 正在奔跑";
    }

    // 子类特有方法
    public function fetch() {
        return "{$this->name} 正在捡球";
    }

    public function wagTail() {
        return "{$this->name} 正在摇尾巴";
    }
}

class Cat extends Animal {
    private $color;

    public function __construct($name, $age, $color) {
        parent::__construct($name, $age, "猫");
        $this->color = $color;
    }

    // 实现抽象方法
    public function makeSound() {
        return "{$this->name} 喵喵叫";
    }

    public function move() {
        return "{$this->name} 正在优雅地走路";
    }

    // 子类特有方法
    public function climb() {
        return "{$this->name} 正在爬树";
    }

    public function purr() {
        return "{$this->name} 发出呼噜声";
    }
}

// 使用示例
echo "=== 动物演示 ===\n";

// 创建子类对象(不能直接创建抽象类对象)
$dog = new Dog("旺财", 3, "金毛");
$cat = new Cat("咪咪", 2, "橘色");

// 调用方法
echo $dog->eat() . "\n";        // 具体方法
echo $dog->makeSound() . "\n";  // 实现的抽象方法
echo $dog->move() . "\n";       // 实现的抽象方法
echo $dog->fetch() . "\n";      // 子类特有方法

echo "\n";

echo $cat->eat() . "\n";
echo $cat->makeSound() . "\n";
echo $cat->move() . "\n";
echo $cat->climb() . "\n";

echo "\n";
echo "动物界:" . Animal::getKingdom() . "\n";

// ❌ 错误:不能实例化抽象类
// $animal = new Animal("动物", 5, "未知");  // Fatal error
?>

接口(Interface)

什么是接口

接口是一种完全抽象的结构,它定义了一组方法契约但不提供实现。接口规定了实现类必须提供哪些方法,但具体如何实现由类自己决定。

接口的特点

  • 完全抽象:所有方法都是抽象的(不需要 abstract 关键字)
  • 多重实现:一个类可以实现多个接口
  • 方法规范:只定义方法签名,不包含方法体
  • 常量支持:可以定义常量(自动为 public static final)
  • 不能有属性:接口不能定义实例属性
  • 不能有构造函数:接口不能定义构造函数

定义接口

<?php
// 定义接口
interface Drawable {
    // 接口中的方法都是抽象的,不需要 abstract 关键字
    public function draw();
    public function getArea();
    public function getPerimeter();
}

// 可以定义常量
interface ShapeConstants {
    const PI = 3.14159;
    const GOLDEN_RATIO = 1.618;
}

// 另一个接口
interface Movable {
    public function move($x, $y);
    public function getPosition();
}

// 可以继承接口
interface Resizable extends Drawable {
    public function resize($scale);
    public function scale($factorX, $factorY);
}
?>

实现接口

<?php
// 实现单个接口
class Circle implements Drawable {
    private $radius;
    private $x = 0;
    private $y = 0;

    public function __construct($radius, $x = 0, $y = 0) {
        $this->radius = $radius;
        $this->x = $x;
        $this->y = $y;
    }

    // 必须实现接口中的所有方法
    public function draw() {
        echo "绘制一个半径为{$this->radius}的圆形,位置({$this->x}, {$this->y})\n";
    }

    public function getArea() {
        return self::PI * $this->radius * $this->radius;  // 使用常量
    }

    public function getPerimeter() {
        return 2 * self::PI * $this->radius;
    }
}

// 实现多个接口
class Rectangle implements Drawable, Movable {
    private $width;
    private $height;
    private $x = 0;
    private $y = 0;

    public function __construct($width, $height, $x = 0, $y = 0) {
        $this->width = $width;
        $this->height = $height;
        $this->x = $x;
        $this->y = $y;
    }

    // 实现 Drawable 接口
    public function draw() {
        echo "绘制一个{$this->width}x{$this->height}的矩形,位置({$this->x}, {$this->y})\n";
    }

    public function getArea() {
        return $this->width * $this->height;
    }

    public function getPerimeter() {
        return 2 * ($this->width + $this->height);
    }

    // 实现 Movable 接口
    public function move($x, $y) {
        $this->x = $x;
        $this->y = $y;
        echo "矩形移动到位置({$x}, {$y})\n";
    }

    public function getPosition() {
        return "当前位置:({$this->x}, {$this->y})";
    }
}

// 实现继承接口
class Triangle implements Resizable {
    private $side1;
    private $side2;
    private $side3;

    public function __construct($side1, $side2, $side3) {
        $this->side1 = $side1;
        $this->side2 = $side2;
        $this->side3 = $side3;
    }

    // 实现 Drawable 接口方法
    public function draw() {
        echo "绘制一个边长为{$this->side1}, {$this->side2}, {$this->side3}的三角形\n";
    }

    public function getArea() {
        // 使用海伦公式
        $s = ($this->side1 + $this->side2 + $this->side3) / 2;
        return sqrt($s * ($s - $this->side1) * ($s - $this->side2) * ($s - $this->side3));
    }

    public function getPerimeter() {
        return $this->side1 + $this->side2 + $this->side3;
    }

    // 实现 Resizable 接口方法
    public function resize($scale) {
        $this->side1 *= $scale;
        $this->side2 *= $scale;
        $this->side3 *= $scale;
        echo "三角形缩放{$scale}倍\n";
    }

    public function scale($factorX, $factorY) {
        // 对于三角形,使用平均缩放因子
        $avgFactor = ($factorX + $factorY) / 2;
        $this->resize($avgFactor);
    }
}

// 使用示例
echo "=== 图形演示 ===\n";

$circle = new Circle(5, 10, 20);
$circle->draw();
echo "面积:" . round($circle->getArea(), 2) . "\n";
echo "周长:" . round($circle->getPerimeter(), 2) . "\n";

echo "\n";

$rectangle = new Rectangle(4, 6);
$rectangle->draw();
echo $rectangle->getPosition() . "\n";
$rectangle->move(100, 100);
echo $rectangle->getPosition() . "\n";

echo "\n";

$triangle = new Triangle(3, 4, 5);
$triangle->draw();
echo "面积:" . round($triangle->getArea(), 2) . "\n";
$triangle->resize(2);
echo "缩放后面积:" . round($triangle->getArea(), 2) . "\n";
?>

抽象类与接口的区别

主要区别对比

特性抽象类接口
实例化不能实例化不能实例化
方法实现可以有具体方法只有抽象方法
属性可以有各种属性只能有常量
构造函数可以有不能有
继承单继承(extends)多实现(implements)
访问修饰符可以用各种修饰符只能是 public
用途定义通用模板定义行为契约

代码示例对比

<?php
// 抽象类示例
abstract class Vehicle {
    protected $brand;
    protected $model;

    // 可以有属性
    public function __construct($brand, $model) {
        $this->brand = $brand;
        $this->model = $model;
    }

    // 可以有具体方法
    public function start() {
        echo "启动 {$this->brand} {$this->model}\n";
    }

    public function stop() {
        echo "停止 {$this->brand} {$this->model}\n";
    }

    // 抽象方法
    abstract public function accelerate();
    abstract public function brake();
}

// 接口示例
interface ElectricVehicle {
    public function charge();
    public function getBatteryLevel();
}

interface AutonomousVehicle {
    public function enableAutopilot();
    public function setDestination($destination);
}

// 继承抽象类并实现多个接口
class Tesla extends Vehicle implements ElectricVehicle, AutonomousVehicle {
    private $batteryLevel = 100;

    public function __construct($model) {
        parent::__construct("Tesla", $model);
    }

    // 实现抽象类的方法
    public function accelerate() {
        echo "Tesla {$this->model} 加速中(静音)\n";
    }

    public function brake() {
        echo "Tesla {$this->model} 刹车中(能量回收)\n";
    }

    // 实现 ElectricVehicle 接口
    public function charge() {
        echo "Tesla {$this->model} 正在充电\n";
        $this->batteryLevel = 100;
    }

    public function getBatteryLevel() {
        return $this->batteryLevel;
    }

    // 实现 AutonomousVehicle 接口
    public function enableAutopilot() {
        echo "Tesla {$this->model} 自动驾驶已启用\n";
    }

    public function setDestination($destination) {
        echo "设置目的地:{$destination}\n";
    }

    // 特有方法
    public function summon() {
        echo "Tesla {$this->model} 召唤功能启动\n";
    }
}

// 使用示例
echo "=== 电动汽车演示 ===\n";

$tesla = new Tesla("Model S");

// 继承的方法
$tesla->start();
$tesla->accelerate();
$tesla->brake();
$tesla->stop();

echo "\n";

// 接口方法
echo "电池电量:" . $tesla->getBatteryLevel() . "%\n";
$tesla->charge();
echo "充电后电量:" . $tesla->getBatteryLevel() . "%\n";

$tesla->enableAutopilot();
$tesla->setDestination("北京天安门");

// 特有方法
$tesla->summon();
?>

实际应用:支付系统设计

使用抽象类定义基础结构

<?php
// 抽象支付基类
abstract class PaymentMethod {
    protected $name;
    protected $transactionId;
    protected $status = 'pending';

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

    // 具体方法:通用功能
    protected function generateTransactionId() {
        return uniqid('TXN_', true);
    }

    public function getTransactionId() {
        return $this->transactionId;
    }

    public function getStatus() {
        return $this->status;
    }

    protected function setStatus($status) {
        $this->status = $status;
    }

    // 模板方法:定义支付流程
    public function processPayment($amount) {
        $this->validateAmount($amount);
        $this->beforePayment($amount);
        $result = $this->doPayment($amount);
        $this->afterPayment($result);
        return $result;
    }

    protected function validateAmount($amount) {
        if ($amount <= 0) {
            throw new Exception("支付金额必须大于0");
        }
    }

    // 钩子方法:子类可以重写
    protected function beforePayment($amount) {
        echo "准备使用{$this->name}支付 ¥{$amount}\n";
    }

    protected function afterPayment($result) {
        if ($result) {
            $this->setStatus('completed');
            echo "{$this->name}支付成功\n";
        } else {
            $this->setStatus('failed');
            echo "{$this->name}支付失败\n";
        }
    }

    // 抽象方法:子类必须实现
    abstract protected function doPayment($amount);
    abstract public function refund($amount);
}

// 接口:定义额外功能
interface Refundable {
    public function canRefund();
    public function getRefundPolicy();
}

interface SupportsInstallment {
    public function getInstallmentOptions($amount);
    public function payWithInstallment($amount, $months);
}

// 具体实现
class CreditCardPayment extends PaymentMethod implements Refundable, SupportsInstallment {
    private $cardNumber;
    private $limit;

    public function __construct($cardNumber, $limit) {
        parent::__construct("信用卡");
        $this->cardNumber = $cardNumber;
        $this->limit = $limit;
    }

    protected function doPayment($amount) {
        if ($amount > $this->limit) {
            echo "超出信用卡额度\n";
            return false;
        }
        echo "信用卡 {$this->cardNumber} 扣款 ¥{$amount}\n";
        return true;
    }

    public function refund($amount) {
        if ($this->canRefund()) {
            echo "退款 ¥{$amount} 到信用卡 {$this->cardNumber}\n";
            $this->setStatus('refunded');
            return true;
        }
        return false;
    }

    // 实现 Refundable 接口
    public function canRefund() {
        return $this->getStatus() === 'completed';
    }

    public function getRefundPolicy() {
        return "180天内可全额退款";
    }

    // 实现 SupportsInstallment 接口
    public function getInstallmentOptions($amount) {
        return [
            3 => $amount * 1.02,   // 3期,2%手续费
            6 => $amount * 1.04,   // 6期,4%手续费
            12 => $amount * 1.08   // 12期,8%手续费
        ];
    }

    public function payWithInstallment($amount, $months) {
        $options = $this->getInstallmentOptions($amount);
        if (isset($options[$months])) {
            $totalAmount = $options[$months];
            $monthly = $totalAmount / $months;
            echo "信用卡分期{$months}期,每期¥" . round($monthly, 2) . "\n";
            return $this->processPayment($totalAmount);
        }
        return false;
    }
}

// 支付宝支付
class AlipayPayment extends PaymentMethod implements Refundable {
    private $accountId;

    public function __construct($accountId) {
        parent::__construct("支付宝");
        $this->accountId = $accountId;
    }

    protected function doPayment($amount) {
        echo "支付宝账号 {$this->accountId} 支付 ¥{$amount}\n";
        return true;
    }

    public function refund($amount) {
        if ($this->canRefund()) {
            echo "退款 ¥{$amount} 到支付宝 {$this->accountId}\n";
            $this->setStatus('refunded');
            return true;
        }
        return false;
    }

    public function canRefund() {
        return $this->getStatus() === 'completed';
    }

    public function getRefundPolicy() {
        return "支持即时退款";
    }
}

// 支付管理器
class PaymentManager {
    private $paymentMethods = [];

    public function addPaymentMethod(PaymentMethod $method) {
        $this->paymentMethods[] = $method;
    }

    public function processPayment($methodIndex, $amount) {
        if (isset($this->paymentMethods[$methodIndex])) {
            return $this->paymentMethods[$methodIndex]->processPayment($amount);
        }
        throw new Exception("支付方式不存在");
    }

    public function processRefund($methodIndex, $amount) {
        if (isset($this->paymentMethods[$methodIndex])) {
            $method = $this->paymentMethods[$methodIndex];
            if ($method instanceof Refundable) {
                return $method->refund($amount);
            }
            throw new Exception("该支付方式不支持退款");
        }
        throw new Exception("支付方式不存在");
    }

    public function showInstallmentOptions($methodIndex, $amount) {
        if (isset($this->paymentMethods[$methodIndex])) {
            $method = $this->paymentMethods[$methodIndex];
            if ($method instanceof SupportsInstallment) {
                $options = $method->getInstallmentOptions($amount);
                echo "分期选项:\n";
                foreach ($options as $months => $totalAmount) {
                    $monthly = $totalAmount / $months;
                    echo "  {$months}期:每期¥" . round($monthly, 2) . ",总计¥{$totalAmount}\n";
                }
                return $options;
            }
            echo "该支付方式不支持分期\n";
        }
        return null;
    }
}

// 使用示例
echo "=== 支付系统演示 ===\n";

$manager = new PaymentManager();
$manager->addPaymentMethod(new CreditCardPayment("1234-5678-9012-3456", 10000));
$manager->addPaymentMethod(new AlipayPayment("user@example.com"));

// 信用卡支付
echo "--- 信用卡支付 ---\n";
$manager->processPayment(0, 1000);
$manager->processRefund(0, 500);

// 分期选项
echo "\n--- 分期选项 ---\n";
$manager->showInstallmentOptions(0, 6000);
$manager->processPayment(0, 6000);  // 这里会调用分期的实现

// 支付宝支付
echo "\n--- 支付宝支付 ---\n";
$manager->processPayment(1, 800);
$manager->processRefund(1, 300);

// 支付宝不支持分期
echo "\n--- 支付宝分期 ---\n";
$manager->showInstallmentOptions(1, 1000);
?>

设计原则和最佳实践

1. 接口隔离原则(ISP)

<?php
// 避免大而全的接口
interface BadAnimalInterface {
    public function fly();
    public function swim();
    public function run();
    public function bark();
    public function meow();
}

// 使用小而专一的接口
interface Flyable {
    public function fly();
}

interface Swimmable {
    public function swim();
}

interface Runnable {
    public function run();
}

interface Vocal {
    public function makeSound();
}

// 根据需要实现相应接口
class Bird implements Flyable, Vocal {
    public function fly() {
        echo "鸟儿在飞翔\n";
    }

    public function makeSound() {
        echo "鸟儿在歌唱\n";
    }
}

class Fish implements Swimmable {
    public function swim() {
        echo "鱼儿在游泳\n";
    }
}

class Dog implements Runnable, Vocal {
    public function run() {
        echo "狗在奔跑\n";
    }

    public function makeSound() {
        echo "狗在汪汪叫\n";
    }
}
?>

2. 选择抽象类还是接口

<?php
// 使用抽象类的场景:
// 1. 需要共享代码
// 2. 需要定义属性
// 3. 需要提供部分默认实现
abstract class DatabaseConnection {
    protected $host;
    protected $username;
    protected $password;
    protected $connection;

    public function __construct($host, $username, $password) {
        $this->host = $host;
        $this->username = $username;
        $this->password = $password;
    }

    // 具体的通用方法
    public function testConnection() {
        try {
            $this->connect();
            echo "连接测试成功\n";
            $this->disconnect();
            return true;
        } catch (Exception $e) {
            echo "连接测试失败:" . $e->getMessage() . "\n";
            return false;
        }
    }

    // 抽象方法,由子类实现
    abstract protected function connect();
    abstract protected function disconnect();
    abstract public function query($sql);
}

// 使用接口的场景:
// 1. 定义行为契约
// 2. 支持多重继承
// 3. 跨类层次结构
interface Cacheable {
    public function get($key);
    public function set($key, $value, $ttl = 3600);
    public function delete($key);
    public function clear();
}

interface Loggable {
    public function log($message, $level = 'info');
}

// 可以同时实现多个接口
class RedisCache implements Cacheable, Loggable {
    // 实现所有接口方法
}

class FileCache implements Cacheable {
    // 只实现需要的接口
}
?>

3. 模板方法模式

<?php
// 使用抽象类实现模板方法模式
abstract class DataProcessor {
    // 模板方法:定义算法骨架
    public function process($data) {
        $data = $this->validateData($data);
        $data = $this->transformData($data);
        $result = $this->saveData($data);
        $this->notifyCompletion($result);
        return $result;
    }

    // 具体方法:通用实现
    protected function validateData($data) {
        if (empty($data)) {
            throw new Exception("数据不能为空");
        }
        echo "数据验证通过\n";
        return $data;
    }

    protected function notifyCompletion($result) {
        echo "数据处理完成\n";
    }

    // 抽象方法:子类实现
    abstract protected function transformData($data);
    abstract protected function saveData($data);
}

class XMLProcessor extends DataProcessor {
    protected function transformData($data) {
        echo "将数据转换为XML格式\n";
        return "<data>" . htmlspecialchars($data) . "</data>";
    }

    protected function saveData($data) {
        echo "保存XML数据到文件\n";
        return true;
    }
}

class JSONProcessor extends DataProcessor {
    protected function transformData($data) {
        echo "将数据转换为JSON格式\n";
        return json_encode(['content' => $data]);
    }

    protected function saveData($data) {
        echo "保存JSON数据到数据库\n";
        return true;
    }
}

// 使用示例
$xmlProcessor = new XMLProcessor();
$xmlProcessor->process("测试数据");

$jsonProcessor = new JSONProcessor();
$jsonProcessor->process("测试数据");
?>

通过本节的学习,你应该掌握了抽象类和接口的概念、区别以及应用场景。抽象类适合定义通用模板和共享代码,而接口适合定义行为契约和实现多重继承。在实际开发中,合理使用抽象类和接口可以让代码更加灵活、可扩展和易于维护。