【问题标题】:IoC and binding to interfacesIoC 和接口绑定
【发布时间】:2014-02-05 19:37:41
【问题描述】:

请原谅一个真正的但 n0​​0b 级别的查询。我正在做一个新项目并开始研究它的 IoC 方面。这是我负责构建框架的第一个工作,所以我对 IoC 有所了解。我强烈推荐使用 Ninject。凉爽的。

但是当我坐下来创建我的第一个依赖于构造函数注入的类时,我感到很震惊——我仍然需要在构造函数中使用所有这些完全自定义/第 3 方接口。那么,如果我的类在其构造函数中都采用 log4net ILog 实例,那么我的代码与 log4net 的耦合度如何?我仍然需要在每个想要记录任何内容的文件中使用 log4net 语句。

我认为 是重点 - 对众多类进行抽象和解耦,并将所有依赖项推入一个类。在我看来,每个想要记录任何内容的类仍然非常绑定到 log4net,并且将 log4net 更改为另一个记录器仍然是乏味的。这是怎么赢的?

我确定我错过了什么,所以请帮帮我?我是否打算在任何我想真正解耦的地方创建自己的接口,然后为实现添加适配器或其他东西?只有这样,我们才会将所有依赖项推到一个区域。

【问题讨论】:

  • 考虑将您的横切关注点(日志记录)移动到一个装饰器类中,然后向 Ninject 注册。并为您的第三方依赖项使用代理类。
  • 是的,这样的讨论是我所期待的。抱歉,它没有出现在我的搜索中。

标签: c# dependency-injection inversion-of-control


【解决方案1】:

如果您使用 IOC,则将接口注入到您的类中,而不是实现中,这样就可以了。要解决您的具体问题,请尝试查看通用日志框架 (http://netcommon.sourceforge.net/),它本身就是 log4net 或许多其他日志框架的包装器。

这将您与通用的日志记录框架结合起来,但它使用非常广泛、稳定并且抽象了日志记录的具体内容,而您不必自己做任何事情。

当我过去使用它时,我使用构建后脚本将 log4net 程序集带入输出目录,因此绑定仅在运行时发生。出于测试目的,就您的代码而言,您正在通过提供的公共接口与通用日志框架对话。

【讨论】:

  • 对,就是这个想法,并且解决了特定的日志记录问题。但这是一个更普遍的问题;日志记录只是一个例子。就我进入容器中获取本身绑定到特定库的接口而言,我所受的约束不亚于在依赖于其构造函数中的这些接口的类中。非常一般的案例本身对我来说还没有感觉那么有用。
  • 嗯,你是对的——你可以编写你自己的接口和你自己的包装类,我的意思是它们必须来自某个地方,对吧?我的建议是寻找已经完成所有代码的可重用接口/包装器。您仍然没有绑定到注入服务的任何真正可用的实现,这意味着它们可以在以后轻松换出。这实际上取决于您要使用的库以及您希望 IOC 走多远。恕我直言,你走得越远 - 你的代码就会变得越健壮。这是很多工作,所以如果其他人已经完成了...... :)
  • 哦,也就是说你也可以通过配置注册依赖。我使用unity而不是ninject,但原理是一样的——你可以通过松散的文件(如app.config或特定的配置文件)配置你的IOC容器来进一步解耦你自己的接口/包装器(当然依赖于IOC实现) .
  • 关于绑定的关键点是,当你绑定到一个具体的实现时,这些绑定都定义在一个位置(组合根),所以如果你需要在未来,您可以在一个地方完成。此外,当您开始进行单元测试并需要模拟实现时,您会发现绑定到接口时会容易得多。
  • 但这就是我的观点——“只在一个地方做”是整个事情的吸引力所在,但这是一个错误的主张——如果我将 log4net 的 ILog 接口实例注入到每个需要的类中要记录任何内容,当我决定废弃 log4net 并使用另一个记录器时,会有多少变化?非常非常多。胜利在哪里?
【解决方案2】:

我知道你在说什么!您将进行一种概括以减少代码重复(我在跳跃)。当您使用.net 框架时,我不得不说它默认不支持面向方面的编程,让您在每种情况下都表现得不同。例如看看这段代码:

public class BlogService : IBlogService
    {
        private readonly IBlogRepository _blogRepository;
        private readonly IUnitOfWork _unitOfWork;
        private readonly ILogger _logger;

        public BlogService(
            IBlogRepository blogRepository,
            IUnitOfWork unitOfWork,
            ILogger logger)
        {
            _blogRepository = blogRepository;
            _unitOfWork = unitOfWork;
            _logger = logger;
        }

   public GetAllBlogPostResponse GetAllBlogPost(GetAllBlogPostRequest request)
        {
            var response = new GetAllBlogPostResponse();
            try
            {
                var blogPosts = _blogRepository.GetAll();
                if (blogPosts != null)
                {
                    response.BlogPostViewModel = blogPosts.ConvertToPostListViewModel();
                    response.Success = true;
                    response.MessageType = MessageType.Success;
                    response.Message =       ServiceMessages.GeneralServiceSuccessMessageOnRetrieveInformation;
                    _logger.Log(string.Format(response.Message));
                }
                else
                {
                    response.MessageType = MessageType.Info;
                    response.Message = ServiceMessages.GeneralServiceAlarmMessageOnRetrieveInformation;
                    _logger.Log(string.Format(response.Message));
                }
            }
            catch (Exception exception)
            {
                response.Success = false;
                response.Message = ServiceMessages.GeneralServiceAlarmMessageOnRetrieveInformation;
                _logger.Log(string.Format(response.Message));
                _logger.Log(exception.Message);
            }
            return response;
        }

我在我的应用程序的每个服务类中注入了 IBlogRepository、IUnitOfWrork 和 ILogger(Log4net)。而不是我在每个 catch 语句中都有类似的响应和通用消息。曾经我想进行一种泛化而不是在每个服务类中重新实现类似的代码,但是在这种情况下进行泛化将更具成本效益和难度。尽管在某些情况下不重复代码非常重要,例如我有这个 BaseController 并从中派生出我的所有控制器:

public class BaseController : Controller
    {
        private readonly ICookieStorageService _cookieStorageService;
        private readonly ILanguageService _languageService;

        public BaseController(ICookieStorageService cookieStorageService,ILanguageService languageService)
        {
            _cookieStorageService = cookieStorageService;
            _languageService = languageService;
        }
    }

所以我不需要每次都在我的控制器中创建 cookieStorageService 和 languageService,因为我已经实现了一次。

【讨论】:

    猜你喜欢
    • 2013-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多