Класс A
зависит от
класса B
Двери с электронным замком и кнопка открытия
public function actionOpen() {
$result = false;
$canBeOpened = HourSettings::find()->select('can_be_opened')
->where(['hour' => date('H')])->scalar();
if ($canBeOpened) {
$fd = dio_open('/dev/ttyS0', O_RDWR | O_NOCTTY | O_NONBLOCK);
dio_fcntl($fd, F_SETFL, O_SYNC);
dio_tcsetattr($fd, ['baud' => 9600, 'bits' => 8]);
dio_write($fd, "OPEN\n");
$result = dio_read($fd, 3) === "1\n";
dio_close($fd);
}
return $this->render('open', ['result' => $result]);
}
class DoorManager
{
public function open()
{
$door = new Door();
return $door->open();
}
}
public function actionOpen()
{
$doorManager = new DoorManager();
return $doorManager->open();
}
actionOpen() зависит от DoorManager
DoorManager зависит от Door
Выдать всем RFID и сделать логирование!
class DoorManager
{
public function open(RfidKey $rfid)
{
$door = new Door();
$log = new EmailAccessLogger('alert@superdoors.com');
$auth = new RfidAuthenticator($rfid, [
'users' => User::find()->where(['active' => 1])->all()
]);
if ($auth->canOpenDoor($door)) {
$log->accessGranted($door, $rfid);
return $door->open();
} else {
$log->accessDenied($door, $rfid);
return false;
}
}
}
Win:
|
Lose:
|
class DoorManager {
private $door; // key, auth, log
public function __construct(RfidKey $key) {
$this->key = $key;
$this->door = new Door();
$this->log = new EmailAccessLogger('alert@superdoors.com');
$this->auth = new RfidAuthenticator($this->key, [
'users' => User::find()->where(['active' => 1])->all()
]);
}
public function open() {
if ($this->auth->canOpenDoor($this->door)) {
$this->log->accessGranted($this->door, $this->rfid);
$this->door->open();
} else {
$this->log->accessDenied($this->door, $this->rfid);
}
}
public function close() { } // implementation
}
Win:
|
Lose:
|
if внутри объекта Door()if перед созданием объекта Door()master и magnet-door-masterMagnetDoorManagerInversion of Control — принцип объектно-ориентированного программирования, используемый для уменьшения связанности в компьютерных программах
interface DoorInterface {
public function open();
public function close();
}
class Door implements DoorInterface {
public function open() { } // implementation
public function close() { } // implementation
}
class MagnetDoor implements DoorInterface {
public function open() { } // implementation
public function close() { } // implementation
}
class FakeDoor implements DoorInterface { // for testing purpose
public function open() { return true; }
public function close() { return true; }
}
abstract class AbstractDoorFactory
{
/** @return DoorInterface */
abstract public function build();
}
class DoorFactory extends AbstractDoorFactory
{
public function build() {
return new Door();
}
}
class MagnetDoorFactory extends AbstractDoorFactory
{
public function build() {
return new MagnetDoor();
}
}
class DoorManager {
private $door; // key, auth, log
public function __construct(DoorInterface $door, RfidKey $key) {
$this->door = $door;
$this->key = $key;
$this->log = new EmailAccessLogger('alert@superdoors.com');
$this->auth = new RfidAuthenticator($this->key, [
'users' => User::find()->where(['active' => 1])->all()
]);
}
}
public function actionOpen($keySecret)
{
$key = new RfidKey($keySecret);
$door = (new MagnetDoorFactory())->build();
$doorManager = new DoorManager($door, $key);
return $doorManager->open();
}
Win:
|
Lose:
|
Dependency Injection — это набор практик, который помогает строить приложения с низким уровнем связанности между компонентами.
это НЕ только:
|
это:
|
class DoorManager {
private $door;
private $key;
private $auth;
private $log;
public function __construct(DoorInterface $door) { }
public function setKey(KeyInterface $key) { }
public function setAuth(AuthManagerInterface $auth) { }
public function setLog(LoggerInterface $log) { }
}
public function actionOpen($keySecret)
{
$door = $this->getDoor();
$doorManager = new DoorManager($door);
$doorManager->setKey(new RfidKey($keySecret));
// ...
return $doorManager->open();
}
Win:
|
Lose:
|
public function open()
{
if ($this->log !== null) {
$this->log->accessGranted($this->door, $this->key);
}
}
class FakeLogger implements LoggerInterface
{
public function accessGranted(DoorInterface $door, KeyInterface $key)
{
return true;
}
}
class DoorManager {
private $door; // key, auth, log
public function __construct(
KeyInterface $key, DoorInterface $door,
AuthManagerInterface $auth, LoggerInterface $log,
) {
$this->door = $door; // key, auth, log
}
}
Win:
|
Lose:
|
public function actionOpen($keySecret)
{
$key = new RfidKey($keySecret);
$door = new MagnetDoor('some-door-id');
$users = User::find()->active()->all();
$auth = new RfidAuthenticator($key, $users);
$log = new EmailAccessLogger('alert@superdoors.com');
$doorManager = new DoorManager($key, $door, $auth, $log);
return $doorManager->open();
}
actionOpenactionClose?...Dependency Injection Container — класс, управляющий созданием объектов других классов.
actionOpen()
public function actionOpen($keySecret)
{
$key = new RfidKey($keySecret);
$door = new MagnetDoor('some-door-id');
$users = User::find()->active()->all();
$auth = new RfidAuthenticator($key, $users);
$log = new EmailAccessLogger('alert@superdoors.com');
$doorManager = new DoorManager($key, $door, $auth, $log);
return $doorManager->open();
}
class Container {
/** @return KeyInterface */
public function getKey($keySecret) {
return new RfidKey($keySecret);
}
/** @return AuthManagerInterface */
public function getAuthenticator($key, $users) {
return new RfidAuthenticator($key, $users);
}
/** @return LoggerInterface */
public function getLogger() {
return new EmailAccessLogger('alert@superdoors.com');
}
/** @return DoorInterface */
public function getDoor() {
return new MagnetDoor('some-door-id');
}
/** @return DoorManagerInterface */
public function getDoorManager($keySecret, $users) {
$key = $this->getKey($keySecret);
$door = $this->getDoor();
$auth = $this->getAuthenticator($key, $users);
$logger = $this->getLogger();
return new DoorManager($key, $door, $auth, $logger);
}
}
public function actionOpen($keySecret)
{
$container = new Container();
$users = User::find()->active()->all();
$doorManager = $container->getDoorManager($keySecret, $users);
return $doorManager->open();
}
Win:
|
Lose:
|
Зачем мы каждый раз создаём объекты, которые не изменятся в контексте запроса?...
class Container {
private $logger;
/** @return LoggerInterface */
public function getLogger()
{
if (!isset($this->logger)) {
$this->logger = new EmailAccessLogger('alert@superdoors.com')
}
return $this->logger;
}
}
}
Используя Service Locator, мы явно запрашиваем зависимости и зависимы от самого локатора.
Используя DiC, что-то даёт зависимости в конструктор и нам всё равно, что это.
function getDependencies($class)
{
$dependencies = [];
$reflection = new ReflectionClass($class);
$constructor = $reflection->getConstructor();
if ($constructor !== null) {
foreach ($constructor->getParameters() as $param) {
$c = $param->getClass();
if ($c instanceof \ReflectionClass) {
$dependencies[] = $c->getName();
}
}
}
return $dependencies;
}
Конфигурируем DiC
$c = Yii::$container;
// interface to concrete implementation
$c->set(KeyInterface::class, RfidKey::class);
$c->set(AuthManagerInterface::class, RfidAuthenticator::class);
// interface to component from Service Locator
$c->set(LoggerInterface::class, function () {
return Yii::$app->get('log');
});
$c->set(DoorInterface::class, function () {
return Yii::$app->get('door');
});
// interface to concrete implementation - complex initialisation
$c->set(DoorManagerInterface::class, function ($cont, $params, $config) {
// DoorManager constructor params order:
// 0 - Key, 1 - Door, 2 - Auth, 3 - Logger
$params[2] = $cont->get(AuthManagerInterface::class, [$params[0]]);
return $cont->get(DoorManager::class, $params, $config);
});
public function actionOpen($keySecret)
{
$container = $this->container;
$key = $container->get(KeyInterface::class, [$keySecret]);
$doorManager = $container->get(DoorManagerInterface::class, [$key]);
return $doorManager->open();
}
Win:
|
Lose:
|