摘要: 通过学习 Effective Java 的第一条规则中学习到了 Service Provider Framework。本文详细的介绍该框架,希望通过本文综合现有的资料,可以一站式的让你理解什么是服务提供者框架。
原文参考
静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在。这 种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如 JDBC (Java数据库连接,Java Database Connectivity) API。服务提供者框架是指这样一个系 统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从 多个实现中解耦出来。
服务提供者框架中有三个重要的组件:服务接口(Service Interface),这是提供者实现 的I提供者注册API (Provider Registration API),这是系统用来注册实现,让客户端访问它 们的;服务访问API (Service Access API),是客户端用来获取服务的实例的。服务访问API 一般允许但是不要求客户端指定某种选择提供者的条件。如果没有这样的规定,API就会返回 默认实现的一个实例。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。
服务提供者框架的第四个组件是可选的:服务提供者接口(Service Provider Interface),这 些提供者负责创建其服务实现的实例。如果没有服务提供者接口,实现就按照类名称注册,并 通过反射方式进行实例化(见第53条)。对于JDBC来说,Connection就是它的服务接口, DriverManager.registerDriver是提供者注册API,DriverManager.get Connection是服务访问API, Driver就是服务提供者接口。
服务提供者框架模式有着无数种变体。例如,服务访问API可以利用适配器(Adapter) 模式[Gamma95,p.139],返回比择供者需要的更丰富的服务接口。下面是一个简单的实现, 包含一个服务提供者接口和一个默认提供者:
概述
我们如何理解这句话:“多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来”?这里有几个元素
- 服务
Service
- 服务提供者
Service Provider
- 系统 应该指代该框架的管理器
- (服务提供者的)客户端
Client
- 实现 服务接口的具体实现
我们用 JDBC
来举例子, JDBC
的 sql.Connection
就是服务,这个服务接口仅定义了功能函数名,而没有实现,而服务提供者需要自行实现这些功能接口。比如 Mysql
就实现了 sql.Connection
的 getSchema()
方法,其中的客户端一般只某个服务提供者的客户端,比如你的项目使用了 Mysql
,那么你的项目就是 Mysql
的客户端。该客户端需要使用 Mysql
提供的服务的实现。而这个框架就是用于解耦客户端和服务提供者的服务实现。
通俗一点就是客户端不需要直接依赖服务提供者的服务实现,而是使用这样一个框架来访问其服务实现,如果有其他服务提供者也遵循同一个框架,那么客户端可以很容易的切换到该服务提供商。比如某个项目使用了 Mysql
并且使用 Mysql
提供的 JDBC
服务实现。如果使用该框架,那么如果有一天突然要变更到 Oracle
,只需要更改为 Oracle
的 JDBC
服务实现,大部分的代码都不需要更改(除了少部分服务提供者特有的特性依赖除外)就可以顺利完成变更。
类图分析
通过该类图我们可以发现,我们实际上在这个框架中有三个角色,一个是服务接口抽象层的标准定制者,一个是服务具体实现的提供商,最后一个是使用服务的客户端。在 Effective-Java 2rd (以后称为 EJ2
) 一书中并没有提到客户端,但是我个人理解只有加上这个角色才能更好的理解这个框架,因为大多数人都曾经扮演过客户端这个角色。
通过该类图我们可以发现,其中客户端和具体的服务提供者之间没有直接依赖,也就意味着客户端可以比较容易切换不同的服务提供者。同时服务提供者也可以在方便的开发具体的服务。也就是在顶层抽象的基础上开发和使用进行了解耦。
应用场景分析
什么情况下会使用这种框架呢?java 的 JDBC 是一个最好的例子,我们前面也一直用这个来距离,其他博文也提出了一些模拟的项目,比如公交卡项目。
概括的讲,如果某个功能有多种实现方法,而且客户端在使用的时候是有一定可能存在更换,那么这个框架就是否有用。
另外其注册和访问的理念也可以单独抽象出来使用,比如我们需要连接某个服务器,但是我们需要控制连接数量或者尽可能复用连接。那么就可以把这个框架中的 providers 换成 connection, 而服务就是连接。我们可以注册连接,然后需要使用连接的时候,直接获取,我们可以注册多个连接构成连接池,还可能在获取连接(也就是 Service access)的时候使用一定的策略,比如负载均衡等。