抽象类与接口
抽象类(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("测试数据");
?>
通过本节的学习,你应该掌握了抽象类和接口的概念、区别以及应用场景。抽象类适合定义通用模板和共享代码,而接口适合定义行为契约和实现多重继承。在实际开发中,合理使用抽象类和接口可以让代码更加灵活、可扩展和易于维护。