【问题标题】:Require Factory class in Extension Method扩展方法中需要工厂类
【发布时间】:2012-01-26 12:29:31
【问题描述】:

我正在开发一个大型遗留 C# 应用程序,分配给我的任务是删除静态工厂类 ServiceLocator.GetObject<T>() 的所有用法,并在整个过程中替换为构造函数注入的依赖项。

在大多数情况下,这很简单,但是在应用程序代码库中大约有 50 种情况有点棘手。例如,Servicelocator 用于静态类、扩展方法,甚至 WPF MarkupExtension!。

例如,如果您遇到这样的代码 sn-p,您会怎么做? (除了哭声)

public static class MyExtensions
{
    private static ISingletonServiceOne _ServiceOne = null;
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
    {
        Guard.ArgumentNotNull(rfq, "rfq");
        Guard.ArgumentNotNull(requester, "requester");

        if (_ServiceOne == null)
        {
            _ServiceOne = ServiceLocator.GetService<ISingletonServiceOne>(requester);
            Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
        }

        return _ServiceOne.GetBannerSummary(rfq);
    }

在上面,ServiceLocator.GetObject() 方法已在 IModel 上的扩展方法中使用,以定位单例注册服务并使用 IModel 在该服务上执行方法。

问题是:

  • 是否有任何模式/实践可以避免此类事情 - 静态类、值转换器或扩展方法中需要的 DI 容器
  • 是否有任何模式/实践来处理 DI 中的循环依赖关系?
  • 如果在良好的代码和交付时间之间进行权衡,您会怎么做?

我正在考虑将 GetBannerSummary() 方法从扩展中移出,在这种情况下只有 IModel,但是(不要笑)在 ValueConverters (WPF) 和 MarkupExtensions 中使用相同的 ServiceLocator 的情况:0

感谢您的 cmets/建议

【问题讨论】:

    标签: c# design-patterns dependency-injection extension-methods factory-pattern


    【解决方案1】:

    我唯一一次使用 ServiceLocator 是在静态方法中,例如扩展方法和 IValueConverters 不幸的是,实际上没有任何其他获得依赖项的好方法。

    唯一(稍微)更好的解决方案是将 ServiceLocator 调用移至延迟加载的属性,以便可以在单元测试期间注入依赖项。

    但是,在您的情况下,这不会发生,因为您将请求者属性传递给 GetService,在这种情况下,理想情况下您需要添加一个 IServiceOneFactory 依赖项,您可以将请求者对象传递给该依赖项。所以:

    public interface IServiceOneFactory
    {
        ISingletonServiceOne Create(object requester);
    }
    
    public static class MyExtensions
    {
        public static IServiceOneFactory ServiceOneFactory
        {
            get 
            {
                if( _ServiceOneFactory==null)
                    _ServiceOneFactory = ServiceLocator.GetService<IServiceOneFactory>();
                return _ServiceOneFactory;
            }
            set { _ServiceOneFactory = value; }
        }
    
        private static IServiceOneFactory _ServiceOneFactory = null;
        private static ISingletonServiceOne _ServiceOne = null;
        private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 
    
        public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
        {
            Guard.ArgumentNotNull(rfq, "rfq");
            Guard.ArgumentNotNull(requester, "requester");
    
            if (_ServiceOne == null)
            {
                _ServiceOne = ServiceOneFactory.Create(requester);
                Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
            }
    
            return _ServiceOne.GetBannerSummary(rfq);
        }
    }
    

    【讨论】:

    • 嗨,马克,感谢您的回答 - 我编辑了 Requestor,因为在内部,ServiceLocator 的实现实际上对它没有任何作用!同意你的预测。我在这里处理的是一个相当令人震惊的意大利面网络,所以欢迎提出任何分解它的建议:)
    • 嗨,安德鲁,不客气——我们都必须经历它!我认为你做的一切都是正确的,但我在静态、扩展等方面看不到 ServiceLocator 的解决方法。如果你按照你应该的那样保持所有依赖项模块化,这是一个必要的邪恶。
    • 是的,大重构正在进行中,因为需要在另一个项目中重用一大块代码作为棱镜“模块”。当我听到它时,我的反应是“大声笑”。无论如何,在 ServiceLocator 的 120 次使用中,我已经降到了 60 次。我想我可以修剪更多,但如果没有“简单的”解决方案,有些可能会留下来。非常感谢
    【解决方案2】:

    是否可以将IServiceX 注入您的类而不是使用静态访问器类?也许让GetBannerSummary 方法成为实现IModel 的抽象基类的一部分?

    当你不控制对象实例化时,DI 不会飞。 WPF 中的触发器、行为或标记扩展属于该类别。那里没有使用 ServiceLocator 的选项。

    【讨论】:

    • 嗯,我怀疑是 watson ......看来我得做很多工作才能解开这个意大利面
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    相关资源
    最近更新 更多