在构造函数中初始化数据时,将逻辑与类分开

时间:2014-12-15 13:36:20

标签: php oop dependency-injection single-responsibility-principle

如果我有Car这样的课程:

class Car
{
    /**
     * @var string $model
     */
    private $model;

    /**
     * Makes a new car based on a model
     *
     * @param string $model Initializes the model
     */
    public function __construct($model) {
        $this->model = $model;
    }

    /**
     * Gets the car model
     *
     * @return string The model
     */
    public function getModel() {
        return $this->model;
    }
}

这样CarValidator

class CarValidator
{
    public function isValidated(Car $car) {
        if (empty($car->getModel())) {
            return false;
        }
        return true;
    }
}

我的用法是这样的:

$bmw = new Car('bmw');

如何在实例化之前验证我的宝马汽车?

1 个答案:

答案 0 :(得分:0)

我认为你有两个选择。

  • 您停止构建无效汽车,在这种情况下,有效汽车的规则必须是汽车类的一部分
  • 您使用工厂创建汽车,工厂代表CarValidator以在施工后验证汽车。这意味着验证是独立的(这似乎是你重视的东西),但意味着汽车可以建立在无效状态。如果验证是上下文的并且您的汽车被重复使用并且“有效”意味着某个地方有不同的东西,那么这可能没问题。

其中哪一个最适用我认为从根本上说,它归结为在您的情况下“有效”以及有效性是否普遍适用。事实可能意味着两者兼而有之。

例如,对于所有汽车而言,拥有空模型可能无效,但目前某些模型颜色组合无效。在这种情况下,如果有人试图创建一个具有模型空字符串的汽车,那么您的汽车构造函数将抛出异常,因为这永远不会有效。

您的验证员可能会在施工后检查汽车是否有效,以确保您当前存在指定的型号/颜色组合。这些规则可能会发生变化,因此更适用于外部验证(您可能在某些国家/地区使用不同的可接受模型,或者根据其他一些因素使用不同的验证规则)。这些可以存在于您的CarValidator班级中。

你的对象的任何基本不变量,你的程序的某些部分将依赖它(比如模型不为空的事实,应该由对象本身验证和强制执行,因为这些不是'业务规则'本身但是系统的不变量。

我可能会选择:

public class CarFactory
{
     private ICarValidator validator;
     public CarFactory(ICarValidator validator){ this.validator=validator;}

     public Car Create(string model)
     {
          Car car = new Car(model);
          if (validator.IsValid(car))
              return car;
          throw new InvalidCarException(car);
     }
}

public Car
{
    private string model;
    public Car(string model)
    {
         if (model=="")
            throw new EmptyModelException();
         this.model=model;
    }
}

这使您可以从业务逻辑(封装在Car中)中分离出不变量(由CarValidator强制执行),并禁止用户获取无效的汽车实例。