typescript 如何解决类构造函数中的循环依赖?

vwhgwdsa  于 2023-05-01  发布在  TypeScript
关注(0)|答案(1)|浏览(178)

我有依赖于FilterService的CategoryService类:

export class FilterService implements IFilterService
{
  protected readonly categoryService: ICategoryService;
  
  constructor(
    categoryService: ICategoryService;
  ) {    
    this.categoryService = categoryService;
  }
}

以及依赖于FilterService的CategoryService:

export class CategoryService implements ICategoryService
    {
      protected readonly filterService: IFilterService;
      
      constructor(
        filterService: IFilterService;
      ) {    
        this.filterService = filterService;
      }
    }

如何解决这种循环依赖?

mwkjh3gx

mwkjh3gx1#

在OOP中组合/聚合时,循环引用通常会产生良好的人类可读逻辑。只要您不想序列化数据,它们就不会造成真实的的问题。
你可以寻找一个可以在序列化/反序列化过程中处理循环引用的库,但是你被“锁定”在那个特定库的循环引用的表达方式中。
因此,消除循环引用的最通用方法是存储id而不是对象引用。
使用getter和setter来'rehydrate'字段(包括列表)可以使这是一个相当愉快的体验,而几乎不需要更改应用程序逻辑。
下面是一个循环引用的代码示例,宠物知道它们的主人,主人反过来也知道它们的宠物等等。

循环引用代码

class Pet {

  constructor(name, species) {
    this.name = name;
    this.species = species;
  }

  chooseOwner(owner) {
    this.owner = owner;
    owner.buyPet(this);
  }
}

class PetOwner {
  pets = [];

  constructor(name) {
    this.name = name;
  }

  buyPet(pet) {
    if (this.pets.includes(pet)) { return; }
    this.pets = [...this.pets, pet];
    pet.chooseOwner(this);
  }
}

// Tests
let jon = new PetOwner('Jon');
let garfield = new Pet('Garfield', 'cat');
let odie = new Pet('Odie', 'dog');
garfield.chooseOwner(jon);
jon.buyPet(odie);
console.log('Jons pets', jon.pets);
console.log('Garfields owner', garfield.owner);
console.log('Odies owner', odie.owner);

没有循环引用的代码

这里是相同的例子,它被修改了,所以 * 它没有任何循环引用 *。
我们对程序中创建的所有示例使用“ObjectMemory”,这使得为每个示例设置一个id并在以后从内存中查找(在我们的getter和setter中)变得容易。只要记住不要改变列表,而是创建新的列表,从而触发有问题的列表的setter,一切正常。..

class ObjectMemory {
  static list = [];

  static add(object) {
    this.list = [...this.list, object];
    return this.list.length - 1;
  }
}

class Pet {
  #ownerId;
  get owner() { return ObjectMemory.list[this.#ownerId] }
  set owner(owner) { this.#ownerId = owner.id; }

  constructor(name, species) {
    this.name = name;
    this.species = species;
    this.id = ObjectMemory.add(this);
  }

  chooseOwner(owner) {
    this.owner = owner;
    owner.buyPet(this);
  }
}

class PetOwner {
  #petIds = [];
  get pets() { return this.#petIds.map(id => ObjectMemory.list[id]); }
  set pets(pets) { this.#petIds = pets.map(pet => pet.id); }

  constructor(name) {
    this.name = name;
    this.id = ObjectMemory.add(this);
  }

  buyPet(pet) {
    if (this.pets.includes(pet)) { return; }
    this.pets = [...this.pets, pet];
    pet.chooseOwner(this);
  }
}

// Tests
let jon = new PetOwner('Jon');
let garfield = new Pet('Garfield', 'cat');
let odie = new Pet('Odie', 'dog');
garfield.chooseOwner(jon);
jon.buyPet(odie);
console.log('Jons pets', jon.pets);
console.log('Garfields owner', garfield.owner);
console.log('Odies owner', odie.owner);

这种方法的额外好处:如果你现在想序列化东西,你真的只需要序列化ObjectMemory。list,因为它包含了所有id:s/lookups的示例。

相关问题