Trang chủ Program Design Pattern – ServiceLocator (P2)

Design Pattern – ServiceLocator (P2)

bởi Tran Tung

Phần 1 đã bàn về cài đặt cơ bản của DI, những điểm mạnh, điểm yếu của partten này, trong phần 2 sẽ bàn về ServiceLocator parten, đây là một dạng của DI.

Cơ bản thì ServiceLocator sẽ giải quyết cùng 1 vấn đề với DI Partten, tuy nhiên ở ví dụ ở phần 1 chúng ta áp dụng dạng Constructor Injection và có nhược điểm là phải khởi tạo service ở nhiều nơi để truyền vào các chỗ cần dùng. ServiceLocator sẽ giải quyết vấn đề trên bằng cách tạo và lưu 1 instance của service vào chung 1 chỗ, mỗi khi cần thì chỉ cần gọi từ đó ra chứ không cần tạo mới nữa.

Service Locator Pattern là gì?

Service Locator là một design pattern thông dụng cho phép tách rời (decouple) một class với các dependency (hay được gọi là service) của nó. Service Locator có thể coi là một đối tượng trung gian trong việc liên kết class và các dependency.

Service Locator pattern mô tả cách để đăng ký và lấy các dependency để sử dụng. Thông thường Service Locator được kết hợp với Factory Pattern hoặc Dependency Injection Pattern để có thể tạo ra các instance của service.

Cài đặt Service Locator Pattern

Các thành phần của Service Locator :

  • Service : các Service thực tế sẽ xử lý các request từ Client. Đối tượng Service ban đầu được tìm bởi ServiceLocator và trả lại kết quả theo yêu cầu.
  • Service Locator : là một điểm liên lạc duy nhất để trả về các Service từ bộ đệm (Cache).
  • Cache : một đối tượng để lưu trữ các tham chiếu đến Service để sử dụng lại chúng sau này.
  • Initial Context : tạo và đăng ký tham chiếu đến các Service trong bộ đệm (Cache).
  • Client : là đối tượng gọi các Service thông qua ServiceLocator.

Ví dụ

Chúng ta sẽ cải tiến ví dụ ở phần 1 với ServiceLocator:

Giữ nguyên phần service từ ví dụ trước

Service interface

1
2
3
4
public interface ILoginService
{
    void DoLogin(string userId);
}

Các implement của interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class GoogleService : ILoginService
{
    public GoogleService()
    {
    }
    public void DoLogin(string userId)
    {
        Console.WriteLine("user " + userId + " do login GG");
    }
}
 
public class FacebookService : ILoginService
{
    public FacebookService()
    {
    }
    public void DoLogin(string userId)
    {
        Console.WriteLine("user " + userId + " do login FB");
    }
}

ServiceLocator là một singleton để đảm bảo nó được truy cập từ mọi nơi và là duy nhất trong chương trình. Trong service locator sẽ giữ instance của service, như trong ví dụ là _loginService, sẽ có 1 hàm để đăng ký và 1 hàm để gọi service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ServiceLocator
{
    private static readonly ServiceLocator _instance = new ServiceLocator();
    private ILoginService _loginService;
    public static ServiceLocator Instance
    {
        get { return _instance; }
    }
    private ServiceLocator()
    {
    }
 
    //hàm đăng ký service
    public void RegisterLoginService(ILoginService loginService)
    {
        _loginService = loginService;
    }
 
    //Hàm gọi service
    public ILoginService GetLoginService()
    {
        return _loginService;
    }
}

Initial Context: khi cần gọi đến service chúng ta sẽ lấy instance từ ServiceLocator ra chứ không tạo mới

1
2
3
4
5
6
7
8
9
10
internal class Program
{
    public static void Main(string[] args)
    {
      ServiceLocator.Instance.RegisterLoginService(new FacebookService());
 
      LoginManager loginManager = new LoginManager(ServiceLocator.Instance.GetLoginService());
      loginManager.Login("userId_123");
    }
}

Với cải tiến trên có vẻ việc gọi service đã ổn hơn cách cũ, tuy nhiên khi thêm service vào chương trình thì phải sửa class ServiceLocator. Chúng ta sẽ cải tiến 1 lần nữa để giải quyết vấn đề trên. Thay vì khai báo từng instance cho service chúng ta sẽ gom chúng lại 1 chỗ và truy cập bằng Type của service, cụ thể như sau:

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
public class ServiceLocator
{
    private static readonly ServiceLocator _instance = new ServiceLocator();
    private readonly IDictionary<Type, object> _serviceCache;
 
    public static ServiceLocator Instance
    {
        get { return _instance; }
    }
 
 
    private ServiceLocator()
    {
        _serviceCache = new Dictionary<Type, object>();
    }
 
 
    public T GetService()
    {
        var key = typeof(T);
        if (!_serviceCache.ContainsKey(key))
        {
            throw new ArgumentException(string.Format("Type '{0}' has not been registered.", key.Name));
        }
 
 
        return (T) _serviceCache[key];
    }
 
 
    public void Register(T service)
    {
        var key = typeof(T);
        if (!_serviceCache.ContainsKey(key))
        {
            _serviceCache.Add(key, service);
        }
        else // overwrite the existing instance.
        {
            _serviceCache[key] = service;
        }
    }
 
    public void DeRegister(Type serviceType)
    {
        if (!_serviceCache.ContainsKey(serviceType))
        {
            throw new ArgumentException(string.Format("Type '{0}' has not been registered.", key.Name));
        }
        else
        {
            _serviceCache.Remove(serviceType);
        }
    }
}

Các service được đưa vào _serviceCache và có hàm Register, GetService, DeRegister phục vụ việc đăng ký, huỷ đăng ký và gọi service ra theo Type của service, với cách làm này mỗi lần thêm 1 service mới sẽ không cần sửa lại class ServiceLocator nữa.

Initial Context mới:

1
2
3
4
5
6
7
8
9
internal class Program
{
    public static void Main(string[] args)
    {
        ServiceLocator.Instance.Register<ILoginService>(new FacebookService());
        LoginManager loginManager = new LoginManager(ServiceLocator.Instance.GetService<ILoginService>());
        loginManager.Login("userId_123");
    }
}

Như vậy bây giờ chỉ có 1 chỗ thay đổi duy nhất là ở trong Initial Context khi chúng ta thay đổi hoặc thêm 1 service mới vào hệ thống.

Nhấn để đánh giá bài viết!
[Số đánh giá: 0 Trung bình: 0]

Có thể bạn quan tâm

Để lại bình luận