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

如何利用多态移除ProductController中createProduct方法的条件语句?

解决方案:用工厂模式+多态消除条件判断

要移除ProductController中的if/else分支,核心是利用多态特性让所有产品子类遵循统一的抽象接口,再配合工厂模式封装实例化逻辑,这样控制器只需要和抽象父类交互,无需关心具体子类类型。

步骤1:重构抽象Product类

首先完善抽象类,定义强制子类实现的抽象方法,同时抽离重复的通用逻辑(比如session提示、页面跳转),避免子类代码冗余:

abstract class Product {
    protected $ID;
    protected $name;
    protected $price;
    protected $productType;

    // 标准构造方法,初始化通用属性
    public function __construct($ID, $name, $price, $productType) {
        $this->ID = $ID;
        $this->name = $name;
        $this->price = $price;
        $this->productType = $productType;
    }

    // 通用getter方法
    public function getID() { return $this->ID; }
    public function getName() { return $this->name; }
    public function getPrice() { return $this->price; }
    public function getProductType() { return $this->productType; }

    // 数据库连接方法(假设原本是父类的通用方法,这里明确出来)
    protected function connect() {
        // 这里写你的数据库连接逻辑,比如PDO实例
        return new PDO("mysql:host=localhost;dbname=your_db", "user", "pass");
    }

    // 强制子类实现的抽象方法:生成插入SQL和参数
    abstract protected function buildInsertSql();
    abstract protected function getInsertParams();

    // 统一的保存方法,封装通用逻辑
    public function setProduct() {
        $sql = $this->buildInsertSql();
        $query = $this->connect()->prepare($sql);
        $query->execute($this->getInsertParams());

        $_SESSION['save-btn'] = "Products added successfully";
        header("Location:../index.php");
        exit; // 跳转后必须终止脚本执行
    }
}

步骤2:重构子类,遵循统一接口

把子类的自定义构造方法改为标准__construct,并实现父类的抽象方法:

修改后的PenDrive子类

class PenDrive extends Product {
    protected $size;

    public function __construct($ID, $name, $price, $productType, $size) {
        parent::__construct($ID, $name, $price, $productType);
        $this->size = $size;
    }

    protected function buildInsertSql() {
        return "INSERT INTO product (ID, productName, price, productType, weight) VALUES (?,?,?,?,?)";
    }

    protected function getInsertParams() {
        return [
            $this->getID(),
            $this->getName(),
            $this->getPrice(),
            $this->getProductType(),
            $this->size
        ];
    }
}

修改后的Luggage子类

class Luggage extends Product {
    protected $height;
    protected $width;
    protected $length;

    public function __construct($ID, $name, $price, $productType, $height, $width, $length) {
        parent::__construct($ID, $name, $price, $productType);
        $this->height = $height;
        $this->width = $width;
        $this->length = $length;
    }

    protected function buildInsertSql() {
        return "INSERT INTO product (ID, productName, price, productType, height, width, length) VALUES (?,?,?,?,?,?,?)";
    }

    protected function getInsertParams() {
        return [
            $this->getID(),
            $this->getName(),
            $this->getPrice(),
            $this->getProductType(),
            $this->height,
            $this->width,
            $this->length
        ];
    }
}

补充Bottle子类示例(按相同逻辑实现)

class Bottle extends Product {
    protected $capacity;

    public function __construct($ID, $name, $price, $productType, $capacity) {
        parent::__construct($ID, $name, $price, $productType);
        $this->capacity = $capacity;
    }

    protected function buildInsertSql() {
        // 假设Bottle对应的字段是capacity,根据实际表结构调整
        return "INSERT INTO product (ID, productName, price, productType, capacity) VALUES (?,?,?,?,?)";
    }

    protected function getInsertParams() {
        return [
            $this->getID(),
            $this->getName(),
            $this->getPrice(),
            $this->getProductType(),
            $this->capacity
        ];
    }
}

步骤3:实现产品工厂类

创建工厂类封装实例化逻辑,把原来Controller里的条件判断移到这里,后续新增产品只需修改工厂的映射,无需改动Controller:

class ProductFactory {
    // 产品类型到类名的映射,扩展新产品时只需添加新条目
    private static $productMap = [
        'Bottle' => 'Bottle',
        'Luggage' => 'Luggage',
        'PenDrive' => 'PenDrive'
    ];

    public static function create($productType, ...$args) {
        // 检查产品类型是否合法
        if (!isset(self::$productMap[$productType])) {
            throw new InvalidArgumentException("Unsupported product type: $productType");
        }

        $className = self::$productMap[$productType];
        // 动态创建实例,参数传递给子类构造方法
        return new $className(...$args);
    }
}

步骤4:重构ProductController

现在控制器的createProduct方法完全不需要if/else分支,直接通过工厂获取实例并调用统一的setProduct方法:

class ProductController {
    public function createProduct($ID, $name, $price, $productType, $capacity, $height, $width, $length, $size) {
        try {
            // 根据产品类型传递对应参数给工厂
            switch ($productType) {
                case 'Bottle':
                    $product = ProductFactory::create($productType, $ID, $name, $price, $productType, $capacity);
                    break;
                case 'Luggage':
                    $product = ProductFactory::create($productType, $ID, $name, $price, $productType, $height, $width, $length);
                    break;
                case 'PenDrive':
                    $product = ProductFactory::create($productType, $ID, $name, $price, $productType, $size);
                    break;
                default:
                    throw new InvalidArgumentException("Unsupported product type: $productType");
            }

            $product->setProduct();
        } catch (InvalidArgumentException $e) {
            $_SESSION['error'] = $e->getMessage();
            header("Location:../index.php");
            exit;
        }
    }
}

进一步优化(可选)

如果想彻底消除Controller里的switch,可以把参数整理成关联数组,让工厂根据产品类型提取所需参数:

// 调整Controller方法参数为关联数组
public function createProduct(array $params) {
    $productType = $params['productType'];
    // 定义每种产品所需的参数键名
    $paramMap = [
        'Bottle' => ['ID', 'name', 'price', 'productType', 'capacity'],
        'Luggage' => ['ID', 'name', 'price', 'productType', 'height', 'width', 'length'],
        'PenDrive' => ['ID', 'name', 'price', 'productType', 'size']
    ];

    if (!isset($paramMap[$productType])) {
        throw new InvalidArgumentException("Unsupported product type: $productType");
    }

    // 提取对应参数
    $args = array_map(fn($key) => $params[$key], $paramMap[$productType]);
    $product = ProductFactory::create($productType, ...$args);
    $product->setProduct();
}

核心优势

  • 多态体现:所有子类都实现父类的抽象方法,控制器只需调用父类定义的setProduct,无需关心具体子类的实现细节。
  • 开闭原则:新增产品时,只需添加子类、更新工厂的$productMap,无需修改现有Controller和其他子类代码。
  • 代码复用:父类封装了通用的数据库连接、session提示和跳转逻辑,避免子类重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 16:50:33