MVC,DI(依赖注入)并从Controller创建Model实例 [英] MVC, DI (dependency injection) and creating Model instance from Controller

查看:233
本文介绍了MVC,DI(依赖注入)并从Controller创建Model实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的调度程序正在选择"正确的控制器;然后创建Controller的实例(DependencyInjectionContainer传递给Controller构造函数);然后调用某些Controller的方法...

class UserController extends Controller
{

  public function __construct(DependencyInjectionContainer $injection) {
    $this->container = $injection;
  }

  public function detailsAction() {
    ...
  }

}

DependencyInjectionContainer包含数据库适配器对象,配置对象等. 现在让我们看看detailsAction()方法包含什么...

public function detailsAction() {

  $model = new UserModel();
  $model->getDetails(12345);

}

如您所见,我正在创建UserModel的新实例并调用getDetails方法. 模型的getDetails()方法应连接到db以获取有关用户的信息.要连接到数据库,UserModel应该能够访问数据库适配器.

将DependencyInjectionContainer传递给UserModel的正确方法是什么? 我认为这种方式是错误的...

public function detailsAction() {

  $model = new UserModel($this->container);
  $model->getDetails(12345);

}

解决方案

您应该仅注入所需的依赖项,而不是将整个DI容器注入类中.

您的UserController需要一个数据库适配器(我们将此接口称为IDBAdapter).在C#中,它可能看起来像这样:

public class UserController
{
    private readonly IDBAdapter db;

    public UserController(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public void DetailsAction()
    {
        var model = new UserModel(this.db);
        model.GetDetails(12345);
    }
}

在这种情况下,我们将依赖项注入到UserModel中.但是,在大多数情况下,如果UserController仅采用依赖关系将其传递给我,我倾向于将其视为DI气味,因此,更好的方法可能是让UserController依赖于这样的Abstract Factory依赖关系:

public interface IUserModelFactory
{
    UserModel Create();
}

在此变体中,UserController可能看起来像这样:

public class UserController
{
    private readonly IUserModelFactory factory;

    public UserController(IUserModelFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException("factory");
        }

        this.factory = factory;
    }

    public void DetailsAction()
    {
        var model = this.factory.Create();
        model.GetDetails(12345);
    }
}

您可以定义一个具体的UserModelFactory,它依赖于IDBAdapter:

public class UserModelFactory : IUserModelFactory
{
    private readonly IDBAdapter db;

    public UserModelFactory(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public UserModel Create()
    {
        return new UserModel(this.db);
    }
}

这可以使您更好地分离关注点.

如果需要多个依赖项,则只需通过构造函数注入它们.当您开始遇到太多问题时,这表明您正在违反单一责任原则,并且是时候重构为聚合服务.

My Dispatcher is "choosing" correct Controller; then creating Controller's instance (DependencyInjectionContainer is passed to Controller constructor); then calling some Controller's method...

class UserController extends Controller
{

  public function __construct(DependencyInjectionContainer $injection) {
    $this->container = $injection;
  }

  public function detailsAction() {
    ...
  }

}

DependencyInjectionContainer contains DB adapter object, Config object etc. Now let's see what detailsAction() method contains...

public function detailsAction() {

  $model = new UserModel();
  $model->getDetails(12345);

}

As you see I'm creating new instance of UserModel and calling getDetails methods. Model's getDetails() method should connect to db to get information about user. To connect to DB UserModel should be able to access DB adapter.

What is the right way to pass DependencyInjectionContainer to the UserModel? I think that this way is wrong...

public function detailsAction() {

  $model = new UserModel($this->container);
  $model->getDetails(12345);

}

解决方案

Instead of injecting the entire DI Container into your classes, you should inject only the dependencies you need.

Your UserController requires a DB Adapter (let's call this interface IDBAdapter). In C# this might look like this:

public class UserController
{
    private readonly IDBAdapter db;

    public UserController(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public void DetailsAction()
    {
        var model = new UserModel(this.db);
        model.GetDetails(12345);
    }
}

In this case we are injectiing the dependency into the UserModel. In most cases, however, I would tend to consider it a DI smell if the UserController only takes a dependency to pass it on, so a better approach might be for the UserController to take a dependency on an Abstract Factory like this one:

public interface IUserModelFactory
{
    UserModel Create();
}

In this variation, the UserController might look like this:

public class UserController
{
    private readonly IUserModelFactory factory;

    public UserController(IUserModelFactory factory)
    {
        if (factory == null)
        {
            throw new ArgumentNullException("factory");
        }

        this.factory = factory;
    }

    public void DetailsAction()
    {
        var model = this.factory.Create();
        model.GetDetails(12345);
    }
}

and you could define a concrete UserModelFactory that takes a dependency on IDBAdapter:

public class UserModelFactory : IUserModelFactory
{
    private readonly IDBAdapter db;

    public UserModelFactory(IDBAdapter db)
    {
        if (db == null)
        {
            throw new ArgumentNullException("db");
        }

        this.db = db;
    }

    public UserModel Create()
    {
        return new UserModel(this.db);
    }
}

This gives you better separation of concerns.

If you need more than one dependency, you just inject them through the constructor. When you start to get too many, it's a sign that you are violating the Single Responsibility Principle, and it's time to refactor to Aggregate Services.

这篇关于MVC,DI(依赖注入)并从Controller创建Model实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆