zoukankan      html  css  js  c++  java
  • [Architecture Pattern] Service Locator

    动机

    Service Locator是一个在开发系统时,很常用的一个模式。在Martin Fowler写的Inversion of Control Containers and the Dependency Injection pattern里,可以发现这个Pattern的身影。Service Locator最主要是定义BLL层内对象生成、对象存放、对象取得的职责,让系统在取得对象时不需要知道对象是如何生成及存放,有效降低系统的耦合性。

    同时学习Service Locator,也为架构设计带入了空间的概念。在设计架构的时候,可以套用Service Locator来做为架构空间的封装。将经对象生成建立的对象,「存放」在Service Locator内,让目标架构「存在」一组对象可以提供使用。

    本篇文章介绍一个Service Locator的实做,这个实做定义对象之间的职责跟互动,用来完成Service Locator应该提供的功能及职责。为自己做个纪录,也希望能帮助到有需要的开发人员。

    结构

    接下来沿用[Architecture Pattern] Repository建立的UserCountService,来当作范例的内容。另外在BLL层使用Service Locator来封装UserCountService的对象生成、对象存放、对象取得。范例的结构如下:

    主要的参与者有:

    ServiceLocator
    -提供存放对象的功能给系统使用。
    -提供存放的对象给系统使用。

    Client
    -使用ServiceLocator内存放的UserCountService。

    UserCountService
    -使用系统内User数据计算各种Count。
    -由Client生成或是ServiceLocator生成。

    透过下面的图片说明,可以了解相关对象之间的互动流程。



    实做

    Service Locator由两种运作逻辑所组成:定位逻辑、生成逻辑。定位逻辑是整个Service Locator的核心,它定义对象存放、对象取得的职责。而对象必须要被生成才能够使用,生成逻辑就是定义对象生成的职责。接着透过实做一组Service Locator,解析Service Locator内的运作逻辑,帮助开发人员理解Service Locator模式。

    范列下载

    实做说明请参照范例程序内容:ServiceLocatorSample点此下载

    定位逻辑

    首先建立ServiceLocatorSample.BLL项目,并且建立ServiceLocator对象用来封装Service Locator的定位逻辑:ServiceLocator提供SetInstance方法,让系统可以存放各种型别的对象。并且ServiceLocator提供GetInstance方法,让系统可以取得先前存放的对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public partial class ServiceLocator
    {
        // Fields 
        private readonly Dictionary<Type, object> _serviceDictionary = new Dictionary<Type, object>();
     
     
        // Methods
        public TService GetInstance<TService>() where TService : class
        {
            // Result
            TService service = default(TService);
     
            // Exist
            if (_serviceDictionary.ContainsKey(typeof(TService)) == true)
            {
                service = _serviceDictionary[typeof(TService)] as TService;
            }
     
            // Return
            return service;
        }
     
        public void SetInstance<TService>(TService service) where TService : class
        {
            #region Require
     
            if (service == null) throw new ArgumentNullException();
     
            #endregion
     
            // Set
            if (_serviceDictionary.ContainsKey(typeof(TService)) == false)
            {
                _serviceDictionary.Add(typeof(TService), service);
            }
            else
            {
                _serviceDictionary[typeof(TService)] = service;
            }
        }
    }

    另外ServiceLocator也套用了Singleton pattern,让系统能方便的使用ServiceLocator。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public partial class ServiceLocator
    {
        // Singleton
        private static ServiceLocator _current;
     
        public static ServiceLocator Current
        {
            get
            {
                if (_current == null)
                {
                    _current = new ServiceLocator();
                }
                return _current;
            }
            set
            {
                _current = value;
            }
        }
    }

    外部生成逻辑

    再来建立一个Console项目,透过ServiceLocator取得UserCountService用来打印的人员数量。而UserCountService是由Console专案来生成、注入ServiceLocator。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    class Program
    {
        static void Main(string[] args)
        {
            // Initialize
            InitializeServiceLocator();
                 
            // UserCountService
            UserCountService userCountService = ServiceLocator.Current.GetInstance<UserCountService>();
     
            // Print
            Console.WriteLine("All Count : " + userCountService.GetAllCount());
            Console.WriteLine("Men Count : " + userCountService.GetMenCount());
     
            // End
            Console.ReadLine();
        }
     
        static void InitializeServiceLocator()
        {
            // UserRepository
            IUserRepositoryProvider userRepositoryProvider = new SqlUserRepositoryProvider();
            UserRepository userRepository = new UserRepository(userRepositoryProvider);
            UserCountService userCountService = new UserCountService(userRepository);
     
            // SetInstance
            ServiceLocator.Current.SetInstance<UserCountService>(userCountService);
        }
    }

    内部生成逻辑

    到目前为止范例程序,已经可以透过ServiceLocator取得UserCountService用来打印的人员数量。但UserCountService是由ServiceLocator之外的函式来生成、存放至ServiceLocator。这造成每次重用的时候,必须要重新建立对象生成、存放的功能。

    为了增加ServiceLocator的重用性,所以修改ServiceLocator对象,封装Service Locator的生成逻辑:ServiceLocator提供CreateInstance方法,让系统可以建立各种型别的对象。并且变更GetInstance的运作流程,让系统取不到先前存放的对象时,会去使用CreateInstance来生成对象。

    (因为是仿真范例,简化了很多UserCountService的设计,并且采用直接建立的方式来示意。实际项目可以采用各种IoC Framework来做生成注入,或是套用各种Factory pattern,这些都能提高ServiceLocator的重用性。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    public partial class ServiceLocator
    {
        // Fields 
        private readonly Dictionary<Type, object> _serviceDictionary = new Dictionary<Type, object>();
     
     
        // Methods
        protected virtual TService CreateInstance<TService>() where TService : class
        {
            // Result
            TService service = default(TService);
     
            // UserCountService
            if (typeof(TService) == typeof(UserCountService))
            {
                IUserRepositoryProvider userRepositoryProvider = new CsvUserRepositoryProvider();
                UserRepository userRepository = new UserRepository(userRepositoryProvider);
                UserCountService userCountService = new UserCountService(userRepository);
                service = userCountService as TService;
            }
     
            // Return
            return service;
        }
     
        public TService GetInstance<TService>() where TService : class
        {
            // Result
            TService service = default(TService);
     
            // Exist
            if (_serviceDictionary.ContainsKey(typeof(TService)) == true)
            {
                service = _serviceDictionary[typeof(TService)] as TService;
            }
            if (service != null) return service;
     
            // Create
            service = this.CreateInstance<TService>();
            if (service != null) this.SetInstance<TService>(service);
     
            // Return
            return service;
        }
     
        public void SetInstance<TService>(TService service) where TService : class
        {
            #region Require
     
            if (service == null) throw new ArgumentNullException();
     
            #endregion
     
            // Set
            if (_serviceDictionary.ContainsKey(typeof(TService)) == false)
            {
                _serviceDictionary.Add(typeof(TService), service);
            }
            else
            {
                _serviceDictionary[typeof(TService)] = service;
            }
        }
    }

    最后修改Console项目,移除项目内生成UserCountService的逻辑。并且同样透过ServiceLocator取得UserCountService用来打印的人员数量。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Program
    {
        static void Main(string[] args)
        {
            // UserCountService
            UserCountService userCountService = ServiceLocator.Current.GetInstance<UserCountService>();
     
            // Print
            Console.WriteLine("All Count : " + userCountService.GetAllCount());
            Console.WriteLine("Men Count : " + userCountService.GetMenCount());
     
            // End
            Console.ReadLine();
        }
    }

    后记

    Service Locator有很多实做版本,这些实做版本依照需求分割、设计,BLL层内对象生成、对象存放、对象取得的职责,用以提高整体架构的重用性、可维护性。一个系统的成败,除了最基本的满足客户需求之外,这些额外的非功能需求也是很重要的一环。这让后续接手维护的开发人员,能够早点回家吃晚餐。:D


    期許自己~
    能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
    真正做到「以形寫神」的境界。


  • 相关阅读:
    strstr 函数的实现
    函数模板与模板函数
    内核态和用户态
    最短路径算法(跟新SPFA,Ford)
    P1042 乒乓球
    P2347 砝码称重
    P1087 FBI树
    P1540 机器翻译
    P1028 数的计算
    P1067 多项式输出
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2511153.html
Copyright © 2011-2022 走看看