AOP-IoC-OOP-DI
目录
今天在学 Midway 的时候又遇到了 IoC 等相关的概念,故而总结一下
随着项目越来越复杂,代码越来越多的时候,总是会出现各种需要“重复”的功能,以上概念/技术就是在这些环境下出现的解决办法
面向对象编程 OOP
改进:从面向过程编程(POP) 变成面向对象编程(OOP)
优点:
- 结构更清晰,可读性高
- 数据更安全,数据不是全局直接能读取到的了
- 代码更少,复用率更高
需求假设:学校系统有学生和老师,他们都用登陆功能,学生可以查看成绩,老师可以设置成绩
C
// 面向过程实现
struct 老师 {
用户名
密码
}
struct 学生 {
用户名
密码
成绩
}
// 学生登陆函数
Bool studentLogin( char *username, char *password) {
// ...
}
// 老师登陆函数
Bool teacherLogin( char *username, char *password) {
// ...
}
// 学生查看成绩函数
Int studentCheckScore( char *username, char *password) {
// ...
}
// 老师设置成绩函数
void teacherSetScore( char *username, char *password, Int score ,char *studentName) {
// ...
}
C++
// 面向对象实现
class User {
public:
char *username;
char *password;
Bool login(String username, String password){
// ...
}
}
class Student : public User {
public:
Int score;
virtual Int checkScore() {
// ...
}
}
class Teacher : public User {
public:
virtual void setScore(Int score, String studentName) {
// ...
}
}
面向切面编程 AOP
现在我们添加一些需求:在登陆,查询成绩,设置成绩时,日志记录和权限校验
原来的方案会导致大量重复的日记和检查代码,并且如果修改了检查代码,所有涉及的函数都要修改
c++
bool checkPermission() {
// ...
}
void log(String str) {
// ...
}
// 面向对象实现
class User {
public:
char *username;
char *password;
Bool login(String username, String password){
if(!checkPermission()) {
log(err);
}
log(str);
// ...
}
}
class Student : public User {
public:
Int score;
virtual Int checkScore() {
if(!checkPermission()) {
log(err);
}
log(str);
// ...
}
}
class Teacher : public User {
public:
virtual void setScore(Int score, String studentName) {
if(!checkPermission()) {
log(err);
}
log(str);
// ...
}
}
C++
// 面向切面编程实现
// 代理类
class UserProxy {
public:
User *user;
bool login(String username, String password){
if(!checkPermission()) {
log(err);
}
log(str);
return user->login(username, password);
}
bool checkPermission() {
// ...
}
void log(String str) {
// ...
}
}
class StudentProxy : public UserProxy {
public:
void checkScore() {
}
}
class TeacherProxy : public UserProxy {
public:
void setScore(Int score, String studentName) {
}
}
// User 类
class User {
// ...
}
// Student 类
class Student : public User {
// ...
}
// Teacher 类
class Teacher : public User {
// ...
}
main(){
UserProxy *userProxy = new UserProxy(new Student());
}
这里我们使用了代理类的方案实现 AOP,业务函数和检查代码分离,结构更清晰
依赖注入 DI
除了构建代理类,还可以使用依赖注入,更加简单
但是依赖注入的语法在 C++中比较复杂,这里使用 TS 做演示
使用@xxx
这种装饰器将检查和日志的函数注入到业务函数上,简介又方便
TypeScript
// 定义日志装饰器
function Log(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`[LOG] 方法 ${methodName} 被调用,参数:`, args);
return originalMethod.apply(this, args);
};
return descriptor;
}
// 定义权限检查装饰器
function CheckPermission(target: any, methodName: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
if (!this.checkPermission()) {
console.error(`[ERROR] 权限不足,无法执行 ${methodName}`);
return false;
}
return originalMethod.apply(this, args);
};
return descriptor;
}
// 定义基础用户类
class User {
username: string;
password: string;
constructor(username: string, password: string) {
this.username = username;
this.password = password;
}
@Log
@CheckPermission
login(username: string, password: string): boolean {
console.log(`[User] ${username} 登录成功`);
return true;
}
checkPermission(): boolean {
return true; // 在这里可以加入实际的权限检查逻辑
}
}
// 定义学生类
class Student extends User {
@Log
checkScore() {
console.log(`[Student] ${this.username} 查询了自己的成绩`);
}
}
// 定义教师类
class Teacher extends User {
@Log
setScore(score: number, studentName: string) {
console.log(`[Teacher] ${this.username} 给 ${studentName} 设定了分数: ${score}`);
}
}
// 主程序
const student = new Student("Alice", "12345");
student.login("Alice", "12345"); // 自动调用日志 & 权限检查
student.checkScore();
const teacher = new Teacher("Bob", "admin");
teacher.login("Bob", "admin");
teacher.setScore(90, "Alice");
Ioc 容器
IoC(Inversion of Control)是一种设计模式,它允许应用程序通过依赖注入的方式来获取依赖对象,而不是自己创建。
比如我们现在需要使用控制函数来执行操作,以便将这些功能导出到外部,学生和老师的数据从数据库中获取
每次创建事务都需要创建类,并赋值
js
// 事务管理类
class TransactionManager {
teacher: Teacher;
student: Student;
constructor(teacher: String, student: String) {
this.teacher = new Teacher(teacher);
this.student = new Student(student);
// 从数据库获取学生和老师的数据
this.student.getData();
this.teacher.getData();
}
// 设置分数
function setScore( score) {
teacher.setScore(student, score);
}
// 请假
function leave(reason) {
teacher.leave(student, reason);
}
// 开除
function dismiss() {
teacher.dismiss(student);
}
}
js
// 使用 IoC 容器
@Injectable()
class Teacher {
// ...
}
@Injectable()
class Student {
// ...
}
class TransactionManager {
@Inject('teacher')
@Inject('student')
function setScore(score) {
teacher.setScore(student, score);
}
function leave(reason) {
teacher.leave(student, reason);
}
function dismiss() {
teacher.dismiss(student);
}
}
IoC 容器的实现这里没有写,在程序加载时,带有@Injectable()
装饰器的类会被注册到 IoC 容器中,当需要使用这些类时,可以通过@Inject()
装饰器来获取对应的实例,这样就不用手动创建实例了
参考资料
事实上我觉得参考资料写得比我好理解多了,例子也更合适