Php 我如何在这个场景中实现抽象?

Php 我如何在这个场景中实现抽象?,php,oop,inheritance,abstraction,Php,Oop,Inheritance,Abstraction,我有一个父类产品和两个子类:牙刷和电锯。它们的设置如下所示 下面是父类: class Product { protected $productid; protected $type; public function __construct( $productid ) { $this->productid = $productid; // Performs a lookup in the database and then popul

我有一个父类
产品
和两个子类:
牙刷
电锯
。它们的设置如下所示

下面是父类:

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
的列表,并获得相应的物品价格,而不管它们是
电锯
还是
牙刷
es

问题(或者是它吗?) 现在我一遍又一遍地听说父类不应该依赖子类来实现功能(是的,我和其他许多人都读过)

这就是为什么我认为我目前使用的解决方案(以下)不是最佳的:

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
属性。

家长不应依赖其子女是正确的,这就是为什么您也在家长中定义它

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');
好的,你可以阅读。另外,你可以在父对象上做它(以及获取它的方法),而不是在子对象上声明价格,然后在需要时在任何子对象中重写该方法

现在我一遍又一遍地听说父类不应该依赖子类来实现功能

没错。而且您的
开关
将是一个不需要的依赖项,而调用
getPrice
则不会,只要它在父类中定义为抽象方法并在子类中重写。那么父类就不需要知道具体的子类,仍然可以调用它们的方法。如果这听起来很奇怪,那么哦,你,阅读多态性,理解这个概念对于理解OOP很重要

但你的问题更深:

这里是要点:如果只有productid,我如何调用子方法?(在上面的场景中,Product类从构造函数中的数据库检索类型,并相应地填充$type属性)

显然,你从来没有创建过链锯或牙刷的实例。你不能用
新产品
创建一个产品,然后告诉它“现在你是一个链锯”.对象的实际类型是不可变的。您试图通过在产品内部创建一个新的链锯来解决这一问题,该产品本来应该是一个链锯,只是为了获取其价格。这是一个可怕的错误,我想您已经意识到了这一点

这就是为什么在注释中建议使用工厂模式。工厂是一个实例化对象并根据参数决定使用哪个子类型的类。它也是此类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();
为简单起见,我将数据库查找放在工厂中。更好的解决方案是将其与两个类完全分离,例如在负责与products表相关的所有数据库查询的
ProductTableGateway
类中。工厂将只接收结果


顺便说一句,我还建议最终去掉这些子类。没有一家严肃的网上商店对每种产品类型都有硬编码的类,而是动态创建不同的属性集,并将不同的价格计算委托给其他类。但这是一个高级主题,现在太离谱了。

目标在哪里ProductID的et列表来自?基类应该只有一个泛型的
getPrice
方法,如果默认值没有意义,该方法将返回默认值,或者什么都不返回。在子类中重写
getPrice
是完全正确的,并且符合多态性的概念。@Cups ProductID来自数据库选项卡恰当地称为产品。摆脱
$type
属性,创建一个工厂方法,它可以生产牙刷或链锯(取决于其参数,即类型)。子类对我来说似乎是多余的。您可以简单地将产品数据传递到产品类中。为什么需要键入所有这些,而不只是在
product
类中将
getPrice
作为抽象函数?谢谢Hugo,但这似乎并不能解决我最初遇到的问题:如果我不知道如何获取价格w事先是什么类型的产品。@FloatingRock只需调用
getPrice
。电锯的
getPrice
方法的行为与牙刷的
getPrice
方法不同。@Asad你能告诉我这是如何工作的吗?我看不出这是可行的。@FloatingRock我不完全确定你的意思。上面的代码演示了如何重写方法。只需实例化一把牙刷和一把电锯,然后试着从中获取价格。这是一个演示。呃,谢谢。但我已经经历了这一过程,无法理解它。如果我不能实例化一个
抽象类,那么我到底该如何知道
的类型呢>产品
是?(因为
产品
的类型在构造函数中确定)完美答案。非常感谢@fab