问题
I've been doing MVC for several months now, and I store everything in my $registry object. When I create a new class, I only ever pass the registry usually, but I'm having to constantly pass the $this->registry
when creating a new class.
e.g.
class something
{
public function __construct($registry)
{
$this->registry = registry;
$this->db = $registry->db;
$this->user = $registry->user; // ......
}
public function something()
{
$class = new something_class($this->registry);
$class->do();
}
}
class something_class
{
public function __construct($registry)
{
$this->registry = $registry;
}
public function do()
{
echo 'Doing something ....';
}
}
My question is, how can I handle the passing of the registry to the new class behind the scenes (in this case when instantiating something_class) inside the registry class somehow? I'm absolutely convinced there is an easy way to do this, but I can't find anything related anywhere to what I'm looking for.
Here is my registry class:
<?php
class registry
{
protected $vars = array();
public function &__set($index, $value)
{
$this->vars[$index] = $value;
return $value;
}
public function &__get($index)
{
return $this->vars[$index];
}
}
回答1:
This is all wrong. "Registry" is an anti-patter and what you are doing the is not dependency injection. You have found a way to fake global variables .. that's it.
As a start, please watch this lecture.
As for, how to correctly do what you want, there are two ways:
- use a factory, that creates a class, using dependencies that you provided
- use a dependency injection container, Auryn
To learn how you use a DI container, you will just have to consult the documentation. But I will explain the basics of factory, which is more of a DIY approach
A factory is an object, that is responsible for initializing other class. For example, you have a large set of classes, which require PDO
as a dependency.
class Factory
{
private $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function create($name) {
return new $name($this->pdo);
}
}
If you use an instance of this class, it would let you create objects, which have the PDO already passed in as a dependency in a constructor:
$factory = new Factory(PDO($dsn, $user, $pass));
$user = $factory->create('User');
$document = $factory->create('Doc');
And as an added benefit, this setup would let bot the User
class instance and the Doc
class instance to share the same PDO object.
回答2:
It's best practice to have your classes or components not be dependent on your container. Let your container do the injection of dependencies.
Here's an example that uses Container from The League of Extraordinary Packages and shows class B being dependent on A:
<?php
$container = new League\Container\Container();
$container->add('b', function () {
$a = new A('foo');
$b = new B($a);
return $b;
});
var_dump($container->get('b')->getValueFromA()); // Outputs "foo"
class A
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
}
class B
{
public function __construct(A $a)
{
$this->a = $a;
}
public function getValueFromA()
{
return $this->a->getValue();
}
}
Each class you write should only be coupled to its direct dependencies. If possible, the use of interfaces is highly recommended for further decoupling.
Please see this Stackoverflow question for more information around the differences between a registry and a DI container. Using a registry is considered an anti-pattern and should be avoided.
回答3:
Your $registry
class is what's called a Service Locator pattern. It is useful in some contexts, but it has a high potential for being abused, especially when you inject the service locator into your class.
Dependency Injection is supposed to expose (show) all the objects that your class uses (or depends on). Injecting $registry
in your class instead hides the dependencies, and this is why it is an anti-pattern. Also, it creates a burden for you as you have to pass it everywhere, you might as well make it a global to make it easier (this will also answer your question). But better tools are available.
Consider your class like so:
- Note the use of dependency injection of $db and $user into constructor of
something
class. Now it is more clear what your class needs to work. - Also note the use of dependency injection container in this case Auryn to create a class for you.
//in your bootstrap or part of your framework
$injector = new Auryn\Injector();
$class = $injector->make('something_class');
//Your own code:
class something
{
public function __construct(Database $db, User $user)
{
$this->db = $db;
$this->user = $user;
}
public function something(something_class $class)
{
$class->do();
}
}
You can also use Auryn to do what you want to do to call the Registry class. But you will soon find that Auryn is a better more robust version of your Registry class and one that forces you to use better dependency-injection techniques for Auryn to work as intended.
Basic point is that your own application (all of your application's classes) best not be aware of any container. Because what containers do is they hide dependencies from your classes. It remains however as acceptable to use containers as part of a framework. Let your framework handle containers, but you focus on your own classes, without calling containers unless those containers are used as part of your framework to wire up your application.
来源:https://stackoverflow.com/questions/39246777/how-to-avoid-manually-passing-my-registry-container-into-constructor-of-every-n