【问题标题】:How to prevent constructor misuse in c# class如何防止 c# 类中的构造函数滥用
【发布时间】:2015-04-21 02:49:42
【问题描述】:

我一直在尝试在 asp.net MVC5 应用程序中实现松散耦合的应用程序。我有一个控制器:

public class HeaderController : Controller
    {
        private IMenuService _menuService;

        public HeaderController(IMenuService menuService)
        {
            this._menuService = menuService;
        }

        //
        // GET: /Header/
        public ActionResult Index()
        {

            return View();
        }


        public ActionResult GetMenu()
        {

            MenuItem menu = this._menuService.GetMenu();
            return View("Menu", menu);

        }

    }

这个控制器中使用的服务是:

public class MenuService : IMenuService
{
    private IMenuRespository _menuRepository;

    public MenuService(IMenuRespository menuRepository)
    {
        this._menuRepository = menuRepository;
    }

    public MenuItem GetMenu()
    {
        return this._menuRepository.GetMenu();
    }
}

服务类中使用的存储库是:

public class MenuRepository : IMenuRespository
    {
        public MenuItem GetMenu()
        {
            //return the menu items
        }
    }

用于服务和存储库的接口如下:

 public interface IMenuService
    {
        MenuItem GetMenu();
    }

public interface IMenuRespository
    {
        MenuItem GetMenu();
    }

HeaderController 的构造函数使用构造函数注入接收MenuService,我使用 ninject 作为处理此问题的 DI 容器。

一切都很好 - 除了在我的控制器中,我仍然可以这样做:

MenuItem menu = new MenuService(new MenuRepository());

...破坏了架构。如何防止以这种方式使用“新”?

【问题讨论】:

  • 不要那样做?不知道为什么这比命名控制器HeaderCantroller“这会破坏架构”更成问题...您是否有特殊原因正在寻找它(例如无法通过良好的命名/代码审查来停止的特定模式)?
  • 能否不验证构造函数内部的参数,如果使用不正确则抛出异常?
  • @AlexeiLevenkov:直接使用构造函数违反了“关注点分离”并将 HeaderController 与 MenuService 和 MenuRepository 紧密耦合。
  • @RonBeyer:我该怎么做?我的意思是,如果我实例化“新”类,它仍然是正确的,因为构造函数正在获取正确的对象类型。我想防止程序员直接调用它,因为它会紧密耦合它们。

标签: c# asp.net asp.net-mvc dependency-injection inversion-of-control


【解决方案1】:

这样做的一种方法是将您的接口和实现移动到单独的 Visual Studio 项目/程序集中,并且仅在实际需要它的项目中引用实现项目 - 其他所有内容都可以为您的 @ 引用接口项目987654321@ - 那时代码可以使用接口,但实际上并不能更新任何实现本身。

然后,您可以在依赖项中的任何 DI 引用实施项目。

WebApp 解决方案:

WebApp Proj(控制器等)--> 服务接口项目

Service Impl Project --> 服务接口项目

即使这是一个很好的方法,它也不是万无一失的——另一个组成部分是教育和代码审查,以提出适合您团队的最佳实践,例如可测试性和依赖注入。

【讨论】:

  • 嗯 - 但不是这个代码:MenuItem menu = this._menuService.GetMenu();需要参考 MenuService 的实际实现吗?
  • 但是如果我有参考,我仍然可以调用 new MenuService(new MenuRepository()),不是吗?
  • 对 - 但我想关于我最初的问题 - 如果我调用 this._menuService.getMenu(),它需要实际实现,我必须添加对具有实现的项目的引用那个方法不行吗?我可以只引用接口项目,它会知道找到实现,即使没有引用?
  • 好建议。此外,您可以将所有实现设为内部并提供“RegisterAllInternalClassesWithContainer”方法以防止直接创建对象的任何合理机会......将您的实现与容器联系起来(这可以通过朋友程序集“通过另一个间接级别”再次解决以注册类)。
  • @JitendraPancholi 什么必须公开?如果你真的想要接口实现可以是显式的(比私有的多一些),并且类本身可以是内部的,如果你的 DI 容器需要,你可能需要公共构造函数......
【解决方案2】:

我认为手动实例化对象的部分问题可能来自与大型团队合作,其中一些成员以错误的方式使用构造函数注入技术。如果是这样的话,我发现通过对他们进行有关框架的教育可以解决大部分问题。有时,您会发现有人以错误的方式做事,但并不常见。另一种选择是在控制器构造函数上添加[EditorBrowsable(EditorBrowsableState.Never)] 属性。构造函数将从智能感知中消失;好吧,它似乎消失了。但是,它仍然可以使用。

您可以将实现分解为另一个 DLL,而不是由 MVC 项目直接引用(隐式引用),因此由于没有直接引用,因此您不能直接使用这些类型。对于每个项目引用的一个项目中的接口,以及间接引用实现的项目,因此只会包含接口。如果您正在进行单元测试,我建议在单元测试项目中包含直接引用,以增强测试覆盖率。

【讨论】:

  • 告诉人们不要使用它:-) ?这个答案是什么?如果有人不遵循指南,您将如何以编程方式限制他们?问题是如果人们可以直接访问实际的实现,我们为什么要实现接口?
  • @JitendraPancholi:这实际上是一个非常好的答案。这是我看到的架构师普遍存在的问题,他们宁愿试图阻止人们做错事,这通常会导致非常复杂的无用代码结构,开发人员仍然会发现大量错误的事情和工作方式围绕建筑师的限制。相反,开发人员应该接受教育,团队应该进行代码审查,因为这样更有效。
  • @Steven:不要通过指南进行此类限制,而是使用上述答案中 BrokenGlass 和 AlexeiLevenkov 提到的方法来创建一个良好的架构来减少人为错误。
  • @JitendraPancholi:你读过this吗?
  • @Steven, BrianMains:感谢您的健康讨论。我非常喜欢它。 :D :)
【解决方案3】:

几个潜在的选择(我从未尝试过,但可能有一些优势):

  • 您可能会编写一个 FXCop 规则,如果在代码中使用了构造函数,则该规则会出错。

  • 您可以将构造函数标记为过时,如果您在代码中使用过时的方法,则构建服务器会失败。

如果 DI 容器通过反射使用它,这一切都应该没问题(尽管在 FXCop 的情况下,如果它在 NInject 命名空间中的方法中,您可能不会抛出)

【讨论】:

  • 最好显式实现接口方法,所以如果有人试图实例化类并访问方法,那么由于访问级别,它甚至不会出现。
  • @JitendraPancholi 好的,但是这些选项包含在其他答案中,我试图提供一些可能的替代方案。我没有意识到只有一种方法可以给这只猫剥皮。
【解决方案4】:

作为一般设计原则,接口(合同)应该在一个程序集中,而实现应该在另一个程序集中。合同程序集应在 MVC 项目中引用,实现的程序集应复制到“bin”文件夹中。比使用“Dynamic Module Loading”加载类型。这样,您将避免上述问题,这是更广泛的解决方案。因为您可以在不构建 UI 和联系程序集的情况下替换实现。

【讨论】:

  • 那是什么设计原则?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 2016-08-04
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
相关资源
最近更新 更多