代码之家  ›  专栏  ›  技术社区  ›  treeblah

通用静态类作为服务定位器

  •  2
  • treeblah  · 技术社区  · 7 年前

    described in Game Programming 模式,我想知道一个可能的通用实现。以下代码

    以下C代码的思想是向应用程序的其他部分提供“全局”服务,只公开一个接口,而不是完整的实现。使用此方法注册的每个服务在应用程序中只有一个实例,但我希望能够轻松地交换所提供接口的不同实现。

    我的问题是:当我在整个应用程序中使用以下类提供不同的服务时,C如何知道我指的是不同类型的不同服务?直觉上,我几乎认为静态变量, _service

    public static class ServiceLocator<T>
    {
        static T _service;
    
        public static T GetService()
        {
            return _service;
        }
    
        public static void Provide(T service)
        {
            _service = service;
        }
    }
    

    以下是一些用法:

    // Elsewhere, providing:
    _camera = new Camera(GraphicsDevice.Viewport);
    ServiceLocator<ICamera>.Provide(_camera);
    
    // Elsewhere, usage:
    ICamera camera = ServiceLocator<ICamera>.GetService();
    
    // Elsewhere, providing a different service:
    CurrentMap = Map.Create(strategy);
    ServiceLocator<IMap>.Provide(CurrentMap);
    
    // Elsewhere, using this different service:
    IMap map = ServiceLocator<IMap>.GetService();
    
    2 回复  |  直到 7 年前
        1
  •  2
  •   Yeldar Kurmangaliyev    7 年前

    C#为开放类型的每个泛型参数组合创建一个单独的闭合类型。

    public static class GenericCounter<T>
    {
        public static int Count { get; set; } = 0;
    }
    
    GenericCounter<int>.Count++;
    GenericCounter<int>.Count++;
    GenericCounter<string>.Count++;
    Console.WriteLine(GenericCounter<double>.Count); // 0
    Console.WriteLine(GenericCounter<int>.Count); // 2
    Console.WriteLine(GenericCounter<string>.Count); // 1
    

    该代码输出:

    0
    2
    1
    

    例如,在您的情况下,行为将是 就像您创建了两个独立的类:

    public static class ServiceLocatorOfIMap
    {
        static IMap _service;
    
        public static IMap GetService()
        {
            return _service;
        }
    
        public static void Provide(IMap service)
        {
            _service = service;
        }
    }
    
    public static class ServiceLocatorOfICamera 
    {
        static ICamera _service;
    
        public static ICamera GetService()
        {
            return _service;
        }
    
        public static void Provide(ICamera service)
        {
            _service = service;
        }
    }
    

    使用方法如下:

    // Elsewhere, providing:
    _camera = new Camera(GraphicsDevice.Viewport);
    ServiceLocatorForICamera.Provide(_camera);
    
    // Elsewhere, usage:
    ICamera camera = ServiceLocatorForICamera.GetService();
    
    // Elsewhere, providing a different service:
    CurrentMap = Map.Create(strategy);
    ServiceLocatorForIMap.Provide(CurrentMap);
    
    // Elsewhere, using this different service:
    IMap map = ServiceLocatorForIMap.GetService();
    

    一般来说,它类似于C在遇到静态泛型类时所做的操作。

        2
  •  1
  •   Scott Hannen    7 年前

    我用它来处理我不能一直使用依赖注入的情况(比如WebForms),但我想编写由DI容器解析的可测试类。

    用法看起来像

    using(var resolved = new ResolvedService<ISomeService>())
    {
        resolved.Service.DoSomething();
    }
    

    好的一面:

    • 您可以使用DI容器来解析和释放资源
    • 它使集装箱和服务登记不可见

    坏的方面:

    • 它需要一个静态类,但它也在复合根中,所以这并不太糟糕。
    • ServiceLocator 不耦合到任何特定容器。但就目前而言,改变这一点并不重要。

    使用这意味着,虽然较大的组件(如.aspx页面)不可测试,但我注入其中的内容是可测试的。这让我产生了一个疯狂的想法——我可以为WebForms页面编写编排器,使它们大部分是可测试的。但希望我永远都不需要这样做。

    internal class ServiceLocator
    {
        private static IWindsorContainer _container;
    
        internal static void Initialize(IWindsorContainer container)
        {
            _container = container;
        }
        internal static TService Resolve<TService>(string key = null)
        {
            if (_container == null)
            {
                throw new InvalidOperationException(
                    "ServiceLocator must be initialized with a container by calling Initialize(container).");
            }
            try
            {
                return string.IsNullOrEmpty(key)
                    ? _container.Resolve<TService>()
                    : _container.Resolve<TService>(key);
            }
            catch (ComponentNotFoundException ex)
            {
                throw new InvalidOperationException(string.Format("No component for {0} has been registered.", typeof(TService).FullName), ex);
            }
        }
    
        internal static void Release(object resolved)
        {
            _container.Release(resolved);
        }
    }
    
    public class ResolvedService<TService> : IDisposable
    {
        private bool _disposed;
    
        private readonly TService _resolvedInstance;
    
        public TService Service
        {
            get { return _resolvedInstance; }
        }
    
        public ResolvedService(string key = null)
        {
            _resolvedInstance = ServiceLocator.Resolve<TService>(key);
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        ~ResolvedService()
        {
            Dispose(false);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;
            ServiceLocator.Release(_resolvedInstance);
            _disposed = true;
        }
    }