实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
上节讲述了如何进行面向对象分析OOA。这小节针对面向对象分析产出的需求,如何来进行面向对象设计OOD和面向对象编程OOP。
如何进行面向对象设计?
面向对象分析的产出是详细的需求描述,那面向对象设计的产出就是类。在面向对象设计环节,我们将需求描述转化为具体的类的设计。我们把这一设计环节拆解细化一下,主要包含以下几个部分:
- 划分职责进而识别出有哪些类;
- 定义类及其属性和方法;
- 定义类与类之间的交互关系;
- 将类组装起来并提供执行入口。
结合示例,如何进行对象设计并实现编码。
1. 划分职责并识别出有哪些类
根据需求描述,把其中涉及的功能点,一个一个罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,是否应该归为同一个类。
上节总结出来的需求描述:
- 调用方进行接口请求的时候,将 URL、AppID、密码、时间戳拼接在一起,通过加密算法生成 token,并且将 token、AppID、时间戳拼接在 URL 中,一并发送到微服务端。
- 微服务端在接收到调用方的接口请求之后,从请求中拆解出 token、AppID、时间戳。
- 微服务端首先检查传递过来的时间戳跟当前时间,是否在 token 失效时间窗口内。如果已经超过失效时间,那就算接口调用鉴权失败,拒绝接口调用请求。
- 如果 token 验证没有过期失效,微服务端再从自己的存储中,取出 AppID 对应的密码,通过同样的 token 生成算法,生成另外一个 token,与调用方传递过来的 token 进行匹配。如果一致,则鉴权成功,允许接口调用;否则就拒绝接口调用。
划分出来的功能点:
- 把 URL、AppID、密码、时间戳拼接为一个字符串;
- 对字符串通过加密算法加密生成 token;
- 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
- 解析 URL,得到 token、AppID、时间戳等信息;
- 从存储中取出 AppID 和对应的密码;
- 根据时间戳判断 token 是否过期失效;
- 验证两个 token 是否匹配;
其中,1、2、6、7和token相关,负责token的生成、验证;
3、4处理URL,负责URL的拼接、解析;
5是操作APPID的密码,负责从存储中读取APPID和密码。
所以,可以粗略地得到这三个核心类:
AuthToken、Url、CredentialStorage
技巧:针对复杂的需求开发,我们首先要做的是进行模块划分,将需求先简单划分成几个小的、独立的功能模块,然后再在模块内部,应用我们刚刚讲的方法,进行面向对象设计。而模块的划分和识别,跟类的划分和识别,是类似的套路。
2. 定义类及属性和方法
AuthToken 类相关的功能点
AuthToken 类相关的功能点有四个:
- 把 URL、AppID、密码、时间戳拼接为一个字符串;
- 对字符串通过加密算法加密生成 token;
- 根据时间戳判断 token 是否过期失效;
- 验证两个 token 是否匹配。
对于方法的识别,识别出需求描述中的动词,作为候选的方法,再进一步过滤筛选。类比一下方法的识别,我们可以把功能点中涉及的名词,作为候选属性,然后同样进行过滤筛选:
从上面的类图可以发现三个小细节:
- 第一个细节:并不是所有出现的名词都被定义为类的属性,比如 URL、AppID、密码、时间戳这几个名词,我们把它作为了方法的参数。
- 第二个细节:我们还需要挖掘一些没有出现在功能点描述中属性,比如 createTime,expireTimeInterval,它们用在 isExpired() 函数中,用来判定 token 是否过期。
- 第三个细节:我们还给 AuthToken 类添加了一个功能点描述中没有提到的方法 getToken()。
第一个细节告诉我们,从业务模型上来说,不应该属于这个类的属性和方法,不应该被放到这个类里。比如 URL、AppID 这些信息,从业务模型上来说,不应该属于 AuthToken,所以我们不应该放到这个类中。
第二、第三个细节告诉我们,在设计类具有哪些属性和方法的时候,不能单纯地依赖当下的需求,还要分析这个类从业务模型上来讲,理应具有哪些属性和方法。这样可以一方面保证类定义的完整性,另一方面不仅为当下的需求还为未来的需求做些准备。
Url 类相关的功能点
有两个:
- 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
- 解析 URL,得到 token、AppID、时间戳等信息。
虽然需求描述中,我们都是以 URL 来代指接口请求,但是,接口请求并不一定是以 URL 的形式来表达,还有可能是 Dubbo、RPC 等其他形式。为了让这个类更加通用,命名更加贴切,我们接下来把它命名为 ApiRequest。下面是根据功能点描述设计的 ApiRequest 类:
CredentialStorage 类相关的功能点
从存储中取出 AppID 和对应的密码。
CredentialStorage 类非常简单,类图如下所示。为了做到抽象封装具体的存储方式,我们将 CredentialStorage 设计成了接口,基于接口而非具体的实现编程。
3. 定义类与类之间的交互关系
因为目前只有三个核心的类,所以只用到了实现关系,也即 CredentialStorage 和 MysqlCredentialStorage 之间的关系。接下来讲到组装类的时候,我们还会用到依赖关系、组合关系,但是泛化关系暂时没有用到。
4. 将类组装起来并执行入口
接口鉴权并不是一个独立运行的系统,而是一个集成在系统上运行的组件,所以,我们封装所有的实现细节,设计了一个最顶层的 ApiAuthenticator 接口类,暴露一组给外部调用者使用的 API 接口,作为触发执行鉴权逻辑的入口。具体的类的设计如下所示:
如何进行面向对象编程(编码)?
面向对象设计完成之后,我们已经定义清晰了类、属性、方法、类之间的交互,并且将所有的类组装起来,提供了统一的执行入口。接下来,面向对象编程的工作,就是将这些设计思路翻译成代码实现。有了前面的类图,这部分工作相对来说就比较简单了。所以,这里我只给出比较复杂的 ApiAuthenticator 的实现。
1 |
|