抽象类
抽象类是无法实例化的类。抽象类可以定义抽象方法,这些方法是没有任何主体的方法,只有一个定义:
abstract class MyAbstractClass {
abstract public function doSomething($a, $b);
}
抽象类应该由子类扩展,然后可以提供这些抽象方法的实现。
像这样的类的主要目的是提供一种模板,允许子类继承,强制结构遵守。让我们举一个例子详细说明:
在这个例子中,我们将实现一个 Worker
接口。首先我们定义接口:
interface Worker {
public function run();
}
为了简化进一步的 Worker 实现的开发,我们将创建一个已经从接口提供 run()
方法的抽象工作类,但是指定了一些需要由任何子类填充的抽象方法:
abstract class AbstractWorker implements Worker {
protected $pdo;
protected $logger;
public function __construct(PDO $pdo, Logger $logger) {
$this->pdo = $pdo;
$this->logger = $logger;
}
public function run() {
try {
$this->setMemoryLimit($this->getMemoryLimit());
$this->logger->log("Preparing main");
$this->prepareMain();
$this->logger->log("Executing main");
$this->main();
} catch (Throwable $e) {
// Catch and rethrow all errors so they can be logged by the worker
$this->logger->log("Worker failed with exception: {$e->getMessage()}");
throw $e;
}
}
private function setMemoryLimit($memoryLimit) {
ini_set('memory_limit', $memoryLimit);
$this->logger->log("Set memory limit to $memoryLimit");
}
abstract protected function getMemoryLimit();
abstract protected function prepareMain();
abstract protected function main();
}
首先,我们提供了一个抽象方法 getMemoryLimit()
。从 AbstractWorker
扩展的任何类都需要提供此方法并返回其内存限制。然后 AbstractWorker
设置内存限制并记录它。
其次,AbstractWorker
在记录它们被调用之后调用 prepareMain()
和 main()
方法。
最后,所有这些方法调用都被分组在 try
-catch
块中。因此,如果子类定义的任何抽象方法抛出异常,我们将捕获该异常,记录并重新抛出它。这可以防止所有子类自己实现它。
现在让我们定义一个从 AbstractWorker
扩展的子类:
class TranscactionProcessorWorker extends AbstractWorker {
private $transactions;
protected function getMemoryLimit() {
return "512M";
}
protected function prepareMain() {
$stmt = $this->pdo->query("SELECT * FROM transactions WHERE processed = 0 LIMIT 500");
$stmt->execute();
$this->transactions = $stmt->fetchAll();
}
protected function main() {
foreach ($this->transactions as $transaction) {
// Could throw some PDO or MYSQL exception, but that is handled by the AbstractWorker
$stmt = $this->pdo->query("UPDATE transactions SET processed = 1 WHERE id = {$transaction['id']} LIMIT 1");
$stmt->execute();
}
}
}
正如你所看到的,TransactionProcessorWorker
很容易实现,因为我们只需要指定内存限制并担心它需要执行的实际操作。TransactionProcessorWorker
中不需要处理错误,因为这是在 AbsractWorker
中处理的。
重要的提示
从抽象类继承时,父类的声明中标记为 abstract 的所有方法都必须由子类定义(或者子类本身也必须标记为 abstract); 此外,必须使用相同(或限制较少)的可见性来定义这些方法。例如,如果将抽象方法定义为 protected,则必须将函数实现定义为 protected 或 public,而不是 private。
摘自 PHP 文档中的类抽象 。
如果你没有在子类中定义父抽象类方法,则会抛出致命的 PHP 错误,如下所示。
致命错误: 类 X 包含 1 个抽象方法,因此必须声明为 abstract 或实现其余方法(X::x)