项目架构(1)--- MVC\IoC\DI \元数据\元组反射\修饰器

这章主要学习:

  • MVC架构
  • IOC控制反转具体思想
  • DI依赖注入的具体实践
  • AOP面向切面和装饰器风格

我们之前学习:

  • express 编写接口
  • mysql  --orm框架 读写数据库

现在,我们需要把他们糅合在一起 实现类似nest.js 或java 的springboot 的框架

 

 MVC 软件架构模式

  1. Model(模型)

    • 职责:表示应用程序的数据业务逻辑。它负责与数据源(如数据库)进行交互,获取、存储和修改数据。模型独立于用户界面,不直接与用户交互。
    • 示例:在一个在线商店应用中,模型部分可以是代表商品、购物车、用户等的类
  2. View(视图)

    • 职责:负责显示数据(模型)给用户,并生成用户界面。视图关注数据的展示,但不直接处理数据的变化。视图通常接收来自模型的数据,并将其呈现给用户。
    • 示例:HTML 页面、图形界面组件(按钮、文本框等)以及报表等。
  3. Controller(控制器)

    • 职责:作为模型和视图之间的中介,控制器处理用户的输入并决定如何更新模型和视图。当用户与视图交互时,控制器接收输入、调用模型进行数据处理,并更新视图显示。
    • 示例:在一个在线商店应用中,控制器接收用户的点击操作,比如“添加到购物车”按钮,调用相应的模型方法更新购物车数据,然后更新视图显示。

MVC 架构的工作流程:

  1. 用户交互:用户通过视图与应用程序进行交互(例如点击按钮、提交表单等)。
  2. 控制器处理请求:控制器接收到用户的输入,处理业务逻辑,并根据需要更新模型或视图。
  3. 模型更新:控制器更新模型(如修改数据、查询数据库等)。
  4. 视图更新:模型的变化触发视图更新,视图展示最新的数据给用户。

MVC 的优点:

  1. 分离关注点:将数据(模型)、用户界面(视图)和业务逻辑(控制器)分离,使得各个模块独立,便于开发、维护和扩展。
  2. 可重用性:视图和模型是相互独立的,因此它们可以重用。例如,可以通过不同的视图展示相同的数据。
  3. 易于维护:由于代码模块化,修改一个模块通常不会影响到其他模块,方便代码维护。

应用场景:

MVC 架构广泛应用于 Web 应用程序、桌面应用程序等,尤其适用于用户界面复杂的应用程序。常见的 Web 开发框架如 DjangoRuby on RailsAngularSpring MVC 都基于 MVC 架构。

简而言之,MVC 架构的核心思想是:通过分离模型、视图和控制器来增强代码的模块化、可维护性和可扩展性。

 

 

 

IoC(控制反转)DI(依赖注入)

     是软件开发中的重要概念,特别是在面向对象编程中,它们有助于增强应用程序的模块化、可扩展性和可维护性。虽然这两个概念紧密相关,但它们有不同的定义和作用。

1. 控制反转(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

元数据(Metadata)

是关于数据的数据。简单来说,元数据提供了对数据的描述、解释或结构信息,它不直接涉及数据的内容,而是描述数据的特征、性质或上下文。

常见的元数据类型

  1. 描述性元数据:描述资源的基本信息,例如标题、作者、创建日期、关键词等。

  2. 结构性元数据:描述数据或资源如何被组织和格式化。例如,数据库表结构、XML 文档的元素和属性等。

  3. 管理性元数据:描述数据的创建、存储、访问权限、版本控制等。例如,文件的创建日期、访问权限、修改历史等。

  4. 技术性元数据:涉及资源如何被存储和技术实现的细节。例如,文件格式、压缩算法、编码标准等。

元数据的作用

  • 提高查找效率:通过元数据,系统可以快速索引和搜索数据。例如,图像的EXIF数据可以帮助你根据拍摄日期或相机型号来筛选图片。

  • 数据管理:元数据帮助组织和管理数据,使得数据可以被有效地访问、分析和修改。例如,数据库的表结构元数据帮助程序开发人员理解数据模型。

  • 数据描述和解释:元数据可以为数据提供更详细的背景和上下文,帮助用户或应用程序更好地理解数据的含义。例如,在图书馆系统中,书籍的元数据包括书名、作者、出版日期等,帮助用户理解书籍的内容和来源。

现实中的例子

  • 图书馆的书籍信息:书籍的元数据包括书名、作者、出版日期、ISBN 等,不是书籍的内容,但这些信息帮助我们识别、查找和管理书籍。

  • 网页中的元数据:在 HTML 中,<meta> 标签用于定义网页的元数据,例如网页的描述、关键词、字符集等。这些元数据有助于搜索引擎优化(SEO)和页面的正确渲染。

 

元组数据的反射(Reflection on Tuple Data)

  是指在运行时动态地获取和操作元组数据结构的信息。元组是一种数据结构,它允许你将不同类型的数据组合在一起,但每个元素的位置和类型是固定的。

在TypeScript中,元组的反射可以通过Reflect对象来实现。Reflect是一个内置对象,它提供了一系列静态方法,用于操作对象和元组等数据结构。

以下是一些常用的Reflect方法,用于元组数据的反射:

  1. Reflect.get:获取对象属性的值。
  2. Reflect.set:设置对象属性的值。
  3. Reflect.has:检查对象是否具有某个属性。
  4. Reflect.deleteProperty:删除对象的属性。
  5. 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)原始对象来实现对其功能的增强。

基本概念

修饰器主要有两个作用:

  1. 增强对象的功能:在对象的原有功能基础上,增加新的功能或改变原有行为。
  2. 不修改对象本身:使用修饰器来增强对象的功能,而不需要改变对象的类或原始实现。

修饰器的实现

在很多现代编程语言中,修饰器通常以函数或类的形式出现,通过某些方式(如装饰器、函数包装等)修改目标对象的行为。

TypeScript 中的对象修饰器

在 TypeScript 中,修饰器是一个特殊的声明,它能够附加到类、方法、属性或参数上。你可以用它来修改或增强目标对象的行为。TypeScript 支持的修饰器包括 类修饰器方法修饰器访问器修饰器属性修饰器参数修饰器

 

打开配置 tsconfig.json

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES5"  // 或更高版本
  }
}

注:

  1. experimentalDecorators 作用是启用 TypeScript 对装饰器的实验性支持。例如当你在代码中使用 @Decorator 语法时(如类装饰器、方法装饰器、属性装饰器等)
  2. emitDecoratorMetadata 作用是装饰器生成 元数据(Metadata),提供额外的类型信息(如参数类型、返回值类型等),需要安装依赖reflect-metadata 库,在 依赖注入(DI)框架(如 Angular、NestJS)、序列化库(如 class-transformer)应用广泛

    获取元数据的示例:

import "reflect-metadata";

class MyClass {
  @LogExecutionTime
  method(param: string): number { ... }
}

// 通过元数据可以获取方法的类型信息
const paramTypes = Reflect.getMetadata("design:paramtypes", MyClass.prototype, "method");
console.log(paramTypes); // [String] - 参数类型信息

注:

  • design:type:属性/方法的类型(如 FunctionString)。

  • design:paramtypes:方法参数的类型数组(如 [String, Number])。

  • design:returntype:方法返回值的类型(如 Number)。

 

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();

//输出:

  //MyService is Injectable
  //MyService created!

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 {
    @LogExecutionTime
    public 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;
    }

    @LogAccess
    get 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 {
    @ReadOnly
    public 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."

 

posted @ 2025-03-31 17:10  蜗牛般庄  阅读(22)  评论(0)    收藏  举报
Title
页脚 HTML 代码