Ответ
NestJS, построенный на TypeScript, активно использует полиморфизм через свою систему внедрения зависимостей (DI). Это позволяет создавать гибкие и легко тестируемые приложения.
-
Абстрактные классы и наследование. Можно определить общую логику в базовом классе и переопределять её в наследниках.
export abstract class CacheService { abstract get(key: string): Promise<string>; abstract set(key: string, value: string): Promise<void>; } @Injectable() export class RedisCacheService extends CacheService { async get(key: string) { /* ...реализация для Redis */ } async set(key: string, value: string) { /* ... */ } } @Injectable() export class InMemoryCacheService extends CacheService { private cache = new Map(); async get(key: string) { return this.cache.get(key); } // ... }Затем в провайдерах модуля можно указать, какую реализацию использовать:
{ provide: CacheService, useClass: RedisCacheService } -
Интерфейсы и их токены. Поскольку интерфейсы TypeScript не существуют во время выполнения, NestJS использует строковые или символьные токены для связи абстракции с реализацией.
export const PAYMENT_SERVICE = Symbol('IPaymentService'); export interface IPaymentService { charge(amount: number): Promise<Receipt>; } @Injectable() export class StripeService implements IPaymentService { /* ... */ } // В модуле providers: [ { provide: PAYMENT_SERVICE, useClass: StripeService } ] // В конструкторе сервиса constructor(@Inject(PAYMENT_SERVICE) private paymentService: IPaymentService) {} -
Динамические провайдеры (
useFactory,useExisting). Позволяют на лету решать, какую конкретную реализацию инжектировать, основываясь на конфигурации или контексте.{ provide: 'CONNECTION', useFactory: (configService: ConfigService) => { if (configService.get('USE_PRODUCTION_DB')) { return new ProductionDatabaseConnection(); } return new MockDatabaseConnection(); }, inject: [ConfigService] }
Таким образом, полиморфизм в NestJS — это краеугольный камень архитектуры, позволяющий легко подменять реализации, писать модульные тесты и соблюдать принцип инверсии зависимостей (DIP).
Ответ 18+ 🔞
Э, слушай, вот NestJS на TypeScript — это вообще ёперный театр с полиморфизмом через их систему внедрения зависимостей. Без неё там нихуя не работает, а с ней — гибкость пиздец и тесты писать одно удовольствие, честно.
-
Абстрактные классы и наследование. Берёшь, делаешь какой-нибудь общий класс-заглушку, где объявляешь, что должно быть, а как — хуй его знает. А потом плодишь наследников, которые уже реализуют эту хрень по-своему.
export abstract class CacheService { abstract get(key: string): Promise<string>; abstract set(key: string, value: string): Promise<void>; } @Injectable() export class RedisCacheService extends CacheService { async get(key: string) { /* ...реализация для Redis */ } async set(key: string, value: string) { /* ... */ } } @Injectable() export class InMemoryCacheService extends CacheService { private cache = new Map(); async get(key: string) { return this.cache.get(key); } // ... }А потом в настройках модуля просто указываешь, какую конкретно реализацию подсовывать, когда кто-то просит
CacheService. Гениально и просто, как тапок.{ provide: CacheService, useClass: RedisCacheService } -
Интерфейсы и их токены. Вот тут, бля, начинается магия. Интерфейсы в TypeScript — они же на этапе выполнения испаряются, как последняя бутылка пива на троих. Поэтому NestJS использует токены — строки или символы, чтобы связать абстракцию с конкретной хуйнёй.
export const PAYMENT_SERVICE = Symbol('IPaymentService'); export interface IPaymentService { charge(amount: number): Promise<Receipt>; } @Injectable() export class StripeService implements IPaymentService { /* ... */ } // В модуле providers: [ { provide: PAYMENT_SERVICE, useClass: StripeService } ] // В конструкторе сервиса constructor(@Inject(PAYMENT_SERVICE) private paymentService: IPaymentService) {}Смотри, какая хитрая жопа: ты в коде опираешься на интерфейс
IPaymentService, а фреймворк под капотом по токенуPAYMENT_SERVICEнаходит, какую именно реализацию тебе впендюрить. Доверия к системе — ебать ноль, но работает, сука, как часы. -
Динамические провайдеры (
useFactory,useExisting). Это вообще песня. Тут ты уже на лету решаешь, какую конкретную реализацию подсунуть, в зависимости от конфига или того, что творится вокруг. Овердохуища гибкости.{ provide: 'CONNECTION', useFactory: (configService: ConfigService) => { if (configService.get('USE_PRODUCTION_DB')) { return new ProductionDatabaseConnection(); } return new MockDatabaseConnection(); }, inject: [ConfigService] }То есть в одном случае тебе продакшн-база, а в другом — заглушка для тестов. И всё это решается в одном месте, а не размазано по всему коду. Красота, ядрёна вошь.
Короче, полиморфизм в NestJS — это не просто какая-то академическая хуйня из учебника. Это реальный рабочий инструмент, на котором всё держится. Благодаря этому можно легко подменять одну хуйню на другую, тесты изоляцией писать и в целом не нарушать принципы нормальной архитектуры. Без этого был бы пиздец, а не фреймворк.