如何在PHP中自动绑定实现?

时间:2016-08-31 09:49:53

标签: php laravel design-patterns interface-implementation

所以我是一名经验丰富的开发人员,即使我已经使用了一段时间了,我也很喜欢魔法如何在表面下发生,它是如何在通过IoC容器实例化类时自动绑定实现的,现在我是试图了解设计模式的基础知识,并了解事情的实际运作方式。

所以我开始使用Animal示例:

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,我正在努力充分利用这本书。此时每次创建一个新的Animal时,我都必须实现make声音方法,这种方法在大多数情况下会有所不同。

所以这本书告诉我,我应该编写一个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,因为它会咆哮,我不想每次实例化一个新的时都写它{ {1}}对象。

那么我如何使用Laravel在实例化Dog时自动传递Bark对象的类似魔法。

PS:我还在学习,所以在理解这些原则的同时,我可能会走错路。如果你知道的话,请指导我。

4 个答案:

答案 0 :(得分:2)

首先,小记:这本书错了。该界面应称为AudibleVocal。 “合理”不是一个真正的词,作者应该感到尴尬。另外,调用与接口同名的变量有点不好。

另一件事是:Laravel的IoC实际上只是一个美化的服务定位器,所以它在这里不会对你有所帮助。

通常,您有两种选择:

  • 使用工厂(在这种情况下会很痛苦和/或棘手)
  • 使用依赖注入容器 - 最好是反射基容器

在这种情况下,我倾向于推荐Auryn。但是,如果你愿意通过有限的配置跳出一些额外的箍,你也可以使用Symfony's DIC

如果您使用的是Auryn,那么您的狗和猫的初始化就是:

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

库本身会查找Dog构造函数的反映,并检测它还需要创建一个新的Bark实例。

答案 1 :(得分:1)

您可以使用工厂方法创建简单的动物工厂(听起来很奇怪)。

工厂

  

通常实施工厂类,因为它们允许项目更紧密地遵循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();

答案 2 :(得分:0)

我认为在这里使用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时自动对象。

Dependency Injection Container将满足您的需求

答案 3 :(得分:-1)

我不知道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”),它仍然有用,但它仍然有用。