自学教程 学编程从这里开始

服务定位器模式

《服务定位器模式》|J2EE企业架构模式

关键词:Service Locator、服务定位器、缓存服务实例、查找服务、解耦服务查找
说明:服务定位器模式不属于GOF23设计模式,属于J2EE企业架构模式

一、什么是服务定位器模式

服务定位器模式(Service Locator Pattern):提供一个统一的服务查找中心,客户端通过服务名称/类型来获取所需服务实例,不需要直接new或者手动创建服务;服务定位器内部维护服务缓存,重复获取时直接返回缓存实例,提升性能。
用来封装服务查找、实例创建逻辑,隔离客户端与服务的具体实现。

核心意图
集中管理服务实例的查找与缓存;客户端只依赖服务抽象,不用关心服务的实例化、远程查找细节。

角色划分

  1. Client(客户端):需要使用服务的调用方,向ServiceLocator请求服务
  2. Service Locator(服务定位器):核心入口,负责查找服务、缓存服务实例
  3. Initial Context(初始上下文):模拟服务注册中心,获取原始服务实例
  4. Service(服务接口):定义业务服务的抽象方法
  5. Concrete Service(具体服务):服务接口实现类

数据流:
Client → ServiceLocator → 先查缓存,无缓存则访问InitialContext获取服务 → 返回Service

二、UML结构图

角色划分:

  1. Client:调用服务定位器获取业务服务
  2. ServiceLocator:持有缓存,调用InitialContext获取服务
  3. InitialContext:服务原始查找容器
  4. Service:服务抽象接口
  5. ConcreteService:服务实现

三、Java完整示例

模拟服务定位器,包含缓存逻辑,代码全部使用英文。

// Service Interface
public interface Service {
    String getName();
    void execute();
}

// Concrete Service
public class EmailService implements Service {
    @Override
    public String getName() {
        return "EmailService";
    }
    @Override
    public void execute() {
        System.out.println("EmailService: Send email message");
    }
}

public class SmsService implements Service {
    @Override
    public String getName() {
        return "SmsService";
    }
    @Override
    public void execute() {
        System.out.println("SmsService: Send sms message");
    }
}

// Initial Context
public class InitialContext {
    public Service lookup(String serviceName) {
        if ("EmailService".equalsIgnoreCase(serviceName)) {
            return new EmailService();
        } else if ("SmsService".equalsIgnoreCase(serviceName)) {
            return new SmsService();
        }
        return null;
    }
}

// Service Locator
import java.util.HashMap;
import java.util.Map;
public class ServiceLocator {
    private static Map<String, Service> cache = new HashMap<>();
    private InitialContext context;

    public ServiceLocator() {
        context = new InitialContext();
    }

    public Service getService(String serviceName) {
        if(cache.containsKey(serviceName)){
            System.out.println("Get service from cache: " + serviceName);
            return cache.get(serviceName);
        }
        Service service = context.lookup(serviceName);
        if(service != null){
            cache.put(serviceName, service);
            System.out.println("Create new service and put into cache: " + serviceName);
        }
        return service;
    }
}

// Client
public class Client {
    public static void main(String[] args) {
        ServiceLocator locator = new ServiceLocator();

        Service service1 = locator.getService("EmailService");
        service1.execute();

        Service service2 = locator.getService("EmailService");
        service2.execute();

        Service service3 = locator.getService("SmsService");
        service3.execute();

        Service service4 = locator.getService("SmsService");
        service4.execute();
    }
}

运行输出:

Create new service and put into cache: EmailService
EmailService: Send email message
Get service from cache: EmailService
EmailService: Send email message
Create new service and put into cache: SmsService
SmsService: Send sms message
Get service from cache: SmsService
SmsService: Send sms message

四、服务定位器模式 vs 依赖注入(DI) vs 业务代表模式

模式定位核心区别
服务定位器J2EE架构模式,主动查找代码主动向定位器拉取服务;自带缓存,客户端控制获取时机
依赖注入DI容器自动注入容器主动把服务注入对象,客户端不主动查找
业务代表模式J2EE架构模式业务代表内部封装服务定位器,重点是隔离表现层和业务服务

业务代表模式底层经常组合使用服务定位器。

五、优缺点总结

✅ 优点

  1. 服务查找逻辑集中封装,客户端不用关心实例创建
  2. 内置缓存,多次获取同一个服务,减少实例创建开销
  3. 服务实现变更,客户端代码无需改动
  4. 支持动态切换服务实现

❌ 缺点

  1. 全局缓存,服务状态共享容易引发并发问题
  2. 隐藏依赖:代码很难直观看到类依赖了哪些服务,不利于静态分析
  3. 单元测试不方便,很难Mock被缓存的服务
  4. 缓存管理复杂,服务销毁、资源释放需要额外处理

六、框架应用场景

  • 早期J2EE EJB查找(JNDI lookup)
  • Spring早期BeanFactory有类似思想

现代开发更推荐依赖注入DI,服务定位器模式使用减少;但在插件系统、动态服务加载场景仍有使用。

七、小结

服务定位器模式核心:提供统一服务查找入口,内置缓存;客户端根据名称主动获取服务实例,屏蔽服务实例化与查找细节。

归属:J2EE架构模式,不属于GOF23;和DI是两种获取依赖的思路。

标签:

0 条笔记