다형성: 하나의 인터페이스, 다양한 동작
Il 다형성 (그리스어 "다양한 형태"에서 유래)는 다양한 클래스의 개체의 능력입니다 동일한 메시지에 대해 다른 방식으로 응답합니다. OOP의 핵심 중 하나입니다. 유연하고 확장 가능하며 유지 관리 가능한 코드를 작성합니다. 다형성을 사용하여 다음과 같은 코드를 작성합니다. 구체적인 구현이 아닌 추상 인터페이스로 작업합니다.
💡 주요 개념
- 하위 유형 다형성: 파생 클래스의 개체를 기본 클래스 개체로 처리
- 메소드 재정의: 하위 클래스의 동작 재정의
- 오리 타이핑: "오리처럼 걷고 오리처럼 꽥꽥거린다면 오리다"
- 인터페이스: 일반적인 행동을 보장하는 계약
하위 유형 다형성
하위 유형 다형성을 사용하면 파생 클래스의 객체가 어디에 있든 사용할 수 있습니다. 기본 클래스의 객체를 예상했습니다. 이는 상속 덕분에 가능합니다.
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. 방법
올바른 객체 유형을 기반으로 런타임에 호출됩니다.
인터페이스를 이용한 다형성
인터페이스는 클래스가 준수해야 하는 계약을 정의합니다. 완전히 다른 클래스 동일한 인터페이스를 구현하여 상속 없이 다형성을 허용할 수 있습니다.
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를 진정으로 강력하게 만드는 메커니즘입니다. 그것은 우리가 글을 쓸 수 있게 해준다 구체적인 구현이 아닌 추상화로 작동하는 일반 코드입니다. 그것을 통해 보자 상속, 인터페이스, 덕 타이핑 또는 제네릭, 다형성은 소프트웨어를 유연하게 만듭니다. 확장 가능하고 유지 관리가 가능합니다.
🎯 핵심 포인트
- 다형성 = "동일한 인터페이스, 다른 동작"
- 재정의는 하위 클래스의 메서드를 재정의합니다.
- 인터페이스는 상속 없이 다형성을 허용합니다.
- TypeScript는 구조적 타이핑(오리 타이핑)을 사용합니다.
- 제네릭 = 컴파일 타임 유형 안전 다형성







