我有一个父类Product
和两个子类:Toothbrush
和Chainsaw
。它们的设置如下所示。
这是父类:
class Product {
protected $productid;
protected $type;
public function __construct( $productid ) {
$this->productid = $productid;
// Performs a lookup in the database and then populates the $type property
}
}
..这里是孩子们:
class Toothbrush extends Product {
public function getPrice() {
return 5; // returning an integer for simplicity; there's a calculation going on here
}
}
class Chainsaw extends Product {
public function getPrice() {
return 1000; // in USD
}
}
我希望遍历$productid
列表并获取相应的商品价格,无论这些商品是chainsaw
还是toothbrush
。
现在我一遍又一遍地听说一个父类不应该依赖子类来实现功能(是的,我读了this question,还有很多其他的。)< / p>
这就是为什么我认为我目前正在使用的解决方案(下面)不是最佳的:
class Product {
...
public function getPrice() {
switch($this->type) {
case 'toothbrush':
$theproduct=new Toothbrush($this->productid);
return $theproduct->getPrice();
break;
case 'chainsaw':
$theproduct=new Chainsaw($this->productid);
return $theproduct->getPrice();
break;
}
}
}
我显然可以感觉到某些东西在这里是疏忽的(我想到当我得到30种不同的产品类型时会发生什么事情让我不寒而栗)。我已经阅读过关于抽象,接口和继承的内容,并且无法确定在这种情况下哪一个可以工作。
谢谢!
看到很多答案,但还没有一个已经钉了它们。以下是要点:
如果只有productid,我如何调用子方法?(在上面的场景中,Product
类从构造函数中的数据库中检索类型并填充$type
相应的财产。
答案 0 :(得分:2)
现在我一遍又一遍地听说父类不应该依赖子类来实现功能
完全。并且你的switch
将是一个不受欢迎的依赖,而对getPrice
的调用则不会,只要它被定义为父级中的抽象方法并在子级中被覆盖。然后父类不需要知道具体的子类,仍然可以调用它们的方法。如果这听起来很奇怪,请阅读有关多态的知识,了解这个概念以理解OOP非常重要。
但你的问题更深入了:
以下是要点:如果只有productid,我如何调用子方法? (在上面的场景中,Product类从构造函数中的数据库中检索类型,并相应地填充$ type属性。
显然你不会创建Chainsaw或Toothbrush的实例。您无法使用new Product
创建产品,然后告诉它“现在您是电锯”。 对象的实际类型是不可变的。您试图通过在产品内部创建一个新的电锯来实现这一目标,该电锯应该是电锯,只是为了获得价格。这是非常错误的,我想你已经意识到了。
这就是评论中建议工厂模式的原因。工厂是一个实例化对象并根据参数决定使用哪个子类型的类。它也是这种switch语句的有效位置。
示例:强>
class ProductFactory
{
public function makeProduct($id)
{
$record = perform_your_database_lookup_here();
switch ($record['type']) {
case 'toothbrush':
return new Toothbrush($id, $record);
case 'chainsaw':
return new Chainsaw($id, $record);
}
}
}
$factory = new ProductFactory();
$product = $factory->makeProduct(123);
echo $product->getPrice();
为简单起见,我将数据库查找放在工厂中。更好的解决方案是将它与两个类完全分开,例如在ProductTableGateway
类中,该类负责与products表相关的所有数据库查询。然后工厂才会收到结果。
顺便说一下,我还建议最后摆脱这些子类。没有严肃的在线商店为每种产品类型都有硬编码类,而是动态创建不同的属性集,并将不同的价格计算委托给其他类。但这是一个高级话题,现在已经走得太远了。
答案 1 :(得分:0)
父母不应该依赖他们的孩子,这是正确的,这就是为什么你在父母中定义它的原因。
class Product {
protected $productid;
public function getPrice() {
return NULL; // or 0 or whatever default you want
}
}
class Toothbrush extends Product {
public function getPrice() {
return 5; // in USD
}
}
class Chainsaw extends Product {
public function getPrice() {
return 1000; // in USD
}
}
class Fake extends Product {
}
$f = new Fake();
var_dump($f->getPrice());
现在代码将始终有效,无论子项是否定义了getPrice()方法。
但也许最好使用像
这样的东西class Product {
protected $productid;
protected $price;
protected $type;
public function __construct($id, $price, $type) {
$this->productid = $id;
$this->price = $price;
$this->type = $type;
}
public function getPrice() {
return $this->price;
}
}
$tooth = new Product(1, 5, 'Toothbrush');
$chain = new Product(2, 1000, 'Chainsaw');
答案 2 :(得分:0)
好吧,你可以阅读http://www.php.net/manual/en/language.oop5.abstract.php。此外,您可以在父项上声明子项的价格(以及获取它的方法),然后在任何子项中覆盖该方法(如果需要)。