静态方法依赖其他类实例的运行原理及扩展场景疑问
问题解答
为什么当前代码能正常运行?
你当前的代码能工作的核心原因是:在调用DestinationController.clearCache()之前,已经有至少一个DestinationController的实例被创建过了。
当任何一个DestinationController实例被初始化时,构造函数会把传入的cacheController赋值给类的静态属性DestinationController.cacheController。这个静态属性是类级别的全局共享变量,一旦被赋值,后续所有静态方法调用都能访问到它。
但这种写法是不稳定且不符合设计规范的:
- 如果在没有创建
DestinationController实例的情况下直接调用clearCache,会直接抛出Cannot read properties of undefined (reading 'del')的错误 - 后续创建新的
DestinationController实例时,会覆盖静态属性cacheController的值,如果传入不同的CacheController实例,会导致所有静态方法使用新的实例,引发不可预期的行为 - 静态方法的可用性依赖于实例的创建,违背了静态方法“不依赖实例状态、可独立调用”的设计初衷
当CacheController依赖其他需要初始化的控制器时,该如何处理?
针对这种场景,推荐以下几种更可靠的方案:
1. 使用依赖注入(DI)容器管理实例
用DI容器(比如TypeDI、Inversify)统一管理所有控制器的生命周期和依赖关系:
- 将
CacheController注册为单例,确保全局只有一个实例 - 在容器初始化时,先创建
CacheController依赖的其他控制器,再创建CacheController实例 - 直接在
DestinationController的静态方法中从DI容器获取CacheController实例,或者通过容器注入到静态属性
示例代码(以TypeDI为例):
// 注册依赖 @Service() export class DependencyController { // ... } @Service({ scope: Scope.Singleton }) export class CacheController { constructor( private dependencyController: DependencyController, host: string, defaultTtlSeconds: number ) { this.client = createNodeRedisClient({ host }); this.defaultTtlSeconds = defaultTtlSeconds; } // ... } // 在DestinationController中使用 export class DestinationController { private static readonly CACHE_KEY = "destination::"; constructor( private readonly categoryController: CategoryController, // ... ) {} static async clearCache(destinationSlug: string): Promise<void> { const cacheController = Container.get(CacheController); await cacheController.del(this.CACHE_KEY + destinationSlug); } }
2. 重构静态方法为实例方法或独立工具类
如果clearCache的逻辑和DestinationController的实例状态无关,更合理的做法是:
- 将
clearCache移到CacheController中,直接通过CacheController的实例调用 - 或者创建一个独立的
DestinationCacheUtils工具类,注入CacheController实例后提供缓存操作方法
如果必须保留在DestinationController,则将其改为实例方法,调用时使用DestinationController的实例:
export class DestinationController { private static readonly CACHE_KEY = "destination::"; constructor( private readonly cacheController: CacheController, private readonly categoryController: CategoryController, // ... ) {} async clearCache(destinationSlug: string): Promise<void> { await this.cacheController.del(DestinationController.CACHE_KEY + destinationSlug); } } // 调用方式 const destController = getDestinationControllerInstance(); // 从DI容器或实例工厂获取 await destController.clearCache("someDestination");
3. 手动初始化静态属性(适合小型项目)
在应用启动的入口处,手动创建所有依赖的实例,然后直接赋值给静态属性:
// 应用启动文件 // 先创建CacheController依赖的控制器 const dependencyController = new DependencyController(...); // 创建CacheController实例 const cacheController = new CacheController("redis-host", 3600, dependencyController); // 赋值给DestinationController的静态属性 DestinationController.cacheController = cacheController; // 之后就可以安全调用静态方法 await DestinationController.clearCache("someDestination");
这种方式明确控制了初始化顺序,避免了依赖实例创建的副作用,但需要手动维护依赖关系,项目复杂时容易出错。
内容的提问来源于stack exchange,提问作者Thibault Muller
相关产品推荐
相关产品推荐

