상속과 구성: 코드 재사용에 대한 두 가지 접근 방식
L'유전 그리고 구성 이는 두 가지 기본 메커니즘입니다. 객체 지향 프로그래밍에서 코드 재사용을 위해. 둘 다 허용합니다. 단순한 구성 요소부터 시작하여 접근 방식과 의미가 있는 복잡한 소프트웨어를 구축합니다. 매우 다릅니다.
🎯 무엇을 배울 것인가
- 상속을 사용하는 경우와 구성을 사용하는 경우
- 추상 클래스와 인터페이스의 차이점
- "상속보다 구성" 원칙
- 일반적인 유전 문제를 피하는 방법
상속: "IS-A" 관계
상속은 관계를 표현한다 "그것은" (is-a). 개 그것은 동물, 원 하나야 형태. 하위 클래스는 상위 클래스의 모든 멤버를 상속하며 다음을 수행할 수 있습니다. 확장하거나 덮어씁니다.
class Vehicle {{ '{' }}
protected speed: number = 0;
constructor(protected brand: string) {{ '{' }}{{ '}' }}
accelerate(amount: number): void {{ '{' }}
this.speed += amount;
console.log(`${{ '{' }}this.brand{{ '}' }} accelera a ${{ '{' }}this.speed{{ '}' }} km/h`);
{{ '}' }}
getInfo(): string {{ '{' }}
return `${{ '{' }}this.brand{{ '}' }} - Velocità: ${{ '{' }}this.speed{{ '}' }} km/h`;
{{ '}' }}
{{ '}' }}
class Car extends Vehicle {{ '{' }}
private doors: number;
constructor(brand: string, doors: number) {{ '{' }}
super(brand); // Chiama il costruttore del padre
this.doors = doors;
{{ '}' }}
// Estende la funzionalità del padre
getInfo(): string {{ '{' }}
return `${{ '{' }}super.getInfo(){{ '}' }} - Porte: ${{ '{' }}this.doors{{ '}' }}`;
{{ '}' }}
openTrunk(): void {{ '{' }}
console.log("Bagagliaio aperto");
{{ '}' }}
{{ '}' }}
const myCar = new Car("Tesla", 4);
myCar.accelerate(50);
console.log(myCar.getInfo());
// Output: Tesla - Velocità: 50 km/h - Porte: 4
추상 클래스와 인터페이스
TypeScript는 두 가지를 모두 제공합니다. 추상 수업 저것 인터페이스 정의하다 재사용 가능한 계약 및 구조.
추상 클래스:
- 구현이 있을 수 있습니다.
- 지원 수정자(비공개, 보호)
- 단일 상속(하나만 확장)
- 이를 사용하여 공통 코드를 공유하십시오.
인터페이스:
- 서명만 가능, 구현 없음
- 모든 회원은 공개입니다.
- 다중 구현(다중 인터페이스 구현)
- 이를 사용하여 계약을 정의합니다.
abstract class Shape {{ '{' }}
protected color: string;
constructor(color: string) {{ '{' }}
this.color = color;
{{ '}' }}
// Metodo concreto (con implementazione)
getColor(): string {{ '{' }}
return this.color;
{{ '}' }}
// Metodo astratto (da implementare nelle sottoclassi)
abstract area(): number;
abstract perimeter(): number;
{{ '}' }}
class Circle extends Shape {{ '{' }}
constructor(private radius: number, color: string) {{ '{' }}
super(color);
{{ '}' }}
area(): number {{ '{' }}
return Math.PI * this.radius ** 2;
{{ '}' }}
perimeter(): number {{ '{' }}
return 2 * Math.PI * this.radius;
{{ '}' }}
{{ '}' }}
const circle = new Circle(5, "rosso");
console.log(circle.area()); // 78.54
console.log(circle.getColor()); // "rosso"
interface Flyable {{ '{' }}
fly(): void;
altitude: number;
{{ '}' }}
interface Swimmable {{ '{' }}
swim(): void;
depth: number;
{{ '}' }}
// Una classe può implementare più interfacce
class Duck implements Flyable, Swimmable {{ '{' }}
altitude: number = 0;
depth: number = 0;
fly(): void {{ '{' }}
this.altitude = 100;
console.log(`Volo a ${{ '{' }}this.altitude{{ '}' }}m di altitudine`);
{{ '}' }}
swim(): void {{ '{' }}
this.depth = 2;
console.log(`Nuoto a ${{ '{' }}this.depth{{ '}' }}m di profondità`);
{{ '}' }}
{{ '}' }}
const duck = new Duck();
duck.fly(); // "Volo a 100m di altitudine"
duck.swim(); // "Nuoto a 2m di profondità"
구성: "HAS-A" 보고서
구성은 관계를 표현합니다. "있다" (가지고). 자동차 가지고있다 엔진, 컴퓨터 하나 있어요 CPU. 상속하는 대신 다른 클래스의 인스턴스를 멤버로 포함합니다.
class Engine {{ '{' }}
constructor(private horsepower: number) {{ '{' }}{{ '}' }}
start(): void {{ '{' }}
console.log(`Motore da ${{ '{' }}this.horsepower{{ '}' }}HP avviato`);
{{ '}' }}
getHorsepower(): number {{ '{' }}
return this.horsepower;
{{ '}' }}
{{ '}' }}
class Transmission {{ '{' }}
constructor(private type: string) {{ '{' }}{{ '}' }}
shift(gear: number): void {{ '{' }}
console.log(`Cambio ${{ '{' }}this.type{{ '}' }}: marcia ${{ '{' }}gear{{ '}' }}`);
{{ '}' }}
{{ '}' }}
// Car "ha un" Engine e "ha una" Transmission
class CarComposed {{ '{' }}
private engine: Engine;
private transmission: Transmission;
constructor(horsepower: number, transmissionType: string) {{ '{' }}
this.engine = new Engine(horsepower);
this.transmission = new Transmission(transmissionType);
{{ '}' }}
start(): void {{ '{' }}
this.engine.start();
{{ '}' }}
drive(): void {{ '{' }}
this.transmission.shift(1);
console.log("Auto in movimento");
{{ '}' }}
getSpecs(): string {{ '{' }}
return `${{ '{' }}this.engine.getHorsepower(){{ '}' }}HP`;
{{ '}' }}
{{ '}' }}
const car = new CarComposed(200, "automatico");
car.start(); // "Motore da 200HP avviato"
car.drive(); // "Cambio automatico: marcia 1"
// "Auto in movimento"
상속보다 구성
원리 "상속보다 구성" 선호하는 구성을 제안합니다 가능할 때마다 상속. 왜?
✅ 구성의 장점:
- 유연성: 런타임 시 동작 변경
- 재사용: 다양한 방법으로 구성 요소 결합
- 테스트: 구성 요소를 독립적으로 테스트
- 약한 결합: 더욱 안전한 편집
⚠️ 상속 문제:
- 변경하기 어려운 엄격한 계층 구조
- 아버지와 아들 사이의 강한 결합
- 취약한 기본 클래스 문제
- 다이아몬드 문제(다중 상속)
상속을 사용해야 하는 경우
상속은 다음과 같은 경우에 적합합니다.
- 하나 있다 명확한 "is-a" 관계 (개는 동물이다)
- 하위 클래스는 하나입니다. 진정한 전문화 슈퍼클래스의
- 유용하다 다형성 (공통 인터페이스로 다양한 객체를 처리)
- 계층 구조는 안정적인 자주 바뀌지 않을 거에요
// Gerarchia chiara e stabile
abstract class Employee {{ '{' }}
constructor(
protected name: string,
protected salary: number
) {{ '{' }}{{ '}' }}
abstract calculateBonus(): number;
getDetails(): string {{ '{' }}
return `${{ '{' }}this.name{{ '}' }} - Stipendio: €${{ '{' }}this.salary{{ '}' }}`;
{{ '}' }}
{{ '}' }}
class Manager extends Employee {{ '{' }}
calculateBonus(): number {{ '{' }}
return this.salary * 0.2; // 20% bonus
{{ '}' }}
{{ '}' }}
class Developer extends Employee {{ '{' }}
constructor(name: string, salary: number, private language: string) {{ '{' }}
super(name, salary);
{{ '}' }}
calculateBonus(): number {{ '{' }}
return this.salary * 0.15; // 15% bonus
{{ '}' }}
{{ '}' }}
컴포지션을 사용해야 하는 경우
다음과 같은 경우 구성이 바람직합니다.
- 관계는 "가-아" (자동차에는 엔진이 있습니다)
- 유용하다 유연한 재사용 기능성
- 행동은 다음과 같아야합니다 교환 가능
- 구조는 할 수 있습니다 진화하다 시간이 지남에 따라
// Strategy pattern con composizione
interface PaymentStrategy {{ '{' }}
pay(amount: number): void;
{{ '}' }}
class CreditCardPayment implements PaymentStrategy {{ '{' }}
pay(amount: number): void {{ '{' }}
console.log(`Pagamento di €${{ '{' }}amount{{ '}' }} con carta di credito`);
{{ '}' }}
{{ '}' }}
class PayPalPayment implements PaymentStrategy {{ '{' }}
pay(amount: number): void {{ '{' }}
console.log(`Pagamento di €${{ '{' }}amount{{ '}' }} con PayPal`);
{{ '}' }}
{{ '}' }}
class ShoppingCartComposed {{ '{' }}
private items: number[] = [];
private paymentStrategy!: PaymentStrategy;
addItem(price: number): void {{ '{' }}
this.items.push(price);
{{ '}' }}
// Cambia strategia a runtime!
setPaymentStrategy(strategy: PaymentStrategy): void {{ '{' }}
this.paymentStrategy = strategy;
{{ '}' }}
checkout(): void {{ '{' }}
const total = this.items.reduce((sum, price) => sum + price, 0);
this.paymentStrategy.pay(total);
{{ '}' }}
{{ '}' }}
const cart = new ShoppingCartComposed();
cart.addItem(50);
cart.addItem(30);
cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(); // "Pagamento di €80 con carta di credito"
cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(); // "Pagamento di €80 con PayPal"
결론
상속과 구성은 적이 아니라 보완적인 도구입니다. 유전은 강력하다 다형성으로 안정적인 계층 구조를 모델링하는 동시에 구성은 유연성과 모듈식 재사용. 경험 법칙: 구성부터 시작됩니다, 사용 "is-a" 관계가 명확하고 이익이 비용보다 클 때만 상속됩니다.
🎯 핵심 포인트
- 상속 = "is-a", 구성 = "has-a"
- 공유 코드를 위한 추상 클래스, 계약을 위한 인터페이스
- 유연성을 높이기 위해 상속보다 구성
- 명확한 다형성을 갖춘 안정적인 계층 구조를 위해 상속 사용







