【问题标题】:Javascript: How to reuse method for creating child instances without creating circular dependenciesJavascript:如何在不创建循环依赖项的情况下重用创建子实例的方法
【发布时间】:2018-09-10 06:39:18
【问题描述】:
abstract class Fruit {
    private content: Fruit[] = [];

    addChild() {
        // Pick one at random (using this as an example instead of the actual criteria that determines this)
        const type = pickOne(['apple', 'banana', 'cherry']);

        switch (type) {
            case 'apple':
                this.content.push(new Apple());
            case 'banana':
                this.content.push(new Banana());
            case 'cherry':
                this.content.push(new Cherry());
        }
    }
}

class Apple extends Fruit { }

class Banana extends Fruit { }

class Cherry extends Fruit { }

如何在不创建循环依赖的情况下对其进行重组,以便:

  1. 每个类都在一个单独的文件中
  2. addChild() 方法适用于所有孩子,无需复制代码

我已经读到,基类了解子类的任何信息通常是一种不好的模式,但我不确定更好的模式是什么样的。

Example that might make more sense

编辑:删除 type 作为参数

【问题讨论】:

  • 我认为这通常取决于用例。你真的应该考虑一下你可能会遇到什么问题,特别是如果你想改变任何东西。
  • 孩子们既是 Fruit 的子类又是其内容的一部分,这似乎很奇怪。你的类不应该是 FruitContainer 并且水果不应该继承自它吗?
  • 您的内容数组是否有理由按子类类型拆分,而不仅仅是 Array
  • @Axnyff 所有水果都可以有一系列嵌套的水果(水果名称实际上没有意义,抱歉)
  • @WilliamOliver 是的,这可能更有意义。我试图表明 Fruit 类本身并没有被使用,但将其切换为抽象类更有意义。

标签: javascript node.js typescript


【解决方案1】:

你需要从工厂类中拆分抽象类(创建新实例):

// fruit.ts
abstract class Fruit {
    private content: Array<Fruit> = [];

    addChild(child: Fruit) {
        this.content.push(child);
    }
}

// fruit-factory.ts
class FruitFactory {
    create(type: 'apple' | 'banana' | 'cherry'): Fruit {
        switch (type) {
            case 'apple':
                return new Apple();
            case 'banana':
                return new Banana();
            case 'cherry':
                return new Cherry();
        }
    }
}

// apple.ts
class Apple extends Fruit { }

// banana.ts
class Banana extends Fruit { }

// cherry.ts
class Cherry extends Fruit { }

Factory design pattern 对于隔离和重用创建对象的代码很有用。

在您更详细的示例中,loadChildren 不必绑定到 List 抽象类。这使得List 抽象有 3 个职责:作为列表、从外部源加载数据和创建新实体。

可维护代码的指导原则是single responsibility principle,其中每个类都有一个单独的职责。

我建议您根据已确定的职责将List 对象拆分为3 个类:abstract class Listclass ListFactoryclass DataFetcher

这将允许您为这些类中的每一个定义一个好的接口。

【讨论】:

  • 谢谢!这可能是我所缺少的,将对此进行更多阅读。
  • 这表示 Fruit 不能使用 FruitFactory 对吧?如果addChild() 方法非常复杂并且必须确定正确的类型,那么我认为它不会那么好用。在这种情况下,解决方案是将addChild() 方法移动到一个不依赖于Fruit 的单独文件中,然后将其导入到每个子水果类中吗?
  • 没有更好的例子很难回答你的问题。在这里,我认为水果有孩子是没有意义的。您能解释一下您的用例吗?我可能会为您提供帮助?
  • 对不起,让我试着想一个比水果更好的例子。也许列表?每个列表中都可以有一个列表。它可能是ulol,然后您有一个方法fetchChild,它根据id 获取子列表并根据响应创建ulol。这更有意义吗?
  • 我认为该解决方案也适用于列表。您定义了一个抽象 AbstractList,它定义了一个 addChildfetchChild 方法以及 2 个子类 UlListOlList。这只是扩展了AbstractList
【解决方案2】:

您可以简单地传递构造函数而不是字符串:

abstract class Fruit {
    private content: Fruit[] = [];

    addChild<F extends Fruit>(C: new (...args: any) => F) {
        this.content.push(new C());
    }
}

class Apple extends Fruit { }

class Banana extends Fruit { }

class Cherry extends Fruit { }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    • 2020-07-15
    • 2018-09-12
    • 2019-11-13
    相关资源
    最近更新 更多