You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用抽象类实现三类产品的多态CRUD操作(禁用条件判断)

Refactoring Product System with Abstract Class & Polymorphism in PHP

Step 1: Create Abstract Base Class Product

Convert your existing Product model into an abstract class that defines shared properties and enforces type-specific methods via abstract declarations. This ensures all product subclasses implement core operations consistently.

abstract class Product {
    protected $sku;
    protected $name;
    protected $price;
    protected $type;

    public function __construct(array $data) {
        $this->sku = $data['sku'];
        $this->name = $data['name'];
        $this->price = $data['price'];
        $this->type = $data['type'];
        $this->validateTypeSpecificAttributes($data);
    }

    // Common getters
    public function getSku(): string { return $this->sku; }
    public function getName(): string { return $this->name; }
    public function getPrice(): float { return $this->price; }
    public function getType(): string { return $this->type; }

    // Abstract methods to be implemented by subclasses
    abstract protected function validateTypeSpecificAttributes(array $data): void;
    abstract public function save(): bool;
    abstract public function delete(): bool;
    abstract public function display(): string;
}

Step 2: Implement Product Subclasses

Create a dedicated subclass for each product type, handling type-specific validation, persistence, and display logic.

DVD Product

class DVDProduct extends Product {
    private $size;

    protected function validateTypeSpecificAttributes(array $data): void {
        if (!isset($data['size']) || !is_numeric($data['size']) || $data['size'] <= 0) {
            throw new InvalidArgumentException("DVD size must be a positive number.");
        }
        $this->size = (int)$data['size'];
    }

    public function save(): bool {
        // Database logic to insert DVD-specific data (sku, name, price, type, size)
        $sql = "INSERT INTO products (sku, name, price, type, size) VALUES (?, ?, ?, ?, ?)";
        // Execute query with $this->sku, $this->name, $this->price, $this->type, $this->size
        return true; // Return actual success status from query
    }

    public function delete(): bool {
        $sql = "DELETE FROM products WHERE sku = ? AND type = ?";
        // Execute query with $this->sku, $this->type
        return true;
    }

    public function display(): string {
        return sprintf(
            "<div class='product'>
                <p>SKU: %s</p>
                <p>Name: %s</p>
                <p>Price: $%.2f</p>
                <p>Type: DVD</p>
                <p>Size: %d MB</p>
            </div>",
            htmlspecialchars($this->sku),
            htmlspecialchars($this->name),
            $this->price,
            $this->size
        );
    }
}

Furniture Product

class FurnitureProduct extends Product {
    private $height;
    private $width;
    private $length;

    protected function validateTypeSpecificAttributes(array $data): void {
        foreach (['height', 'width', 'length'] as $attr) {
            if (!isset($data[$attr]) || !is_numeric($data[$attr]) || $data[$attr] <= 0) {
                throw new InvalidArgumentException("Furniture $attr must be a positive number.");
            }
        }
        $this->height = (float)$data['height'];
        $this->width = (float)$data['width'];
        $this->length = (float)$data['length'];
    }

    public function save(): bool {
        $sql = "INSERT INTO products (sku, name, price, type, height, width, length) VALUES (?, ?, ?, ?, ?, ?, ?)";
        // Execute query with relevant attributes
        return true;
    }

    public function delete(): bool {
        $sql = "DELETE FROM products WHERE sku = ? AND type = ?";
        // Execute query
        return true;
    }

    public function display(): string {
        return sprintf(
            "<div class='product'>
                <p>SKU: %s</p>
                <p>Name: %s</p>
                <p>Price: $%.2f</p>
                <p>Type: Furniture</p>
                <p>Dimensions: %.2fx%.2fx%.2f cm</p>
            </div>",
            htmlspecialchars($this->sku),
            htmlspecialchars($this->name),
            $this->price,
            $this->height,
            $this->width,
            $this->length
        );
    }
}

Book Product

class BookProduct extends Product {
    private $weight;

    protected function validateTypeSpecificAttributes(array $data): void {
        if (!isset($data['weight']) || !is_numeric($data['weight']) || $data['weight'] <= 0) {
            throw new InvalidArgumentException("Book weight must be a positive number.");
        }
        $this->weight = (float)$data['weight'];
    }

    public function save(): bool {
        $sql = "INSERT INTO products (sku, name, price, type, weight) VALUES (?, ?, ?, ?, ?)";
        // Execute query
        return true;
    }

    public function delete(): bool {
        $sql = "DELETE FROM products WHERE sku = ? AND type = ?";
        // Execute query
        return true;
    }

    public function display(): string {
        return sprintf(
            "<div class='product'>
                <p>SKU: %s</p>
                <p>Name: %s</p>
                <p>Price: $%.2f</p>
                <p>Type: Book</p>
                <p>Weight: %.2f Kg</p>
            </div>",
            htmlspecialchars($this->sku),
            htmlspecialchars($this->name),
            $this->price,
            $this->weight
        );
    }
}

Step 3: Refactor AddProduct Controller

Eliminate type conditionals by dynamically instantiating the correct subclass based on the submitted product type.

class AddProductController {
    public function handleRequest() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->renderForm();
            return;
        }

        try {
            $data = $_POST;
            $type = strtolower($data['type']);
            $productClass = ucfirst($type) . 'Product';

            // Validate valid product subclass exists
            if (!class_exists($productClass) || !is_subclass_of($productClass, Product::class)) {
                throw new InvalidArgumentException("Invalid product type.");
            }

            $product = new $productClass($data);
            if ($product->save()) {
                header("Location: /products");
                exit;
            } else {
                throw new Exception("Failed to save product.");
            }
        } catch (InvalidArgumentException $e) {
            $this->renderForm(['error' => $e->getMessage()]);
        } catch (Exception $e) {
            $this->renderForm(['error' => 'An unexpected error occurred.']);
        }
    }

    private function renderForm(array $data = []) {
        // Render add product form with error messages (include type dropdown: dvd, furniture, book)
    }
}

Step 4: Refactor Products Controller

Display products without type checks by instantiating the correct subclass for each record and calling its display() method.

class ProductsController {
    public function index() {
        $productsData = $this->getAllProductsFromDB();
        $products = [];

        foreach ($productsData as $productData) {
            $type = strtolower($productData['type']);
            $productClass = ucfirst($type) . 'Product';
            
            if (class_exists($productClass) && is_subclass_of($productClass, Product::class)) {
                $products[] = new $productClass($productData);
            }
        }

        $this->renderProducts($products);
    }

    private function getAllProductsFromDB(): array {
        // Fetch all products with their type-specific attributes from database
        $sql = "SELECT * FROM products";
        // Execute query and return array of product data
        return []; // Replace with actual database results
    }

    private function renderProducts(array $products) {
        echo "<h1>Products</h1>";
        foreach ($products as $product) {
            echo $product->display();
        }
        echo "<a href='/add-product'>Add New Product</a>";
    }
}

Key Advantages

  • No Type Conditionals: All type-specific logic lives in subclasses, keeping controllers clean and uncoupled from product types.
  • Extensibility: Adding a new product type only requires creating a new Product subclass—no changes to existing controllers.
  • Maintainability: Validation, persistence, and display logic are centralized per product type, making updates easier.

内容的提问来源于stack exchange,提问作者pedram

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 09:55:14