Как автоматически связать реализации в PHP?

Так что я — разработчик laravel, и хотя я некоторое время работал с ним, и мне нравится, как магия происходит под поверхностью, как она автоматически связывает реализации при создании экземпляров классов через контейнер IoC, сейчас я пытаюсь пойти основам шаблонов дизайна и узнать, как все работает на самом деле.

Итак, я начал с примера с животными:

abstract class Animal
{
abstract function makeSound();
}

class Dog extends Animal
{
public function makeSound()
{
echo "Bark!\n";
}
}

class Cat extends Animal
{
public function makeSound()
{
echo "Bark!\n";
}
}

Итак, я читаю Head First Design Patterns и пытаюсь максимально использовать книгу. На этом этапе каждый раз, когда я создаю новое животное, мне нужно будет реализовать метод make sound, который будет отличаться в большинстве случаев.

Таким образом, книга говорит мне, что я должен кодировать интерфейс Soundable, а затем иметь реализации этого интерфейса в расширенных классах Animal.

Я наконец-то придумал что-то вроде этого:

interface Soundable
{
function sound();
}

class Bark implements Soundable
{
public function sound()
{
return "Bark!\n";
}
}

class Meow implements Soundable
{
public function sound()
{
return "Meow!\n";
}
}

class Animal
{
public $soundable;

public function __construct(Soundable $soundable)
{
$this->soundable = $soundable;
}

public function makeSound()
{
echo $this->soundable->sound();
}
}

class Dog extends Animal
{

}

class Cat extends Animal
{

}

function makeAnimal(Animal $animal){
return $animal;
}

// Making a dog
$animal = makeAnimal(new Dog(new Bark()));
$animal->makeSound();

// Making a cat
$animal = makeAnimal(new Cat(new Meow()));
$animal->makeSound();

Так что теперь, когда у меня есть другое животное, которое лает или мяукает, я могу просто реализовать эту реализацию во время создания животного.

Теперь мой вопрос заключается в том, как сказать PHP, чтобы автоматически передавать new Bark() в то время как создание экземпляров Dog класс, так как он будет лаять, и я не хочу писать это каждый раз, когда я создаю новый экземпляр Dog объект.

Итак, как мне использовать подобную магию, которую использует Laravel для автоматической передачи объекта Bark при создании экземпляра Dog.

PS: я все еще учусь, поэтому, возможно, я пойду в неправильном направлении, понимая эти принципы. Пожалуйста, ведите меня, если вы знаете лучше.

3

Решение

Прежде всего, небольшая заметка: книга не права. Интерфейс должен называться Audible или же Vocal, «Звук» не является реальным словом, и автор должен быть смущен. Кроме того, вызывать переменную с тем же именем, что и интерфейс, довольно плохо.

Другое дело: IoC Laravel на самом деле просто прославленный локатор сервисов, так что он вам здесь не поможет.

Обычно у вас есть два варианта:

  • использовать фабрику (которая в данном конкретном случае будет болезненной и / или хитрой)
  • используйте контейнер для инъекций зависимости — предпочтительно рефлексный

Я склонен рекомендовать Auryn в этом случае. Хотя, если вы хотите перепрыгнуть несколько дополнительных обручей и испытываете трудности из-за ограниченной конфигурации, вы также можете использовать DIC Symfony.

Если бы вы использовали Auryn, инициализация вашей собаки и кошки была бы просто:

$injector = new Auryn\Injector;
$dog = $injector->make('Dog');
$cat = $injector->make('Cat');

Библиотека будет самостоятельно искать отражение конструктора для Dog и обнаружить, что ему также нужно будет создать новый Bark пример.

2

Другие решения

Вы можете просто создать фабрику животных (звучит странно) с помощью фабричных методов.

Фабрики

Фабричные классы часто реализуются, потому что они позволяют проекту более точно следовать принципам SOLID. В частности, принципы разделения интерфейсов и инверсии зависимостей.

Для получения дополнительной информации смотрите здесь https://www.sitepoint.com/understanding-the-factory-method-design-pattern/
Просто помните, что если вы хотите записать какой-то модульный тест, не используйте статические методы, просто создайте экземпляр фабрики, в будущем может потребоваться создание фабрики с некоторыми зависимостями.

<?php
class AnimalFactory
{
public function createDog() : Dog
{
return new Dog(new Bark());
}
}

$factory = new AnimalFactory();
$dog = $factory->createDog();
1

Я думаю, что использование IoC здесь было бы хорошим решением.

От Laravel Docs (Контекстная привязка)

Иногда у вас может быть два класса, которые используют один и тот же интерфейс, но вы хотите внедрить разные реализации в каждый класс. Например, когда наша система получает новый Заказ, мы можем захотеть отправить событие через PubNub, а не Pusher. Laravel предоставляет простой и удобный интерфейс для определения этого поведения:

$this->app->when('App\Handlers\Commands\CreateOrderHandler')
->needs('App\Contracts\EventPusher')
->give('App\Services\PubNubEventPusher');

Таким образом, ваш поставщик услуг в Laravel может выглядеть так

public function register()
{
$this->app->when(Dog::class)->needs(Soundable::class)->give(Bark::class);
$this->app->when(Cat::class)->needs(Soundable::class)->give(Meow::class);
}

И тогда вы можете создать экземпляр своего класса, используя контейнер для инъекций зависимостей Laravel

$dog = app()->make(Dog::class);
$dog->sound(); // Bark!
$cat = app()->make(Cat::class);
$cat->sound(); // Meow!

Подводя итоги и относящиеся к вашему вопросу:

Итак, как я могу использовать подобную магию, которую использует Laravel, чтобы пройти кору
объект автоматически при создании экземпляра Dog.

Контейнер для инъекций зависимостей будет соответствовать вашим потребностям

0

Я не знаю laravel, но, по-моему, ваш код должен был выглядеть так:

interface Soundable
{
function sound();
}

class Animal
{
protected name;

public function __construct($name) {
$this->name=name;
}

public function get_name() { return $this->name; }
}

class Dog extends Animal implements Soundable
{
public function sound()
{
return "Bark!\n";
}
}

class Cat extends Animal implements Soundable
{
public function sound()
{
return "Meow!\n";
}
}

// Making a dog
$animal = new Dog("Fido");
$animal->sound();

// Making a cat
$animal = new Cat("Fuffi");
$animal->sound();
echo $animal->get_name(); // print "Fuffi"

Итак, вы можете видеть, что если вы хотите реализовать только интерфейс, вам даже не нужен класс Animal. Фактически, я установил это только для того, чтобы показать, что он все еще может быть полезен для реализации некоторого метода, который может быть полезен для всех нисходящих классов (например, get_name, который возвращает защищенный атрибут «name»).

-1
По вопросам рекламы [email protected]