1、课程目标
如何来进行面向对象设计(OOD)和面向对象编程(OOP)
2、 如何进行面向对象设计?
- 那面向对象设计的产出就是类
- 在面向 对象设计环节,我们将需求描述转化为具体的类的设计
2.1、划分职责进而识别出有哪些类
类是现实世界中事物的一个建模
那就是根据需求描述,把其中涉及的功能点,一个一个 罗列出来,然后再去看哪些功能点职责相近,操作同样的属性,可否应该归为同一个类
针对鉴权这个例子,具体该如何来做 如下图
> 1. 把 URL、AppID、密码、时间戳拼接为一个字符串
- 对字符串通过加密算法加密生成 token
- 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL
- 解析 URL,得到 token、AppID、时间戳等信息
- 从存储中取出 AppID 和对应的密码
- 根据时间戳判断 token 是否过期失效
- 验证两个 token 是否匹配
1、2、6、7 都是跟 token 有关
3、4 都是在处理 URL,负责 URL 的拼接、解析
5 是操作 AppID 和密码,负责从 存储中读取 AppID 和密码
2.2、 定义类及其属性和方法
识别出了三个核心的类,它们分别是 AuthToken、Url 和 CredentialStorage
2.3、AuthToken 类相关的功能点
- 把 URL、AppID、密码、时间戳拼接为一个字符串
- 对字符串通过加密算法加密生成 token
- 根据时间戳判断 token 是否过期失效;
- 验证两个 token 是否匹配 .
2.4、 Url 类相关的功能点
- 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
- 解析 URL,得到 token、AppID、时间戳等信息
2.5 CredentialStorage
从存储中取出 AppID 和对应的密码
3、 定义类与类之间的交互关系
UML 统一建模语言中定义了六种类之间的关系 都是如下图
3.1 泛化
继承
public class A { ... }
public class B extends A { ... }
3.2 实现
public interface A {...}
public class B implements A { ... }
3.3 聚合
是一种包含关系,A 类对象包含 B 类对象,B 类对象的生命周期可 以不依赖 A 类对象的生命周期
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
3.4 组合
也是一种包含关系。A 类对象包含 B 类对象,B 类对象的生命周期 跟依赖 A 类对象的生命周期,B 类对象不可单独存在,比如鸟与翅膀之间的关系
public class A {
private B b;
public A() {
this.b = new B();
}
}
3.5 关联
是一种非常弱的关系,包含聚合、组合两种关系。具体到代码层面, 如果 B 类对象是 A 类的成员变量,那 B 类和 A 类就是关联关系
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
或者
public class A {
private B b;
public A() {
this.b = new B();
}
}
3.6 依赖
是一种比关联关系更加弱的关系,包含关联关系。不管是 B 类对象 是 A 类对象的成员变量,还是 A 类的方法使用 B 类对象作为参数或者返回值、局部变量, 只要 B 类对象和 A 类对象有任何使用关系,我们都称它们有依赖关系
public class A {
private B b;
public A(B b) {
this.b = b;
}
}
或者
public class A {
private B b;
public A() {
this.b = new B();
}
}
或者
public class A {
public void func(B b) { ... }
}
对类 与类之间的关系做了调整,只保留了四个关系:泛化、实现、组合、依赖,这样你掌握起来 会更加容易。
4、 将类组装起来并提供执行入口
接口鉴权并不是一个独立运行的系统,而是一个集成在系统上运行的组件,所以,我们封装 所有的实现细节,设计了一个最顶层的 ApiAuthencator 接口类,暴露一组给外部调用者 使用的 API 接口,作为触发执行鉴权逻辑的入口
5、 如何进行面向对象编程?
只 给出比较复杂的 ApiAuthencator 的实现
public interface ApiAuthencator {
void auth(String url);
void auth(ApiRequest apiRequest);
}
public class DefaultApiAuthencatorImpl implements ApiAuthencator {
private CredentialStorage credentialStorage;
public ApiAuthencator() {
this.credentialStorage = new MysqlCredentialStorage();
}
public ApiAuthencator(CredentialStorage credentialStorage) {
this.credentialStorage = credentialStorage;
}
@Override
public void auth(String url) {
ApiRequest apiRequest = ApiRequest.buildFromUrl(url);
auth(apiRequest);
}
@Override
public void auth(ApiRequest apiRequest) {
String appId = apiRequest.getAppId();
String token = apiRequest.getToken();
long timestamp = apiRequest.getTimestamp();
String originalUrl = apiRequest.getOriginalUrl();
AuthToken clientAuthToken = new AuthToken(token, timestamp);
if (clientAuthToken.isExpired()) {
throw new RuntimeException("Token is expired.");
}
String password = credentialStorage.getPasswordByAppId(appId);
AuthToken serverAuthToken = AuthToken.generate(originalUrl, appId, password
if (!serverAuthToken.match(clientAuthToken)) {
throw new RuntimeException("Token verfication failed.");
6、 辩证思考与灵活应用
设 计和实现基本上是按照功能点的描述,逐句照着翻译过来的。这样做的好处是先做什么、后 做什么,非常清晰、明确,有章可循,即便是没有太多设计经验的初级工程师,都可以按部 就班地参照着这个流程来做分析、设计和实现