Если у меня есть 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');
Как мне проверить мой автомобиль BMW перед его созданием?
На мой взгляд, у вас есть 2 варианта.
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
) и запрещает вашим пользователям получать недействительные экземпляры автомобиля вообще.
Других решений пока нет …