创卫网站 建设 方案,建设银行网站图片大全,wordpress怎么改页面底部,电子商务网站建设与管理教案策略模式#xff08;Strategy Pattern#xff09;是一种软件设计模式#xff0c;它定义了算法族#xff0c;分别封装起来#xff0c;使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。这种模式涉及到三个角色#xff1a;
上下文#xff08;Context…策略模式Strategy Pattern是一种软件设计模式它定义了算法族分别封装起来使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。这种模式涉及到三个角色
上下文Context持有一个策略类的引用用来与策略类交互。策略接口Strategy Interface定义了每个策略或算法必须遵循的接口。具体策略Concrete Strategies实现策略接口的类提供具体的算法实现。
策略模式的优点
分离算法策略模式通过分离算法和上下文来提高内聚性和灵活性。易于扩展可以定义新的策略而不影响到其他的代码。避免条件语句策略模式避免了使用多重条件选择语句。
举例说明
假设我们正在开发一个导航系统需要支持多种路线规划算法例如最短路径、最少时间和避开高速。 当使用 TypeScript 来实现策略模式时基本的模式和概念与 Java 类似只是语法稍有不同。以下是使用 TypeScript 实现策略模式的示例
首先定义策略接口和具体策略类
// 策略接口
interface RouteStrategy {buildRoute(pointA: string, pointB: string): string;
}// 具体策略类 - 最短路径策略
class ShortestPathStrategy implements RouteStrategy {buildRoute(pointA: string, pointB: string): string {return 最短路径从 ${pointA} 到 ${pointB};}
}// 具体策略类 - 最少时间策略
class MinTimeStrategy implements RouteStrategy {buildRoute(pointA: string, pointB: string): string {return 最少时间路径从 ${pointA} 到 ${pointB};}
}// 具体策略类 - 避开高速策略
class AvoidHighwaysStrategy implements RouteStrategy {buildRoute(pointA: string, pointB: string): string {return 避开高速的路径从 ${pointA} 到 ${pointB};}
}接下来创建上下文类和使用策略模式
// 上下文类
class NavigationContext {private strategy: RouteStrategy;setRouteStrategy(strategy: RouteStrategy): void {this.strategy strategy;}buildRoute(pointA: string, pointB: string): string {return this.strategy.buildRoute(pointA, pointB);}
}// 使用策略模式
const context new NavigationContext();// 选择最短路径策略
context.setRouteStrategy(new ShortestPathStrategy());
console.log(context.buildRoute(起点, 终点));// 切换到最少时间策略
context.setRouteStrategy(new MinTimeStrategy());
console.log(context.buildRoute(起点, 终点));// 切换到避开高速策略
context.setRouteStrategy(new AvoidHighwaysStrategy());
console.log(context.buildRoute(起点, 终点));在这个例子中RouteStrategy 是策略接口ShortestPathStrategy、MinTimeStrategy 和 AvoidHighwaysStrategy 是实现了不同路线规划算法的具体策略类。NavigationContext 是上下文负责接受不同的策略并使用它们来构建路线。这样当需要改变路线规划算法时只需更换不同的策略类即可无需修改 NavigationContext 的代码。
理解和运用这些设计模式可以帮助你更有效地使用 NestJS 构建可维护和可扩展的应用程序。
在 NestJS 中策略模式主要用于以下几个领域
Authentication
在 NestJS 中实现策略模式主要是通过 Guards 来完成的特别是在处理授权Authorization时。我们可以通过定义不同的 Guards 来实现不同的授权策略例如基于角色的授权RBAC或者是基于权限的授权。
示例基于角色的访问控制RBAC
假设我们有一个简单的应用程序它有两种用户角色普通用户User和管理员Admin。我们想要实现的是某些操作只能由管理员执行。
步骤 1定义角色
首先我们定义一个角色枚举Enum。
export enum Role {User user,Admin admin,
}步骤 2创建 Roles 装饰器
接下来我们创建一个自定义装饰器来标记特定的路由需要特定的角色。
import { SetMetadata } from nestjs/common;export const Roles (...roles: Role[]) SetMetadata(roles, roles);步骤 3实现 RolesGuard
然后我们实现一个 RolesGuard它将检查用户是否具有访问特定路由所需的角色。
import { CanActivate, ExecutionContext, Injectable } from nestjs/common;
import { Reflector } from nestjs/core;Injectable()
export class RolesGuard implements CanActivate {constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext): boolean {const roles this.reflector.getRole[](roles, context.getHandler());if (!roles) {return true;}const request context.switchToHttp().getRequest();const user request.user;return roles.some(role user.roles?.includes(role));}
}在这个 Guard 中我们使用 Reflector 来获取与当前路由处理程序相关联的角色。然后我们检查当前用户是否具有这些角色之一。
步骤 4应用 RolesGuard
最后我们需要在模块中注册这个 Guard 并应用到具体的路由上。
// 在模块中
Module({providers: [{provide: APP_GUARD,useClass: RolesGuard,},],
})
export class AppModule {}// 在控制器中
Controller(items)
export class ItemsController {Get()Roles(Role.Admin) // 只有管理员可以访问findAll() {// ...}
}在这个例子中RolesGuard 作为一个策略用于控制对特定路由的访问。通过简单地更改 Roles 装饰器中的参数我们可以轻松地改变访问控制的策略而无需修改其他业务逻辑。
Logging
在 NestJS 中实现日志策略模式通常涉及到自定义日志服务。这样你可以根据需要切换或扩展不同的日志策略例如输出日志到控制台、文件、远程服务器等。
示例自定义日志服务
假设我们需要实现两种日志策略一种是简单地将日志输出到控制台另一种是将日志记录到文件中。
步骤 1定义日志接口
首先我们定义一个日志接口LoggerService它描述了日志服务应该实现的方法。
interface LoggerService {log(message: string): void;error(message: string, trace: string): void;warn(message: string): void;debug(message: string): void;verbose(message: string): void;
}步骤 2实现具体的日志策略
接下来我们实现两个具体的日志策略分别是 ConsoleLogger 和 FileLogger。
Injectable()
class ConsoleLogger implements LoggerService {log(message: string) { console.log(message); }// ...实现其他方法
}Injectable()
class FileLogger implements LoggerService {log(message: string) {// 将消息写入文件}// ...实现其他方法
}步骤 3动态选择日志策略
然后我们可以根据需要在应用中动态选择使用哪个日志策略。
Module({providers: [{provide: LoggerService,useClass: process.env.NODE_ENV development ? ConsoleLogger : FileLogger,},],
})
export class AppModule {}在这个模块中我们根据环境变量来决定使用 ConsoleLogger 还是 FileLogger。
步骤 4使用日志服务
在应用的其他部分我们可以注入并使用 LoggerService。
Controller(items)
export class ItemsController {constructor(Inject(LoggerService) private logger: LoggerService) {}Get()findAll() {this.logger.log(Fetching all items);// ...业务逻辑}
}在这个控制器中我们通过构造函数注入了 LoggerService。不论底层使用的是哪种日志策略我们都可以通过相同的方式记录日志。
Exception Handling
在 NestJS 中异常处理通常通过异常过滤器Exception Filters来实现这可以被视为一种策略模式的应用。异常过滤器允许你定义不同的处理策略来处理不同类型的异常。以下是一个详细的例子展示如何在 NestJS 中使用异常过滤器来实现异常处理的策略模式。
示例自定义异常过滤器
假设我们的应用需要特定的处理方式来处理数据库异常和 HTTP 异常。
步骤 1创建自定义异常过滤器
首先我们创建两个异常过滤器一个用于处理数据库异常另一个用于处理 HTTP 异常。
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from nestjs/common;Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx host.switchToHttp();const response ctx.getResponse();const status exception.getStatus();response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),message: exception.message,});}
}Catch(DatabaseException)
export class DatabaseExceptionFilter implements ExceptionFilter {catch(exception: DatabaseException, host: ArgumentsHost) {// 处理数据库异常的逻辑}
}步骤 2注册异常过滤器
接下来我们需要在应用中注册这些异常过滤器。你可以全局注册或针对特定控制器或路由注册。 全局注册: Module({// ...providers: [{provide: APP_FILTER,useClass: HttpExceptionFilter,},{provide: APP_FILTER,useClass: DatabaseExceptionFilter,},],
})
export class AppModule {}针对特定控制器的注册: Controller(users)
UseFilters(new HttpExceptionFilter(), new DatabaseExceptionFilter())
export class UsersController {// ...
}步骤 3触发异常
在应用的任何地方抛出异常对应的过滤器将会捕获并处理它。
Controller(users)
export class UsersController {Get(:id)findOne(Param(id) id: string): string {throw new HttpException(User not found, HttpStatus.NOT_FOUND);}
}在这个例子中当 findOne 方法抛出 HttpException 时HttpExceptionFilter 会被触发并按照其逻辑处理异常。
Request Processing
在 NestJS 中请求处理Request Processing通常涉及拦截器Interceptors这些拦截器可以被视为一种策略模式的实现。拦截器允许你在请求处理流程中插入自定义逻辑比如日志记录、响应转换、错误处理等。接下来我将通过一个详细的例子来说明如何在 NestJS 中使用拦截器实现请求处理的策略。
示例响应转换拦截器
假设我们需要一个拦截器来统一格式化所有 API 响应。这个拦截器将拦截出站响应并将其转换为一个标准的格式。
步骤 1创建拦截器
首先我们创建一个名为 TransformInterceptor 的拦截器。
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from nestjs/common;
import { Observable } from rxjs;
import { map } from rxjs/operators;Injectable()
export class TransformInterceptorT implements NestInterceptorT, ResponseT {intercept(context: ExecutionContext, next: CallHandler): ObservableResponseT {return next.handle().pipe(map(data ({data,timestamp: new Date().toISOString(),path: context.switchToHttp().getRequest().url,})));}
}interface ResponseT {data: T;timestamp: string;path: string;
}在这个拦截器中我们通过 rxjs 的 map 操作符来转换处理函数返回的数据。我们把数据包装成一个对象其中包含数据、时间戳和请求路径。
步骤 2注册拦截器
接下来我们需要在应用中注册这个拦截器。有两种方式可以注册拦截器全局注册和针对特定路由的注册。 全局注册: Module({// ...providers: [{provide: APP_INTERCEPTOR,useClass: TransformInterceptor,},],
})
export class AppModule {}针对特定路由的注册: 在特定控制器或者处理函数上使用拦截器 Controller(items)
UseInterceptors(TransformInterceptor)
export class ItemsController {// ...
}步骤 3使用拦截器
一旦拦截器被注册它就会自动应用于你的请求处理流程。在上面的例子中任何通过 ItemsController 的响应都会被 TransformInterceptor 拦截并格式化。
Validation and Transformation
从源码层面详细分析 NestJS 中的管道Pipes是一个涉及到多个文件和类的复杂过程但我会尽量简化并解释关键部分。
管道的基本原理
在 NestJS 中管道Pipes是负责处理输入数据的中间件它们在控制器处理函数执行之前运行。管道可以执行数据转换或数据验证。当你在控制器的参数前使用管道NestJS 会在将请求传递给处理函数之前执行这些管道。
核心类ValidationPipe
以 ValidationPipe 为例这是一个内置的管道通常用于 DTOData Transfer Object验证。我们将从它的源码开始分析。 ValidationPipe的定义 ValidationPipe 是一个实现了 PipeTransform 接口的类。PipeTransform 接口要求实现一个名为 transform 的方法。 export class ValidationPipe implements PipeTransformany {async transform(value, metadata: ArgumentMetadata) {// ...验证逻辑}
}使用 Class Validator 进行验证 ValidationPipe 的 transform 方法使用 class-validator 库来验证输入数据。如果数据不符合 DTO 定义的规则它会抛出异常。 import { validate } from class-validator;async transform(value, { metatype }): Promiseany {if (!metatype || !this.toValidate(metatype)) {return value;}const object plainToClass(metatype, value);const errors await validate(object);if (errors.length 0) {throw new BadRequestException(Validation failed);}return value;
}调用管道 当请求到达控制器时NestJS 会根据控制器方法的装饰器如 Body来确定需要应用哪个管道。 在 Body(new ValidationPipe()) 的情况下NestJS 会创建一个 ValidationPipe 实例并调用它的 transform 方法将传入的请求体作为参数。 异常处理 如果验证失败ValidationPipe 会抛出一个 BadRequestException。NestJS 捕获这个异常并根据异常类型生成相应的 HTTP 响应。