如何利用多态移除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
相关产品推荐
相关产品推荐

