GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Design pattern Hướng dẫn Java Design Pattern – Service Locator

Hướng dẫn Java Design Pattern – Service Locator

Đăng vào 21/01/2019 . Được đăng bởi GP Coder . 12118 Lượt xem . Toàn màn hình

Trong các ứng dụng, chúng ta thường gặp trường hợp một class Client phụ thuộc vào một service hoặc một component là những lớp cụ thể (concrete class) trong lúc chạy ứng dụng.

Sự phụ thuộc của class Client vào các service này sẽ có một số vấn đề cần phải giải quyết:

  • Nếu thay thế hoặc cập nhật các service phụ thuộc, chúng ta cần thay đổi mã nguồn của class Client.
  • Các concrete class của một phụ thuộc có thể thay đổi tại thời điểm run-time hay không?
  • Các class cần phải viết những đoạn code cho việc quản lý, khởi tạo các phụ thuộc.
  • Khó khăn khi viết Unit Test do các lớp được kết hợp chặt chẽ.

Giải pháp cho vấn đề này chính là áp dụng Service Locator pattern, nó tạo ra một class chứa các tham chiếu đến các service và nó đóng gói các xử lý nghiệp vụ để xác định các service.

Nội dung

  • 1 Service Locator Pattern là gì?
  • 2 Cài đặt Service Locator Pattern như thế nào?
  • 3 Lợi ích của Service Locator Pattern là gì?
  • 4 Sử dụng Service Locator Pattern khi nào?

Service Locator Pattern là gì?

The service locator pattern is a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer. This pattern uses a central registry known as the “service locator” which on request returns the information necessary to perform a certain task.

The ServiceLocator is responsible for returning instances of services when they are requested for by the service consumers or the service clients.

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 như thế nào?

Các thành phần tham gia Service Locator Pattern:

  • 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ụ sử dụng Service Locator Pattern

MessagingService.java


package com.gpcoder.patterns.creational.servicelocator;

public interface MessagingService {
	
	String getMessageBody();

	String getServiceName();
}

EmailService.java


package com.gpcoder.patterns.creational.servicelocator;

public class EmailService implements MessagingService {

	public String getMessageBody() {
		return "This is message body of Email message";
	}

	public String getServiceName() {
		return "EmailService";
	}
}

SMSService.java


package com.gpcoder.patterns.creational.servicelocator;

public class SMSService implements MessagingService {

	public String getMessageBody() {
		return "This is message body of SMS message";
	}

	public String getServiceName() {
		return "SMSService";
	}
}

InitialContext.java


package com.gpcoder.patterns.creational.servicelocator.example1;

public class InitialContext {

	public MessagingService lookup(String serviceName) {
		if (serviceName.equalsIgnoreCase("EmailService")) {
			return new EmailService();
		} else if (serviceName.equalsIgnoreCase("SMSService")) {
			return new SMSService();
		}
		return null;
	}
}

Cache.java


package com.gpcoder.patterns.creational.servicelocator.example1;

import java.util.ArrayList;
import java.util.List;

public class Cache {

	private static final List<MessagingService> SERVICES = new ArrayList<>();

	public MessagingService getService(String serviceName) {
		for (MessagingService messagingService : SERVICES) {
			if (messagingService.getClass().getSimpleName().equals(serviceName)) {
				return messagingService;
			}
		}
		return null;
	}

	public void addService(MessagingService newService) {
		SERVICES.add(newService);
	}
}

ServiceLocator.java


package com.gpcoder.patterns.creational.servicelocator.example1;

public final class ServiceLocator {

	private static Cache cache = new Cache();
	
	private ServiceLocator() {
		throw new IllegalAccessError("Can't construct this class directly");
	}

	public static MessagingService getService(String serviceName) {

		MessagingService service = cache.getService(serviceName);

		if (service != null) {
			System.out.println("Get service from cache: " + serviceName);
			return service;
		}

		System.out.println("Create a new service and add to cache: " + serviceName);
		InitialContext context = new InitialContext();
		service = context.lookup(serviceName);
		cache.addService(service);
		return service;
	}
}

ServiceLocatorPatternExample.java


package com.gpcoder.patterns.creational.servicelocator.example1;

public class ServiceLocatorPatternExample {

	public static void main(String[] args) {
		MessagingService service = ServiceLocator.getService("EmailService");
		System.out.println(service.getMessageBody());

		MessagingService smsService = ServiceLocator.getService("SMSService");
		System.out.println(smsService.getMessageBody());

		MessagingService emailService = ServiceLocator.getService("EmailService");
		System.out.println(emailService.getMessageBody());
	}
}

MessagingService.java


Create a new service and add to cache: EmailService
This is message body of Email message
Create a new service and add to cache: SMSService
This is message body of SMS message
Get service from cache: EmailService
This is message body of Email message

Ví dụ cãi tiến Service Locator Pattern với Reflection

Trong ví dụ trên, lớp InitialContext chịu trách nhiệm khởi tạo các class dựa trên đối số serviceName. Cách làm này chưa được tốt, mỗi khi có service mới chúng ta phải sửa đổi code của class này để khởi tạo Service tương ứng. Chúng ta có thể sử dụng sửa đổi class này lại bằng cách nhận đối số là một serviceName có đầy đủ cả package của nó, sau đó sử dụng Reflection để khởi tạo instance cho Service. Ví dụ: com.gpcoder.patterns.creational.servicelocator.EmailService

Code của InitialContext được viết lại như sau:


package com.gpcoder.patterns.creational.servicelocator.example2_reflection;

import com.gpcoder.patterns.creational.servicelocator.MessagingService;

public class InitialContext {

	public MessagingService lookup(String serviceName) {
		try {
			Class<MessagingService> clazz = (Class<MessagingService>) Class.forName(serviceName);
			Object newInstance = clazz.newInstance();
			return cast(newInstance, clazz);
		} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static <T> T cast(Object obj, Class<T> clazz) {
		if (clazz.isAssignableFrom(obj.getClass())) {
			return clazz.cast(obj);
		}
		throw new ClassCastException("Failed to cast instance.");
	}
}

Class Cache của chúng ta cũng cần sửa lại đôi chút:


package com.gpcoder.patterns.creational.servicelocator.example2_reflection;

import java.util.ArrayList;
import java.util.List;

import com.gpcoder.patterns.creational.servicelocator.MessagingService;

public class Cache {

	private static final List<MessagingService> SERVICES = new ArrayList<>();

	public MessagingService getService(String serviceName) {
		for (MessagingService messagingService : SERVICES) {
			if (messagingService.getClass().getCanonicalName().equals(serviceName)) {
				return messagingService;
			}
		}
		return null;
	}

	public void addService(MessagingService newService) {
		SERVICES.add(newService);
	}
}

Source code đầy đủ của ví dụ này, các bạn có thể tham khảo thêm trên Github.

Lợi ích của Service Locator Pattern là gì?

Ưu điểm:

  • Nó tạo và giữ một thể hiện của các class Service trong một ServiceLocator duy nhất.
  • Tách rời một class với các dependency của nó, nhờ đó có thể dễ dàng thay thế các dependency tại thời điểm run-time mà không cần phải re-compile hay thậm chí là restart ứng dụng.
  • Các dependency sẽ được sử dụng dưới dạng interface, đảm bảo không sử dụng các class cụ thể (concrete) của dependency.
  • Dễ dàng test các class do không phụ thuộc vào các dependency.
  • Ứng dụng có thể được chia ra các phần ít bị ràng buộc (loose coupling) với nhau. Các đoạn code để tạo, quản lý dependency được tách riêng ra khỏi các class.

Nhược điểm:

  • Khó khăn khi debug và phát hiện lỗi do Client không biết được phần còn lại của hệ thống, Service nào sẽ được thực thi, mọi thứ được đăng ký với Service Locator.
  • Service Locator phải là duy nhất, nên có thể gặp hiện tượng thắt cổ chai.
  • Service Locator che dấu các lớp được đăng ký với Client, do đó có thể gặp lỗi run-time thay vì compile-time khi Service không tồn tại.
  • Service Locator làm cho code khó bảo trì hơn so với việc sử dụng DI (Dependency Injection). Các concrete class vẫn có phụ thuộc vào ServiceLocator, ServiceLocator chịu trách nhiệm tạo ra các phụ thuộc của nó. Với DI chỉ được gọi 1 lần để tiêm phụ thuộc vào một số lớp chính. Các lớp mà lớp chính này phụ thuộc vào sẽ đệ quy các phụ thuộc của chúng, cho đến khi có một đối tượng hoàn chỉnh. DI dễ viết Unit Test hơn.

Sử dụng Service Locator Pattern khi nào?

  • Khi cần tách rời một class với các dependency của nó.
  • Khi cần quản lý tập trung việc khởi tạo các Service.

Lời kết:

Service Locator Pattern là một mẫu đơn giản để giúp chúng ta giảm sự phụ thuộc giữa các service trong ứng dụng với việc áp dụng nguyên lý Inversion of Control. Tuy nhiên, trong trường hợp sử dụng các lớp trong nhiều ứng dụng, Dependency Injection là sự lựa chọn tốt hơn.

Service Locator cũng là một thuật ngữ hay được nhắc đến cùng với nguyên lý Dependency Inversion trong SOLID, IoC Container, DI container. Service Locator chính là một cách thực hiện của IoC Container. Trong các bài viết tiếp theo chúng ta sẽ cùng tìm hiểu về các khái niệm liên quan này.

Tài liệu tham khảo:

  • https://www.baeldung.com/java-service-locator-pattern
  • https://www.geeksforgeeks.org/service-locator-pattern/
  • https://www.tutorialspoint.com/design_pattern/service_locator_pattern.htm
  • https://en.wikipedia.org/wiki/Service_locator_pattern
  • https://www.oracle.com/technetwork/java/servicelocator-137181.html
  • https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
5.0
14
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Creational Pattern, Design pattern Được gắn thẻ: Creational Design Pattern, Design pattern

Hướng dẫn Java Design Pattern – DAO
Hướng dẫn Java Design Pattern – Dependency Injection

Có thể bạn muốn xem:

  • Hướng dẫn Java Design Pattern – Prototype (22/09/2018)
  • Giới thiệu Google Guice – Aspect Oriented Programming (AOP) (14/02/2019)
  • Hướng dẫn Java Design Pattern – Dependency Injection (28/01/2019)
  • Giới thiệu thư viện Apache Commons Chain (01/05/2019)
  • Refactoring Design Pattern với tính năng mới trong Java 8 (26/09/2019)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (97662 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97348 lượt xem)
  • Giới thiệu Design Patterns (87188 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (85901 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83409 lượt xem)

Nội dung bài viết

  • 1 Service Locator Pattern là gì?
  • 2 Cài đặt Service Locator Pattern như thế nào?
  • 3 Lợi ích của Service Locator Pattern là gì?
  • 4 Sử dụng Service Locator Pattern khi nào?

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link