Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
class DependedClass {
public function __construct(Time $time) {
// ...
}
}class TimeFactory {
public function create() {
$result = Time::fromValues(0, 0);
return $result;
}
}
class DependedClass {
public function __construct(TimeFactory $factory) {
// ...
}
}которые и тестировать-то нет неообходимости
(«объектов значений» по вашей терминологии)
Меня интересовало, что будет инжектиться в класс, завязанный на класс Time, если его конструктор недоступен:
Он не имеет какого-то сложного поведения, которое можно было бы использовать — вся его ценность в тех данных, которыми он наполнен.
В чем вообще смысл создания таких объектов DI-фреймворком автоматически?
Если сервис Х имеет зависимость от обьекта Time
public function __construct(Foo $foo, Bar $bar, int $defaultTTL) {
// ...
$this->defaultTTL = Time::create($defaultTTL);
}tl; dr — Не ограничивай себя одним конструктором в классе Time. Используй статические фабричные методы.
use Symfony\Component\DependencyInjection\ContainerBuilder;
$container = new ContainerBuilder();
$container->register('time', Time::class);
$obj = $container->get('time');PHP Fatal error: Uncaught exception 'ReflectionException' with message 'Access to non-public constructor of class ...\Time // Не удаляем пустой конструктор, т.к. это защитит нас от возможности создать объект извне
private function __construct()
{
}$container = new ContainerBuilder();
$container->setDefinition('time', (new Definition())->setFactory('Time::create'));
$obj = $container->get('time');use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
$container = new ContainerBuilder();
// $container->register('time', Time::class);
$container->setDefinition('time', (new Definition())->setFactory(Time::class . '::fromValues'));
// $obj = Time::fromValues(2, 3);
$obj = $container->get('time');PHP Warning: Missing argument 1 for ...\Time::fromValues()Да и обращение к приватным полям объекта извне этого объекта является больше хаком и не факт что так будет работать всегда.
Если такое поведение интерпретатора поменяется, то и код перестанет работать.
в контексте класса
<?php
class A
{
private $a;
}
class B extends A
{
private $a;
public static function create($value)
{
$instance = new static;
$instance->a = $value;
return $instance;
}
}
$object = B::create(42);
var_dump($object);
/*
object(B)#1 (2) {
["a":"B":private]=>
int(42)
["a":"A":private]=>
NULL
}
*/И вы никогда не можете быть уверенными на 100%
и я не вижу смысла закрывать наследование
p.s. в java protected действует на namespace
Но к чему это приведёт? Да скорее всего к copy-paste..
final.protected свойства — это вообще кастыль, и нужно 10 раз подумать прежде чем делать что-то protected и уж тем более public.
какого фига джависты всегда фигачат private и добавляют getField\setField, когда можно просто сделать поле публичным.
public function chagePassword(string $password) {
$this->password = $password;
}друзья похапешиники пошли по той же тропе джавы и зачастую переусложняют интерфейсы
Может стоит иногда поплёвывать на важность инкапсуляции
Я в сомнениях, даже обычный POPO объект, той же самой доктрины может превратиться в 200-строчный класс, состоящий из одних полей + get\set
мне кажется, что вопрос правильного создания модели не должен лежать в зоне отвественности этой самой модели
class Buyer {
// ...
private function __construct(string $email, string $password, string $type) {
$this->email = $email;
$this->password = $password;
$this->type = $type;
}
public static function register(string $email, string $password) : Buyer {
return new static($email, $password, static::TYPE_REGULAR);
}
public static function registerAsWholesaler(string $email, string $password, string $phone, string $arr) : Buyer{
$buyer = new static($email, $password, static::TYPE_WHOLESALER);
$buyer->phone = $phone;
$buyer->arr = $arr;
return $buyer;
}
}<?php
class Buyer
{
// ...
private function __construct(string $email, string $password, string $type)
{
$this->email = $email;
$this->password = $password;
$this->type = $type;
}
public static function register(string $email, string $password) : Buyer
{
return new static($email, $password, static::TYPE_REGULAR);
}
public static function registerAsWholesaler(string $email, string $password, string $phone, string $arr) : Buyer
{
$buyer = new static($email, $password, static::TYPE_WHOLESALER);
$buyer->phone = $phone;
$buyer->arr = $arr;
return $buyer;
}
}мне кажется, что вопрос правильного создания модели не должен лежать в зоне отвественности этой самой модели.
А вопрос создания объекта делегировать на более высокий уровень.
Допустим у Вас в системе могут появлятся пользователи из формы регистрации на вашем сайте, а могут быть импортированы из системы партнера.
Вы можете создать класс-менеджер, который будет уметь привести данные с формы/с csv/с запроса на апи в общий вид и создать объект пользователя.
Вот тут вполне пригодятся легковесные моки этих же классов.
Но, внезапно оказывается, что у сущности не 2-3 поля, а несколько десятков, а то и сотен.
сущность отчет, которая содержит сотни собранных метрик.
подождать одну секунду или 10 — небольшая разница
у менеджера всего несколько методов, которые возвращают нужный тип сущност
$reportManager->getReport(PremiumPurchasesSpecification::new());$reportManager замокать. Но вот если у нас есть метод$userManager->register($userRegistrationDTO);add() с аргументом определенного типа.
Во-первых в PHP есть DateTime
DateRange::fromString('within 5 days');
DateRange::week();
DateRange::between(new DateTime(), new DateTime('+5 days'));Лучше пусть конвертер будет отдельно от класса Time
$date = Carbon::parse($someStringWithDateTime);Но переусложненный конструктор и статики для «testing aids» мне все равно не нравятся.
предпочитаю ограничиваться DateTimeImmutable.
конвертер будет отдельно от класса Time, и его можно будет отдельно редактировать, добавлять новые форматы.
Как использовать именованные конструкторы в PHP