Kodokon kodokon.com

การสืบทอด อินเทอร์เฟซ trait และคลาสนามธรรม

เลือกเครื่องมือออกแบบที่ถูกต้อง ไม่ว่าจะเป็นสัญญา โครงร่างของอัลกอริทึม หรือการใช้ซ้ำแนวราบ ด้วยการเข้าใจข้อแลกเปลี่ยนของแต่ละอย่าง

9 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

PHP มอบเครื่องมือออกแบบสี่อย่างที่ทับซ้อนกันบางส่วน ได้แก่ การสืบทอดแบบรูปธรรม (concrete inheritance) คลาสนามธรรม อินเทอร์เฟซ และ trait การเลือกสรุปลงที่คำถามเดียว: คุณต้องการแบ่งปันอะไร? สัญญา (contract): อินเทอร์เฟซ สัญญาพร้อมโครงร่างของการนำไปใช้: คลาสนามธรรม โค้ดที่ใช้ร่วมกันระหว่างคลาสที่ไม่มีความเชื่อมโยงเชิงลำดับชั้น: trait ส่วนการสืบทอดแบบรูปธรรมนั้นหาเหตุผลมารองรับได้ยากขึ้นเรื่อย ๆ มันผูกมัดกันแน่นเกินไป และ PHP อนุญาตให้มีคลาสแม่ได้เพียงตัวเดียวเท่านั้น

PHP
<?php

declare(strict_types=1);

abstract class Exporter
{
    public function export(array $rows): string
    {
        return $this->head() . $this->body($rows);
    }

    abstract protected function head(): string;

    abstract protected function body(
        array $rows
    ): string;
}

final class CsvExporter extends Exporter
{
    protected function head(): string
    {
        return "id;name\n";
    }

    protected function body(array $rows): string
    {
        $lines = array_map(
            fn (array $r): string => implode(';', $r),
            $rows,
        );

        return implode("\n", $lines);
    }
}

$csv = new CsvExporter();
echo $csv->export([[1, 'Ada'], [2, 'Linus']]);
Template method: คลาสแม่กำหนดอัลกอริทึมตายตัว ส่วนคลาสลูกจัดหารายละเอียด

อินเทอร์เฟซคือสัญญาที่บริสุทธิ์: มีเพียงลายเซ็นและค่าคงที่ ไม่มีสถานะและไม่มีการนำไปใช้ ข้อได้เปรียบชี้ขาดของมันเหนือคลาสนามธรรมคือ คลาสหนึ่งสามารถนำอินเทอร์เฟซไปใช้ได้ หลายตัว และคลาสที่มีอยู่แล้วตัวใดก็ได้สามารถรับสัญญานั้นมาใช้ภายหลังได้ ในทางกลับกัน คลาสนามธรรมพกพาสถานะและโค้ดที่ใช้ร่วมกัน แต่ก็กินช่องการสืบทอดเพียงช่องเดียวของคุณไป ในทางปฏิบัติ: จงระบุชนิดพารามิเตอร์ของคุณด้วยอินเทอร์เฟซ (Cache ไม่ใช่ ArrayCache) เพื่อให้คุณสลับการนำไปใช้ได้ ทั้งในการทดสอบและในการใช้งานจริง

PHP
<?php

declare(strict_types=1);

interface Cache
{
    public function get(string $key): ?string;

    public function set(
        string $key,
        string $value
    ): void;
}

final class ArrayCache implements Cache
{
    private array $items = [];

    public function get(string $key): ?string
    {
        return $this->items[$key] ?? null;
    }

    public function set(
        string $key,
        string $value
    ): void {
        $this->items[$key] = $value;
    }
}

$cache = new ArrayCache();
$cache->set('lang', 'PHP');
echo $cache->get('lang');
โค้ดฝั่งผู้เรียกขึ้นอยู่กับ Cache: การนำไปใช้ยังคงสลับเปลี่ยนกันได้

trait แก้ปัญหาการแบ่งปันแบบ แนวราบ: การฉีดเมท็อดชุดเดียวกันเข้าไปในคลาสที่ไม่มีบรรพบุรุษร่วมกัน เมื่อ trait สองตัวมีเมท็อดชื่อเดียวกัน PHP จะปฏิเสธที่จะเลือกให้คุณ และเรียกร้องให้แก้ความขัดแย้งอย่างชัดเจน: insteadof ระบุเวอร์ชันที่จะเก็บไว้ ส่วน as สร้างชื่อแทน (alias) เพื่อให้อีกเวอร์ชันยังเข้าถึงได้ มันยืดยาวโดยตั้งใจ ความขัดแย้งต้องยังคงมองเห็นได้ในโค้ด

PHP
<?php

declare(strict_types=1);

trait FileLogger
{
    public function log(string $message): void
    {
        echo "[file] $message\n";
    }
}

trait AuditLogger
{
    public function log(string $message): void
    {
        echo "[audit] $message\n";
    }
}

final class PaymentService
{
    use FileLogger, AuditLogger {
        FileLogger::log insteadof AuditLogger;
        AuditLogger::log as audit;
    }
}

$service = new PaymentService();
$service->log('Payment received');
$service->audit('Payment received');
ความขัดแย้งของ trait: insteadof เป็นตัวตัดสิน ส่วน as เก็บชื่อแทนไว้

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. อินเทอร์เฟซมีข้อได้เปรียบชี้ขาดอะไรเหนือคลาสนามธรรม?
    • มันสามารถเก็บคุณสมบัติที่ใช้ร่วมกันได้
    • คลาสหนึ่งสามารถนำอินเทอร์เฟซไปใช้ได้หลายตัว แต่สืบทอดจากคลาสได้เพียงคลาสเดียว
    • เมท็อดของมันทำงานเร็วกว่า
  2. trait สองตัวที่ถูกใช้โดยคลาสเดียวกันต่างประกาศเมท็อด log() จะเกิดอะไรขึ้น?
    • PHP ใช้เมท็อดจาก trait ตัวแรกที่ระบุไว้
    • เกิดข้อผิดพลาดร้ายแรง เว้นแต่จะแก้อย่างชัดเจนด้วย insteadof
    • PHP รวมเมท็อดทั้งสองเข้าเป็นหนึ่งเดียว
  3. คลาสนามธรรมสามารถบรรจุสิ่งใดที่อินเทอร์เฟซทำไม่ได้?
    • ค่าคงที่
    • คุณสมบัติและเมท็อดที่ถูกนำไปใช้แล้ว
    • ลายเซ็นของเมท็อดแบบ public