这章主要学习:
- MVC架构
- IOC控制反转具体思想
- DI依赖注入的具体实践
- AOP面向切面和装饰器风格
我们之前学习:
- express 编写接口
- mysql --orm框架 读写数据库
现在,我们需要把他们糅合在一起 实现类似nest.js 或java 的springboot 的框架
MVC 软件架构模式
-
Model(模型):
- 职责:表示应用程序的数据和业务逻辑。它负责与数据源(如数据库)进行交互,获取、存储和修改数据。模型独立于用户界面,不直接与用户交互。
- 示例:在一个在线商店应用中,模型部分可以是代表商品、购物车、用户等的类。
-
View(视图):
- 职责:负责显示数据(模型)给用户,并生成用户界面。视图关注数据的展示,但不直接处理数据的变化。视图通常接收来自模型的数据,并将其呈现给用户。
- 示例:HTML 页面、图形界面组件(按钮、文本框等)以及报表等。
-
Controller(控制器):
- 职责:作为模型和视图之间的中介,控制器处理用户的输入并决定如何更新模型和视图。当用户与视图交互时,控制器接收输入、调用模型进行数据处理,并更新视图显示。
- 示例:在一个在线商店应用中,控制器接收用户的点击操作,比如“添加到购物车”按钮,调用相应的模型方法更新购物车数据,然后更新视图显示。
MVC 架构的工作流程:
- 用户交互:用户通过视图与应用程序进行交互(例如点击按钮、提交表单等)。
- 控制器处理请求:控制器接收到用户的输入,处理业务逻辑,并根据需要更新模型或视图。
- 模型更新:控制器更新模型(如修改数据、查询数据库等)。
- 视图更新:模型的变化触发视图更新,视图展示最新的数据给用户。
MVC 的优点:
- 分离关注点:将数据(模型)、用户界面(视图)和业务逻辑(控制器)分离,使得各个模块独立,便于开发、维护和扩展。
- 可重用性:视图和模型是相互独立的,因此它们可以重用。例如,可以通过不同的视图展示相同的数据。
- 易于维护:由于代码模块化,修改一个模块通常不会影响到其他模块,方便代码维护。
应用场景:
MVC 架构广泛应用于 Web 应用程序、桌面应用程序等,尤其适用于用户界面复杂的应用程序。常见的 Web 开发框架如 Django、Ruby on Rails、Angular、Spring MVC 都基于 MVC 架构。
简而言之,MVC 架构的核心思想是:通过分离模型、视图和控制器来增强代码的模块化、可维护性和可扩展性。
IoC(控制反转) 和 DI(依赖注入)
是软件开发中的重要概念,特别是在面向对象编程中,它们有助于增强应用程序的模块化、可扩展性和可维护性。虽然这两个概念紧密相关,但它们有不同的定义和作用。
1. IoC(控制反转)
控制反转(Inversion of Control, IoC) 是一种设计原则,它将对象的创建和依赖关系的管理交给框架或外部容器,而不是由对象本身来负责。
通常在传统的编程模型中,类内部会自己管理依赖的创建和控制,比如在类中直接创建其他类的实例。控制反转的核心思想就是“反转”这种控制,把对象的创建和依赖关系管理的控制权交给外部系统(如IoC容器),从而使得类之间的耦合度降低,增强模块的独立性和可测试性。
IoC的关键特点:
- 反转控制:通过外部容器(如Spring框架的IoC容器)来控制对象的创建和管理,而不是由对象自己创建依赖对象。
- 解耦:使得类之间的依赖关系不再由类本身直接管理,减少了代码的耦合性。
- 增强灵活性:控制反转使得对象更易于替换和扩展,因为它们不直接依赖于特定的实现。
2. DI(依赖注入)
依赖注入(Dependency Injection, DI) 是实现控制反转的一种方式。它是一种设计模式,通过将对象所依赖的其他对象注入到类中,而不是让类自己去创建依赖对象,从而实现依赖关系的反转。
简单来说,依赖注入 就是外部容器(如Spring)将一个对象的依赖关系注入到对象的构造函数、属性或方法中,而不是由对象自己去创建这些依赖。
DI的关键特点:
- 依赖关系注入:通过外部的IoC容器或框架,自动将所需的依赖项注入到类中。依赖项可以通过构造器注入、属性注入或方法注入的方式传递。
- 解耦和可测试性:通过依赖注入,可以将业务逻辑与其依赖的组件解耦,使得代码更加易于测试,因为测试时可以替换真实的依赖为模拟对象(mock)或伪对象。
IoC与DI的关系
- IoC是更广泛的概念,它描述了控制反转的整个思想和方法。IoC的实现方式可以有多种,依赖注入(DI)是其中最常见的一种。
- DI是实现IoC的一种方式。依赖注入是控制反转的具体实现手段之一。通过依赖注入,我们将对象所依赖的组件或服务交给容器管理,并将这些依赖注入到目标对象中,完成反转控制。
举例:
1. 构造器注入(Constructor Injection)
// Repository 类 class Repository {public getData() {return "Some data from the repository";} }// Service 类,通过构造器注入 Repository 依赖 class Service {private repository: Repository;constructor(repository: Repository) {this.repository = repository; // 依赖注入 }public getServiceData(): string {return this.repository.getData();} }// 创建 Repository 实例 const repo = new Repository(); // 创建 Service 实例,并注入 Repository const service = new Service(repo);console.log(service.getServiceData()); // 输出: Some data from the repository
2. 属性注入(Property Injection)
3. 方法注入(Method Injection)
class Repository {public getData() {return "Some data from the repository";} }// Service 类,通过 setter 方法注入 Repository 依赖 class Service {private repository: Repository;public setRepository(repository: Repository) {this.repository = repository; // 依赖注入 }public getServiceData(): string {return this.repository.getData();} }// 创建 Repository 实例 const repo = new Repository(); // 创建 Service 实例 const service = new Service(); // 通过 setter 方法注入 Repository service.setRepository(repo);console.log(service.getServiceData()); // 输出: Some data from the repository
元组数据的反射(Reflection on Tuple Data)
是指在运行时动态地获取和操作元组数据结构的信息。元组是一种数据结构,它允许你将不同类型的数据组合在一起,但每个元素的位置和类型是固定的。
在TypeScript中,元组的反射可以通过Reflect
对象来实现。Reflect
是一个内置对象,它提供了一系列静态方法,用于操作对象和元组等数据结构。
以下是一些常用的Reflect
方法,用于元组数据的反射:
Reflect.get
:获取对象属性的值。Reflect.set
:设置对象属性的值。Reflect.has
:检查对象是否具有某个属性。Reflect.deleteProperty
:删除对象的属性。Reflect.ownKeys
:返回对象自身的属性键。
const tuple = [1, 'hello', true];// 获取元组的第一个元素 const firstElement = Reflect.get(tuple, 0); console.log(firstElement); // 输出: 1// 设置元组的第二个元素 Reflect.set(tuple, 1, 'world'); console.log(tuple); // 输出: [1, 'world', true]// 检查元组是否包含某个元素 const hasHello = Reflect.has(tuple, 'hello'); console.log(hasHello); // 输出: false// 删除元组的第三个元素 Reflect.deleteProperty(tuple, 2); console.log(tuple); // 输出: [1, 'world']
对象修饰器(Object Decorator)
是一个设计模式,常见于面向对象编程中,尤其是在一些现代的编程语言和框架(如 TypeScript、Python、Java 等)中使用。在这种模式下,通过修饰器对现有的对象或类进行扩展、修改或增强其功能,而不需要直接修改对象或类的原始代码。
修饰器模式(Decorator Pattern)
是 结构型设计模式,它允许你通过将功能附加到对象上来扩展它们,而无需修改对象的结构或直接继承它们。修饰器通常通过包裹(wrap)原始对象来实现对其功能的增强。
基本概念
修饰器主要有两个作用:
- 增强对象的功能:在对象的原有功能基础上,增加新的功能或改变原有行为。
- 不修改对象本身:使用修饰器来增强对象的功能,而不需要改变对象的类或原始实现。
修饰器的实现
在很多现代编程语言中,修饰器通常以函数或类的形式出现,通过某些方式(如装饰器、函数包装等)修改目标对象的行为。
TypeScript 中的对象修饰器
在 TypeScript 中,修饰器是一个特殊的声明,它能够附加到类、方法、属性或参数上。你可以用它来修改或增强目标对象的行为。TypeScript 支持的修饰器包括 类修饰器、方法修饰器、访问器修饰器、属性修饰器 和 参数修饰器。
1. 类修饰器 (Class Decorator)
类修饰器是一个应用于类构造函数的函数。它们可以用来修改类的定义,添加额外的功能或行为。
function Injectable(constructor: Function) {console.log(`${constructor.name} is Injectable`); }@Injectable class MyService {constructor() {console.log("MyService created!");} }const service = new MyService();
2. 方法修饰器 (Method Decorator)
function LogExecutionTime(target: any, propertyName: string, descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args: any[]) {const start = Date.now();console.log(`Executing ${propertyName}...`);const result = originalMethod.apply(this, args);const end = Date.now();console.log(`Execution of ${propertyName} took ${end - start}ms`);return result;}; }class MyClass {@LogExecutionTimepublic myMethod() {// 模拟一个耗时的操作for (let i = 0; i < 1e6; i++) {}} }const obj = new MyClass(); obj.myMethod(); // 会打印执行时间
3. 访问器修饰器 (Accessor Decorator)
function LogAccess(target: any, propertyName: string, descriptor: PropertyDescriptor) {const originalGetter = descriptor.get;descriptor.get = function() {console.log(`Accessing ${propertyName}`);return originalGetter?.apply(this);}; }class Person {private _name: string;constructor(name: string) {this._name = name;}@LogAccessget name() {return this._name;} }const person = new Person("John"); console.log(person.name); // 会打印 "Accessing name" 然后打印 "John"
4. 属性修饰器 (Property Decorator)
function ReadOnly(target: any, propertyName: string) {let value: any;const getter = () => value;const setter = (newValue: any) => {console.log(`Attempting to modify ${propertyName}...`);};Object.defineProperty(target, propertyName, {get: getter,set: setter,enumerable: true,configurable: true}); }class User {@ReadOnlypublic username: string;constructor(username: string) {this.username = username;} }const user = new User("admin"); console.log(user.username); // "admin" user.username = "newUser"; // 控制台打印 "Attempting to modify username..."
5. 参数修饰器 (Parameter Decorator)
function LogParameter(target: any, methodName: string, parameterIndex: number) {const method = target[methodName];console.log(`Parameter at index ${parameterIndex} in method ${methodName} is being modified.`); }class MyClass {greet(@LogParameter name: string) {console.log(`Hello, ${name}`);} }const obj = new MyClass(); obj.greet("Alice"); // 会打印 "Parameter at index 0 in method greet is being modified."