Структурная типизация и полиморфизм - добавление супертипов

Ниже приведен пример кода (полиморфный), взятый из angular framework с использованием синтаксиса TypeScript,

export abstract class AbstractControlDirective {
   ...
}

AbstractControlDirective подтипы

export abstract class NgControl extends AbstractControlDirective {
     ...
}
export abstract class ControlContainer extends AbstractControlDirective {
   ...
}
export declare abstract class ControlContainer extends AbstractControlDirective {
  ...
}
export declare abstract class NgControl extends AbstractControlDirective {
    ...
}

NgControl подтипы

export class NgModel extends NgControl implements OnChanges, OnDestroy {
   ...
}

export class FormControlDirective extends NgControl implements OnChanges {
    ....
}

export class FormControlName extends NgControl implements OnChanges, OnDestroy {
   ...
}

export declare class FormControlDirective extends NgControl implements OnChanges {
    ...
}

export declare class FormControlName extends NgControl implements OnChanges, OnDestroy {
   ...
}

export declare class NgModel extends NgControl implements OnChanges, OnDestroy {
   ...
}

В общем, есть много ситуаций, в которых встречаются новые требования, чтобы добавить очевидный супертип на промежуточных уровнях иерархии классов, который вводит поломку в подтипы, если не используется какой-либо шаблон проектирования. Использование шаблона проектирования может сделать иерархию классов менее подверженной ошибкам, но нарушает структуру иерархии.


Чтобы избежать этой проблемы, можем ли мы поддерживать эту иерархию без использования ключевого слова extends? TypeScript структурно типизирован ...


person overexchange    schedule 28.04.2018    source источник
comment
Все, о чем я могу думать, это об использовании интерфейсов или implementing вместо расширения абстрактных классов. Какие шаблоны проектирования, по вашему мнению, могут предотвратить это? Мне было бы интересно увидеть их   -  person Drenai    schedule 29.04.2018


Ответы (1)


Класс должен наследовать от родителя (ключевое слово extends в классах ES6), если выполняется одно или несколько следующих условий:

  • родительский класс имеет явный конструктор, который должен быть унаследован в дочернем классе, включая поля класса и свойства параметров конструктора (которые являются синтаксическим сахаром для тела конструктора класса ES6).

  • родительский класс имеет конкретные члены-прототипы (методы и геттеры / сеттеры), которые должны присутствовать в дочернем классе

  • экземпляры дочернего класса должны быть идентифицированы как экземпляры родительского класса во время выполнения с child instanceof Parent

  • родительский класс имеет декораторы, которые влияют на все вышеперечисленное

В противном случае наследование не дает никаких преимуществ и приводит к чрезмерной цепочке прототипов; дочерний класс может просто реализовать родительский интерфейс (ключевое слово implements).

AbstractControlDirective содержит конкретные члены, которые должны быть унаследованы в дочерних классах, поэтому _ 5_ расширяет его:

export abstract class NgControl extends AbstractControlDirective {...}

HttpXsrfTokenExtractor содержит только абстрактные элементы, поэтому HttpXsrfCookieExtractor не нужно его расширять, а просто реализует как интерфейс:

export class HttpXsrfCookieExtractor implements HttpXsrfTokenExtractor {...}
person Estus Flask    schedule 28.04.2018
comment
К вашему сведению: родительский класс имеет конкретные члены, которые должны присутствовать в дочернем классе. Вы сталкивались со сценарием, в котором вы добавляете метод в родительский класс, который нужен для 4 дочерних классов, но не для 2 других дочерних классов (поскольку это не имеет смысла). Легко сказать, что иерархия классов была плохо спроектирована, поскольку мы попали в этот сценарий. Но как бы вы отреагировали на этот сценарий? - person overexchange; 29.04.2018
comment
Во-первых, какое мне дело, если затронуты 2 или более дочерних класса? Я хочу настроить таргетинг только на эти 4 дочерних класса. Для категории, имеющей эти 2, я наткнулся на десятки классов в реальном времени, которые пострадали после того, как код был запущен в производство. - person overexchange; 29.04.2018
comment
С точки зрения тестирования область применения таких изменений увеличивается. - person overexchange; 29.04.2018
comment
Итак, я пытаюсь понять, как мы поддерживаем отношения типов без использования ключевого слова extend. Разве нельзя решить вопросы, упомянутые в ваших первых двух пунктах, с помощью композиции? И третий пункт - использование структурной типизации? - person overexchange; 29.04.2018
comment
Если иерархия классов в какой-то момент пошла не так, ее следует реорганизовать или оставить такой, в зависимости от того, насколько она важна или дорогостоящая. Это. Если базовым классом является Human, но кажется, что дочерние классы - это John, Rick, Duck и Robot, базовый класс, вероятно, должен стать Biped, а Human, Duck и Robot должны напрямую наследовать от него. Эта проблема, вероятно, не имеет никаких последствий, кроме дизайна, потому что интерфейсы классов содержат элементы, которых не должно быть, например duck.speak (). Но я не уверен, как это применимо к вопросу. - person Estus Flask; 29.04.2018
comment
Вы можете поддерживать наследование типов с помощью наследования интерфейса или типов объединения (они в основном взаимозаменяемы, но могут быть некоторые различия, stackoverflow.com/a/ 49723584/3731501). Остается неясным, почему эта тема является для вас проблемой. Если вам нужно наследование, оно вам нужно (кроме случаев , когда вам его не). Альтернативой наследованию является размещение тех же методов в классах, реализующих один и тот же интерфейс, что недопустимо. Вы не можете имитировать маркированный список с помощью структурной типизации - для этого и нужно наследование. - person Estus Flask; 29.04.2018
comment
Существуют низкоуровневые альтернативы extends, такие как Object.setPrototypeOf - классы ES6 являются синтаксическим сахаром, и это то, что TS использует для их переноса в ES5. Вы должны хорошо понимать, как работает наследование JS и как код TS преобразуется в JS. Было бы более существенно, если бы у вас были реальные примеры, которые вас беспокоят - вы только что перешли от классов Angular к своим собственным, не показывая их. - person Estus Flask; 29.04.2018