PHP 抽象类

示例

抽象类是无法实例化的类。抽象类可以定义抽象方法,这些方法没有任何主体,只有一个定义:

abstract class MyAbstractClass {
    abstract public function doSomething($a, $b);
}

应该通过子类扩展抽象类,然后可以提供这些抽象方法的实现。

这样的类的主要目的是提供一种模板,该模板允许子类继承,“强制”遵循的结构。让我们用一个例子来详细说明一下:

在此示例中,我们将实现一个Worker接口。首先我们定义接口:

interface Worker {
    public function run();
}

为了简化进一步的Worker实现的开发,我们将创建一个抽象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) {
            // 捕获并重新抛出所有错误,以便工人可以记录下来
            $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) {
            // 可能引发一些PDO或MYSQL异常,但这由AbstractWorker处理
            $stmt = $this->pdo->query("UPDATE transactions SET processed = 1 WHERE id = {$transaction['id']} LIMIT 1");
            $stmt->execute();
        }
    }
}

如您所见,它TransactionProcessorWorker非常容易实现,因为我们只需要指定内存限制并担心它需要执行的实际操作。不需要进行错误处理,TransactionProcessorWorker因为该错误是在中处理的AbsractWorker。

重要的提示

从抽象类继承时,父类声明中标记为抽象的所有方法都必须由子代定义(或者子代本身也必须标记为抽象);此外,必须以相同(或较少限制)的可见性定义这些方法。例如,如果抽象方法定义为protected,则函数实现必须定义为protected或public,但不能定义为private。

摘自用于类抽象的PHP文档。

如果在子类中定义父抽象类方法,则将引发致命PHP错误,如下所示。

致命错误:类X包含1抽象方法,因此必须声明为抽象,或在其中实现其余方法(X :: x)