PHP - 工厂的产品有特殊的方法

PHP - a product of a factory has special method

我尝试使用工厂模式来创建产品。我为产品创建了一个接口 类 来实现和设计一个工厂,该工厂将根据传递给它的参数生成产品。

界面

interface Product
{
  public function getName();
}

产品

class ProductA implements Product{
  public function getName()
  {
    return 'A'; 
  }
}

class ProductB implements Product{
  public function getName()
  {
    return 'B'; 
  }
  public function SpecialMethod()
  {
    return 'only B';
  }
}

工厂

class Factory(){
  public function getProduct($type)
  {
    switch ($type) {
      case 'A':
        return new ProductA();
        break;

      case 'B':
        return new ProductA();
        break;

      default:
        # code...
        break;
    }
  }
}

用法

function someBussinessLogic($type)
{

  // ...ignore...

  $factory = new Factory;
  $product = $factory->getProduct($type);

  if ($type == 'B') {
    $product->SpecialMethod();
  }

  // ...ignore...
}

在用法中,我必须检查$type是否是'B'。我认为这种行为有点奇怪。我应该这样做还是把 SpecialMethod 放到 interface Product?或者有什么更好的方法吗?


[编辑]

是的,我担心将来会有产品C、D、E...,每个产品都有自己的特殊方法。如果我把所有特殊的方法都放在接口上,接口会变得很大,其他产品都需要实现无意义的方法。

我会说这取决于。如果您打算随着时间的推移将此方法添加到更多 classes,请选择接口。如果有一些 classes,我不认为这是不好的做法,它们仍然必须实现这个方法但有一个空的主体。这样你就可以摆脱你的 if 而不必回到你的代码,如果 class C 也会实现这个方法。

如果只是这一种特殊情况,我(个人)会像您已经拥有的那样去寻找。


或者您可以在调用 someBusinessLogic() 的地方调用特殊方法,因为在该语句之后没有任何内容。我的意思是:

someBusinessLogic($type)->specialMethod();

如果这种方法适用于您现有的代码,我强烈建议您采用这种方式而不是 if。

我会这样做

$product = $factory->getProduct($type);

if (is_a($product, ProductB::class)) {
   $product->SpecialMethod();
}

或者如果您有许多 class 带有 SpecialMethod 的接口,那么您可以检查接口而不是单个 class.

供参考is_a

Checks if the given object is of this class or has this class as one of its parents.

您也可以使用instanceOf

if ($product instanceof ProductB::class) {
   $product->SpecialMethod();
}

这里有一个有用的post关于两者之间的区别

What is the difference between is_a and instanceof?

还有

我也没看出这有什么本质上的错误

interface ProductInterface
{
    public function getName();
    public function SpecialMethod();
}

不过我还要在其中添加一个摘要 class。

abstract class AbstractProduct impliments ProductInterface{
    abstract public function getName();
    public function SpecialMethod()
    {
        return ''; //or false
    }
}

class ProductA extends AbstractProduct {
    public function getName()
    {
        return 'A'; 
    }
}

class ProductB extends AbstractProduct {
    public function getName()
    {
        return 'A'; 
    }
    //override the concrete method in abstract class
    public function SpecialMethod()
    {
        return 'only B'; 
    }
}

这样您就可以在其他 class 中将该方法留空,只要您不依赖 SpecialMethod 获取某种类型的 return 值。我也会像我一样修改名称,但这只是我的偏好。

那么你可以这样做

$product = $factory->getProduct($type);
$product->SpecialMethod();  //return "only B" or ""

因为大多数 classes 将继承抽象 class 中的空方法,只有那些实现其覆盖的产品才会有所作为。如果多个产品都具有该方法,这是有道理的,但制作一个单独的界面并检查它也很有意义。所以它更多地取决于 SpecialMethod 实际做什么的细节。

工厂模式:对维基百科来说,工厂方法模式是一种创建模式,它使用工厂方法来处理创建对象的问题,而不必指定将要创建的对象的确切 class。 这里你提到的问题是调用 $product->SpecialMethod();

之前的检查如果我们通过 class 实现很明显ProductB只需要specialMethod。所以在 someBussinessLogic($type) 中,不管代码设计如何,实际上你只想在 [= 的情况下调用 specialMethod 12=]typeB,因为在其他情况下没有 specialMethod 功能(例如为什么要调用 specialMethod所有的情况?)。所以你自动需要一张支票。
另一方面,如果您正在编写通用代码,您的实体不应该有特定的行为。如果有,您应该将它们从通用代码中插入,而不是在通用代码中进行检查。

现在,由于您将不得不明确地处理此类检查,您可能想知道工厂模式如何帮助您?。好吧,当您想更改特定类型的功能时,工厂模式会大放异彩,您将有一个点,可以从那里插入 class 并插入具有新功能的更新后的 class .

底线是,如果您想在通用代码中处理特定行为,如上述情况,您 必须在代码中的某处写一个检查。最好在通用模块之外,而不是损害通用代码的完整性。