Angular依赖注入类:如何在单类中存储不同JSON数据?
解决方案:无需为每个JSON文件创建单独仓库类
你完全不用为每个物种编写单独的仓库子类,Angular的DI体系下有两种更优雅的方式解决这个问题:
方案一:工厂模式(推荐,贴合你的仓库设计思路)
通过一个工厂服务来创建不同物种对应的SpeciesRepository实例,每个实例独立维护对应物种的数据,既保留DI的优势,又能实现多实例隔离。
步骤1:改造基础仓库类
去掉@Injectable(因为它由工厂实例化,而非DI容器直接管理),将speciesType作为构造参数传入:
export class SpeciesRepository { private data: string[] | null = null; private loadPromise: Promise<void> | null = null; constructor( private httpClient: HttpClient, private readonly speciesType: string ) {} // 确保数据仅加载一次 async load(): Promise<void> { if (this.loadPromise) return this.loadPromise; if (this.data) return; this.loadPromise = this.httpClient.get<string[]>(`assets/${this.speciesType}.json`) .toPromise() .then(data => { this.data = data; this.loadPromise = null; }) .catch(err => { this.loadPromise = null; throw err; }); return this.loadPromise; } getByIndex(index: number): string | undefined { return this.data?.[index]; } }
步骤2:创建仓库工厂服务
用DI管理工厂,工厂负责生成对应物种的仓库实例:
@Injectable({ providedIn: 'root' }) export class SpeciesRepositoryFactory { constructor(private httpClient: HttpClient) {} create(speciesType: string): SpeciesRepository { return new SpeciesRepository(this.httpClient, speciesType); } }
步骤3:在组件/服务中使用
比如地牢生成组件预加载数据:
@Component({ /* 组件配置 */ }) export class GenerateDungeonComponent { private humanRepo: SpeciesRepository; private dogRepo: SpeciesRepository; private catRepo: SpeciesRepository; constructor(private repoFactory: SpeciesRepositoryFactory) { this.humanRepo = repoFactory.create('human'); this.dogRepo = repoFactory.create('dog'); this.catRepo = repoFactory.create('cat'); } async ngOnInit() { // 并行预加载所有需要的物种数据 await Promise.all([ this.humanRepo.load(), this.dogRepo.load(), this.catRepo.load() ]); // 数据加载完成,继续地牢生成逻辑 } }
房间组件中直接使用预加载好的仓库实例(可通过服务共享或输入传递):
@Component({ /* 组件配置 */ }) export class RoomComponent { @Input() speciesRepos: SpeciesRepository[] = []; getRandomSpeciesName(repo: SpeciesRepository): string | undefined { const dataLength = repo.getByIndex(0) ? repo.getByIndex(0).length : 0; return repo.getByIndex(Math.floor(Math.random() * dataLength)); } }
方案二:单例仓库维护多物种数据
如果不想管理多个实例,可以改造单例仓库,用Map存储不同物种的数据,实现单例下的多物种隔离:
@Injectable({ providedIn: 'root' }) export class SpeciesRepository { private speciesData = new Map<string, string[]>(); private loadPromises = new Map<string, Promise<void>>(); constructor(private httpClient: HttpClient) {} async load(speciesType: string): Promise<void> { if (this.loadPromises.has(speciesType)) { return this.loadPromises.get(speciesType)!; } if (this.speciesData.has(speciesType)) { return; } const promise = this.httpClient.get<string[]>(`assets/${speciesType}.json`) .toPromise() .then(data => { this.speciesData.set(speciesType, data); this.loadPromises.delete(speciesType); }) .catch(err => { this.loadPromises.delete(speciesType); throw err; }); this.loadPromises.set(speciesType, promise); return promise; } getByIndex(speciesType: string, index: number): string | undefined { return this.speciesData.get(speciesType)?.[index]; } }
使用时只需在调用方法时指定物种类型:
// 地牢生成组件预加载 await Promise.all([ this.speciesRepo.load('human'), this.speciesRepo.load('dog') ]); // 房间组件获取数据 const randomDogName = this.speciesRepo.getByIndex('dog', Math.floor(Math.random() * 2));
方案选择建议
- 工厂模式:每个物种有独立的仓库实例,代码语义更清晰,适合需要将仓库实例传递给不同服务/组件的场景(贴合你的DDD分层思路)。
- 单例多物种模式:实现更轻量,无需管理多个实例,适合仅需在调用时指定物种的简单场景。
两种方案都能避免新增物种时编写新类的问题,后续只需添加JSON文件,在业务逻辑中指定对应的物种类型即可。
内容的提问来源于stack exchange,提问作者James Morgan
相关产品推荐
相关产品推荐

