1、课程目标

如何来进行面向对象设计(OOD)和面向对象编程(OOP)

2、 如何进行面向对象设计?

  1. 那面向对象设计的产出就是类
  2. 在面向 对象设计环节,我们将需求描述转化为具体的类的设计

2.1、划分职责进而识别出有哪些类

类是现实世界中事物的一个建模
那就是根据需求描述,把其中涉及的功能点,一个一个 罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否应该归为同一个类
针对鉴权这个例子,具体该如何来做 如下图

14、实战二(下):如何利用面向对象设计和编程开发接口鉴权功 能 - 图4> 1. 把 URL、AppID、密码、时间戳拼接为一个字符串

  1. 对字符串通过加密算法加密生成 token
  2. 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL
  3. 解析 URL,得到 token、AppID、时间戳等信息
  4. 从存储中取出 AppID 和对应的密码
  5. 根据时间戳判断 token 是否过期失效
  6. 验证两个 token 是否匹配

1、2、6、7 都是跟 token 有关
3、4 都是在处理 URL,负责 URL 的拼接、解析
5 是操作 AppID 和密码,负责从 存储中读取 AppID 和密码

2.2、 定义类及其属性和方法

识别出了三个核心的类,它们分别是 AuthToken、Url 和 CredentialStorage

2.3、AuthToken 类相关的功能点

  1. 把 URL、AppID、密码、时间戳拼接为一个字符串
  2. 对字符串通过加密算法加密生成 token
  3. 根据时间戳判断 token 是否过期失效;
  4. 验证两个 token 是否匹配 .

image.png

2.4、 Url 类相关的功能点

  1. 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
  2. 解析 URL,得到 token、AppID、时间戳等信息

image.png

2.5 CredentialStorage

从存储中取出 AppID 和对应的密码

image.png

3、 定义类与类之间的交互关系

UML 统一建模语言中定义了六种类之间的关系 都是如下图

3.1 泛化

继承

  1. public class A { ... }
  2. public class B extends A { ... }

3.2 实现

  1. public interface A {...}
  2. public class B implements A { ... }

3.3 聚合

是一种包含关系,A 类对象包含 B 类对象,B 类对象的生命周期可 以不依赖 A 类对象的生命周期

  1. public class A {
  2. private B b;
  3. public A(B b) {
  4. this.b = b;
  5. }
  6. }

3.4 组合

也是一种包含关系。A 类对象包含 B 类对象,B 类对象的生命周期 跟依赖 A 类对象的生命周期,B 类对象不可单独存在,比如鸟与翅膀之间的关系

  1. public class A {
  2. private B b;
  3. public A() {
  4. this.b = new B();
  5. }
  6. }

3.5 关联

是一种非常弱的关系,包含聚合、组合两种关系。具体到代码层面, 如果 B 类对象是 A 类的成员变量,那 B 类和 A 类就是关联关系

  1. public class A {
  2. private B b;
  3. public A(B b) {
  4. this.b = b;
  5. }
  6. }
  7. 或者
  8. public class A {
  9. private B b;
  10. public A() {
  11. this.b = new B();
  12. }
  13. }

3.6 依赖

是一种比关联关系更加弱的关系,包含关联关系。不管是 B 类对象 是 A 类对象的成员变量,还是 A 类的方法使用 B 类对象作为参数或者返回值、局部变量, 只要 B 类对象和 A 类对象有任何使用关系,我们都称它们有依赖关系

  1. public class A {
  2. private B b;
  3. public A(B b) {
  4. this.b = b;
  5. }
  6. }
  7. 或者
  8. public class A {
  9. private B b;
  10. public A() {
  11. this.b = new B();
  12. }
  13. }
  14. 或者
  15. public class A {
  16. public void func(B b) { ... }
  17. }

对类 与类之间的关系做了调整,只保留了四个关系:泛化、实现、组合、依赖,这样你掌握起来 会更加容易。

4、 将类组装起来并提供执行入口

接口鉴权并不是一个独立运行的系统,而是一个集成在系统上运行的组件,所以,我们封装 所有的实现细节,设计了一个最顶层的 ApiAuthencator 接口类,暴露一组给外部调用者 使用的 API 接口,作为触发执行鉴权逻辑的入口

image.png

5、 如何进行面向对象编程?

只 给出比较复杂的 ApiAuthencator 的实现

  1. public interface ApiAuthencator {
  2. void auth(String url);
  3. void auth(ApiRequest apiRequest);
  4. }
  5. public class DefaultApiAuthencatorImpl implements ApiAuthencator {
  6. private CredentialStorage credentialStorage;
  7. public ApiAuthencator() {
  8. this.credentialStorage = new MysqlCredentialStorage();
  9. }
  10. public ApiAuthencator(CredentialStorage credentialStorage) {
  11. this.credentialStorage = credentialStorage;
  12. }
  13. @Override
  14. public void auth(String url) {
  15. ApiRequest apiRequest = ApiRequest.buildFromUrl(url);
  16. auth(apiRequest);
  17. }
  18. @Override
  19. public void auth(ApiRequest apiRequest) {
  20. String appId = apiRequest.getAppId();
  21. String token = apiRequest.getToken();
  22. long timestamp = apiRequest.getTimestamp();
  23. String originalUrl = apiRequest.getOriginalUrl();
  24. AuthToken clientAuthToken = new AuthToken(token, timestamp);
  25. if (clientAuthToken.isExpired()) {
  26. throw new RuntimeException("Token is expired.");
  27. }
  28. String password = credentialStorage.getPasswordByAppId(appId);
  29. AuthToken serverAuthToken = AuthToken.generate(originalUrl, appId, password
  30. if (!serverAuthToken.match(clientAuthToken)) {
  31. throw new RuntimeException("Token verfication failed.");

6、 辩证思考与灵活应用

设 计和实现基本上是按照功能点的描述,逐句照着翻译过来的。这样做的好处是先做什么、后 做什么,非常清晰、明确,有章可循,即便是没有太多设计经验的初级工程师,都可以按部 就班地参照着这个流程来做分析、设计和实现