【问题标题】:How do you call a subclass method in the baseclass method?如何在基类方法中调用子类方法?
【发布时间】:2021-05-13 15:14:20
【问题描述】:

如果这个问题太令人困惑,我很抱歉,我会尽量简单。

TLDR;如何在 Typescript 基类中调用必须在子类中定义的子类的私有方法?

我有许多不同的 Typescript 子类,它们需要不同的代码来执行相同的操作:写入文件。

90% 的基本代码是相同的,每个子类都有一些方法的具体实现。

所以我采用了相同的代码并将其放在一个基类中,但它调用了一个必须在子类上实现的方法,并且最好是私有的。

这是一个例子:

abstract class BaseClass {
  constructor() { }

  SetOptions() {
    // Set the options
    // Return the options
  }

  GetOptions() {
    // Get the options
    this.WriteStuff();
  }

  private WriteStuff(arg1: Item, arg2: number) {}
}

class SubClass1 extends BaseClass {
  private WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

class SubClass2 extends BaseClass {
  private WriteStuff(arg1: SubClass2Item, number: number) {
    // Write stuff the SubClass2 way
  }
}

SubClass1Item 和 SubClass2Item 实现了 Item 接口。

所有子类对 SetOptions 和 GetOptions 使用相同的代码,但 WriteStuff 在每个子类中都有个性化的代码,并且需要是私有的,所以它不会被其他开发人员意外调用。

Typescript 说我不能这样做,因为“类型具有私有属性 'WriteStuff' 的单独声明”。并且 Typescript 不会让我不在 BaseClass 中声明该方法,因为那时我正在调用一个不存在但将存在于子类中的方法。

这可能吗?

【问题讨论】:

  • 方法不应该是私有的,应该是抽象的。 (或受保护的摘要)
  • 这本质上与private 方法的想法不兼容。它必须是protected(或public)才能让父母/孩子访问它。
  • 您可能还需要将其设为通用以支持不同类型的Item
  • 如前所述:如果您需要 any 外部代码来使用它,无论是“任何代码”、“同一包中的某些代码”还是“超类中的某些代码”根据定义,代码不是私有的。就基于类的 OO 而言,孩子们知道他们的超类的方法,但反过来却不知道(也不应该)。调用this.functionName() 将始终运行实例函数,如果有的话,或者沿着类链向上直到找到一个,然后运行它。

标签: javascript typescript inheritance subclass private-members


【解决方案1】:

私人与受保护

private 方法只能在类本身中访问,不能在任何后代中访问。你不能有一个private abstract 方法,因为这是一个内在的矛盾——如果后代需要实现它,那么它就不是private

您想使用protected 修饰符。每the docs

protected 修饰符的作用与 private 修饰符非常相似,但声明为 protected 的成员也可以在派生类中访问。

你想在你的BaseClass 中声明所有的concretions 必须实现一个WriteStuff() 方法。这是一个abstract 方法,这意味着它在BaseClass 中没有实现,并且必须被覆盖。

在你的后代中,WriteStuff 的实现必须是protected 而不是private

代码

abstract class BaseClass {
  /* .... */
  protected abstract WriteStuff(arg1: Item, arg2: number): void;
}
class SubClass1 extends BaseClass {
  protected WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

Playground Link

arg1 类型

您的类并非都共享相同的签名,因为它们对于传递给WriteStuff 的第一个参数arg1 都有不同的类型。您很可能希望根据 arg1 的类型或其他值使用 generic class

如果SubClass1ItemSubClass2Item 不扩展Item,那么您绝对需要一个泛型类,因为子类中WriteStuff 的实现不能分配给基类。

如果他们扩展Item,那么当前设置不会出现打字稿错误。但是,当您在 BaseClass 中从 GetOptions() 调用 this.WriteStuff 时,可能会出现运行时错误。 BaseClass 如何知道它拥有的Item 是否可分配给SubClass1SubClass2 中预期的Item 子类型?这就是泛型可以提供帮助的地方。

我们将BaseClass 设为基于变量ItemType 的泛型。我们可以要求所有ItemType 变量扩展一个共享基Item,但我们不必这样做。

子类本身不是通用类。他们将使用他们需要的特定ItemType 扩展BaseClass 的版本。

代码

abstract class BaseClass<ItemType extends Item> {
  /* ... */
  
  GetOptions(item: ItemType) {
    // Get the options
    this.WriteStuff(item, 5);
  }

  protected abstract WriteStuff(arg1: ItemType, arg2: number): void;
}
class SubClass1 extends BaseClass<SubClass1Item> {
  protected WriteStuff(arg1: SubClass1Item, number: number) {
    // Write stuff in the SubClass1 way
  }
}

Playground Link

【讨论】:

  • 琳达!这正是我一直在寻找的答案。感谢您清晰而充实的回答。对此,我真的非常感激。这成功了。你说得对,直到现在我才想到,我需要泛型。你超越了回答我的问题。我希望我能给你更多的分数。谢谢。
【解决方案2】:

JavaScript 最近支持 private field declarations,包括 private instance methods。在那里,一种可能的方法是为基类的超级调用提供子类特定的实现......

class BaseClass {

  // default implementation of private instance method.
  #writeStuff = () => void 0;

  constructor(writeStuff) {
    // at instantiation time, if appropriate, ...
    if (typeof writeStuff === 'function') {
    
      // ... overwrite the default implementation.
      this.#writeStuff = writeStuff;
    }
  }
  
  // prototypal method ...
  getOptions(...args) {
  
    // ... with access to a private instance method.
    this.#writeStuff(...args);
  }
  setOptions() {/* ... */}
}

class SubClass1 extends BaseClass {
  constructor() {

    const writeStuff = (...args) =>
      // Write stuff in the SubClass1 way
      console.log(`SubClass1 [...args] : ${ [...args] }`);

    super(writeStuff);

    // do other things
  }
}
class SubClass2 extends BaseClass {
  constructor() {

    const writeStuff = (...args) =>
      // Write stuff in the SubClass2 way
      console.log(`SubClass2 [...args] : ${ [...args] }`);

    super(writeStuff);

    // do other things
  }
}

const objA = new SubClass1;
const objB = new SubClass2;

objA.getOptions('foo', 'bar');
objB.getOptions('baz', 'biz');
.as-console-wrapper { min-height: 100%!important; top: 0; }

【讨论】:

    猜你喜欢
    • 2011-01-05
    • 1970-01-01
    • 2012-06-28
    • 2010-12-05
    • 1970-01-01
    • 2018-10-15
    • 1970-01-01
    • 2016-10-05
    • 1970-01-01
    相关资源
    最近更新 更多