ポリモーフィズム: 1 つのインターフェイス、多数の動作
Il 多態性 (ギリシャ語の「多くの形」から)は、さまざまなクラスのオブジェクトの能力です 同じメッセージにさまざまな方法で応答します。これは OOP の柱の 1 つであり、これにより次のことが可能になります。 柔軟で拡張性があり、保守しやすいコードを作成します。ポリモーフィズムを使用すると、次のようなコードを作成できます。 具体的な実装ではなく、抽象的なインターフェイスを操作します。
💡 主要な概念
- サブタイプ多型: 派生クラスのオブジェクトを基本クラスのオブジェクトとして扱う
- メソッドのオーバーライド: サブクラスの動作を再定義する
- アヒルタイピング: 「アヒルのように歩き、アヒルのように鳴くなら、それはアヒルだ」
- インターフェース: 共通の行動を保証する契約
サブタイプ多態性
サブタイプ多態性により、派生クラスのオブジェクトをどこにいても使用できるようになります 基本クラスのオブジェクトが期待されていました。これは継承のおかげで可能になります。
class Animal {{ '{' }}
constructor(protected name: string) {{ '{' }}{{ '}' }}
makeSound(): void {{ '{' }}
console.log(`${{ '{' }}this.name{{ '}' }} fa un suono generico`);
{{ '}' }}
{{ '}' }}
class Dog extends Animal {{ '{' }}
// Override del metodo makeSound
makeSound(): void {{ '{' }}
console.log(`${{ '{' }}this.name{{ '}' }} abbaia: Woof!`);
{{ '}' }}
{{ '}' }}
class Cat extends Animal {{ '{' }}
makeSound(): void {{ '{' }}
console.log(`${{ '{' }}this.name{{ '}' }} miagola: Meow!`);
{{ '}' }}
{{ '}' }}
// Polimorfismo in azione: stesso metodo, comportamenti diversi
function playWithAnimal(animal: Animal): void {{ '{' }}
animal.makeSound(); // Non sappiamo quale animale sia!
{{ '}' }}
const animals: Animal[] = [
new Dog("Rex"),
new Cat("Whiskers"),
new Dog("Buddy")
];
animals.forEach(animal => playWithAnimal(animal));
// Output:
// Rex abbaia: Woof!
// Whiskers miagola: Meow!
// Buddy abbaia: Woof!
方法 playWithAnimal 受け入れる Animal、しかしそれは正しく動作します
と Dog e Cat それらはのサブタイプであるため、 Animal。方法
correct は、オブジェクトの実際の型に基づいて実行時に呼び出されます。
インターフェイスによるポリモーフィズム
インターフェイスは、クラスが尊重する必要がある規約を定義します。全く違うクラス 同じインターフェイスを実装できるため、継承なしでポリモーフィズムが可能になります。
interface Drawable {{ '{' }}
draw(): void;
getArea(): number;
{{ '}' }}
class Circle implements Drawable {{ '{' }}
constructor(private radius: number) {{ '{' }}{{ '}' }}
draw(): void {{ '{' }}
console.log(`Disegno un cerchio di raggio ${{ '{' }}this.radius{{ '}' }}`);
{{ '}' }}
getArea(): number {{ '{' }}
return Math.PI * this.radius ** 2;
{{ '}' }}
{{ '}' }}
class Square implements Drawable {{ '{' }}
constructor(private side: number) {{ '{' }}{{ '}' }}
draw(): void {{ '{' }}
console.log(`Disegno un quadrato di lato ${{ '{' }}this.side{{ '}' }}`);
{{ '}' }}
getArea(): number {{ '{' }}
return this.side ** 2;
{{ '}' }}
{{ '}' }}
class Triangle implements Drawable {{ '{' }}
constructor(private base: number, private height: number) {{ '{' }}{{ '}' }}
draw(): void {{ '{' }}
console.log(`Disegno un triangolo ${{ '{' }}this.base{{ '}' }}x${{ '{' }}this.height{{ '}' }}`);
{{ '}' }}
getArea(): number {{ '{' }}
return (this.base * this.height) / 2;
{{ '}' }}
{{ '}' }}
// Funzione polimorfica: lavora con qualsiasi Drawable
function renderShape(shape: Drawable): void {{ '{' }}
shape.draw();
console.log(`Area: ${{ '{' }}shape.getArea().toFixed(2){{ '}' }}`);
{{ '}' }}
const shapes: Drawable[] = [
new Circle(5),
new Square(4),
new Triangle(6, 3)
];
shapes.forEach(renderShape);
// Output:
// Disegno un cerchio di raggio 5
// Area: 78.54
// Disegno un quadrato di lato 4
// Area: 16.00
// Disegno un triangolo 6x3
// Area: 9.00
メソッドのオーバーライドとスーパー
L'オーバーライド サブクラスが継承されたメソッドをオーバーライドできるようにします。
super 親クラスの実装にアクセスできるようになります。
class Logger {{ '{' }}
log(message: string): void {{ '{' }}
console.log(`[LOG] ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class TimestampedLogger extends Logger {{ '{' }}
// Override con funzionalità aggiuntiva
override log(message: string): void {{ '{' }}
const timestamp = new Date().toISOString();
// Chiama il metodo del padre e aggiunge timestamp
super.log(`[${{ '{' }}timestamp{{ '}' }}] ${{ '{' }}message{{ '}' }}`);
{{ '}' }}
{{ '}' }}
class ColoredLogger extends Logger {{ '{' }}
override log(message: string): void {{ '{' }}
// Comportamento completamente diverso
console.log(`\x1b[32m[LOG] ${{ '{' }}message{{ '}' }}\x1b[0m`);
{{ '}' }}
{{ '}' }}
function logMessage(logger: Logger, msg: string): void {{ '{' }}
logger.log(msg);
{{ '}' }}
const basicLogger = new Logger();
const timeLogger = new TimestampedLogger();
const colorLogger = new ColoredLogger();
logMessage(basicLogger, "Messaggio base");
// [LOG] Messaggio base
logMessage(timeLogger, "Messaggio con timestamp");
// [LOG] [2024-12-26T10:30:00.000Z] Messaggio con timestamp
logMessage(colorLogger, "Messaggio colorato");
// [LOG] Messaggio colorato (in verde)
TypeScript でのダックタイピング
TypeScript が使用するもの 構造的型付け (ダックタイピング): オブジェクトにプロパティがあるかどうか および必要なメソッドは、明示的に実装していなくても、型と互換性があります。 インターフェース。
interface Quackable {{ '{' }}
quack(): void;
{{ '}' }}
class Duck {{ '{' }}
quack(): void {{ '{' }}
console.log("Quack!");
{{ '}' }}
{{ '}' }}
class Person {{ '{' }}
name: string;
constructor(name: string) {{ '{' }}
this.name = name;
{{ '}' }}
// Person ha un metodo quack, quindi è compatibile con Quackable!
quack(): void {{ '{' }}
console.log(`${{ '{' }}this.name{{ '}' }} imita un'anatra: Quack!`);
{{ '}' }}
{{ '}' }}
// Accetta qualsiasi oggetto con metodo quack()
function makeItQuack(quacker: Quackable): void {{ '{' }}
quacker.quack();
{{ '}' }}
const duck = new Duck();
const person = new Person("Mario");
makeItQuack(duck); // "Quack!"
makeItQuack(person); // "Mario imita un'anatra: Quack!"
// Anche oggetti literal funzionano!
makeItQuack({{ '{' }}
quack: () => console.log("Oggetto anonimo: Quack!")
{{ '}' }});
// "Oggetto anonimo: Quack!"
パラメトリック多態性 (ジェネリックス)
I ジェネリック医薬品 これらを使用すると、さまざまな型で動作するコードを作成できます。 タイプの安全性を維持します。これはコンパイル時のポリモーフィズムの一種です。
// Repository generico che funziona con qualsiasi tipo
class Repository<T> {{ '{' }}
private items: T[] = [];
add(item: T): void {{ '{' }}
this.items.push(item);
{{ '}' }}
findById(id: number): T | undefined {{ '{' }}
return this.items[id];
{{ '}' }}
getAll(): T[] {{ '{' }}
return [...this.items];
{{ '}' }}
filter(predicate: (item: T) => boolean): T[] {{ '{' }}
return this.items.filter(predicate);
{{ '}' }}
{{ '}' }}
interface User {{ '{' }}
id: number;
name: string;
{{ '}' }}
interface Product {{ '{' }}
id: number;
title: string;
price: number;
{{ '}' }}
// Stesso codice, tipi diversi
const userRepo = new Repository<User>();
userRepo.add({{ '{' }} id: 1, name: "Alice" {{ '}' }});
userRepo.add({{ '{' }} id: 2, name: "Bob" {{ '}' }});
const productRepo = new Repository<Product>();
productRepo.add({{ '{' }} id: 1, title: "Laptop", price: 999 {{ '}' }});
productRepo.add({{ '{' }} id: 2, title: "Mouse", price: 25 {{ '}' }});
// Type-safe: TypeScript sa che users è User[]
const users = userRepo.getAll();
const expensiveProducts = productRepo.filter(p => p.price > 500);
デザインパターンにおけるポリモーフィズム
ポリモーフィズムは多くのデザイン パターンの基礎です。見てみましょう 戦略パターン:
// Interfaccia comune per tutte le strategie
interface SortStrategy {{ '{' }}
sort(data: number[]): number[];
{{ '}' }}
class BubbleSort implements SortStrategy {{ '{' }}
sort(data: number[]): number[] {{ '{' }}
console.log("Ordinamento con Bubble Sort");
const arr = [...data];
// Implementazione bubble sort (semplificata)
for (let i = 0; i < arr.length; i++) {{ '{' }}
for (let j = 0; j < arr.length - i - 1; j++) {{ '{' }}
if (arr[j] > arr[j + 1]) {{ '{' }}
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
{{ '}' }}
{{ '}' }}
{{ '}' }}
return arr;
{{ '}' }}
{{ '}' }}
class QuickSort implements SortStrategy {{ '{' }}
sort(data: number[]): number[] {{ '{' }}
console.log("Ordinamento con Quick Sort");
// Implementazione quick sort (semplificata con sort nativo)
return [...data].sort((a, b) => a - b);
{{ '}' }}
{{ '}' }}
class DataSorter {{ '{' }}
private strategy!: SortStrategy;
// Cambia strategia a runtime (polimorfismo!)
setStrategy(strategy: SortStrategy): void {{ '{' }}
this.strategy = strategy;
{{ '}' }}
sortData(data: number[]): number[] {{ '{' }}
if (!this.strategy) {{ '{' }}
throw new Error("Strategia non impostata");
{{ '}' }}
return this.strategy.sort(data);
{{ '}' }}
{{ '}' }}
const sorter = new DataSorter();
const data = [5, 2, 8, 1, 9];
sorter.setStrategy(new BubbleSort());
console.log(sorter.sortData(data));
// "Ordinamento con Bubble Sort"
// [1, 2, 5, 8, 9]
sorter.setStrategy(new QuickSort());
console.log(sorter.sortData(data));
// "Ordinamento con Quick Sort"
// [1, 2, 5, 8, 9]
ポリモーフィズムの利点
✅ メリット
- 拡張性: 既存のコードを変更せずに新しいクラスを追加する
- 保守性: よりクリーンでより整理されたコード
- 再利用: 異なるタイプでも同じ機能
- テスト容易性: インターフェースを介したモックとスタブ
- 柔軟性: 実行時に動作を変更する
結論
ポリモーフィズムは、OOP を真に強力にするメカニズムです。それは私たちが書くことを可能にします 具体的な実装ではなく抽象化を扱う汎用コード。やり遂げましょう 継承、インターフェース、ダックタイピングまたはジェネリックス、ポリモーフィズムによりソフトウェアが柔軟になり、 拡張性と保守性が高い。
🎯 重要なポイント
- ポリモーフィズム = 「同じインターフェイス、異なる動作」
- Override はサブクラスのメソッドをオーバーライドします
- インターフェイスにより、継承なしでポリモーフィズムが可能になります
- TypeScript は構造的型付け (ダック タイピング) を使用します。
- ジェネリック = コンパイル時のタイプセーフなポリモーフィズム







