TypeScript - Duck Typing: A Beginner's Guide

안녕하세요, 미래의 코딩 슈퍼스타 여러분! 오늘 우리는 TypeScript의 fascinierende 세계로 뛰어들어 Duck Typing이라는 개념을 탐구해보겠습니다. Duck Typing은 이름만 들어도 재미있을 만큼 fun한 개념입니다. 프로그래밍에 처음 도전하는 분이라也不用担心; 저는 여러분을 단계별로 안내해드릴 것입니다. 수년 동안 수많은 학생들을 가르친 경험을 바탕으로입니다. 그럼, 함께 쿼끼를 시작해보겠습니다!

TypeScript - Duck-Typing

What is Duck Typing?

鸭塘里有一只鸟在游泳. 它长得像鸭子, 叽叽喳喳叫, 游泳도 잘합니다. 그래서鸭子라고 부를 수 있죠? 이게 프로그래밍에서의 Duck Typing의 핵심입니다!

Duck Typing은 "만약 그것이鸭子처럼 걸어다녀하고,鸭子처럼叽叽喳喳叫하며,鸭子처럼游泳하면, 그것은鸭子이야"라는 개념입니다. 프로그래밍 용어로는, 객체의 타입이나 클래스보다는 그 객체가 정의한 메서드나 속성이 더 중요하다는 뜻입니다.

제가 가르치던 시절의 이야기를 공유하겠습니다. 제가 가르치던 한 학생은 프로그래밍에서 타입을 이해하는 데 어려움을 겪고 있었습니다. 그녀에게 물었습니다. 그녀의 연필케이스를 생각해보라고요. 그것이 플라스틱이나 금속, 또는 직물로 만들어졌는지 중요하지 않습니다. 연필과 펜을 담을 수 있다면, 그것은 연필케이스로서의 역할을 다하고 있습니다. 이것이 바로 Duck Typing입니다!

How Duck Typing Works in TypeScript

TypeScript에서 Duck Typing은 객체가 특정 타입의 모든 필요한 속성과 메서드를 가지고 있다면, 그 객체를 해당 타입으로 사용할 수 있게 해줍니다. "만약 그것이 필요한 모든 기능을 가지고 있다면, 그것이 무엇인지 신경 쓰지 않는다!"라고 할 수 있습니다.

간단한 예제를 보겠습니다:

interface Quackable {
quack(): void;
}

function makeItQuack(duck: Quackable) {
duck.quack();
}

let rubberDuck = {
quack: () => console.log("Squeak! Squeak!")
};

let realDuck = {
quack: () => console.log("Quack! Quack!"),
swim: () => console.log("Splash! Splash!")
};

makeItQuack(rubberDuck); // Output: Squeak! Squeak!
makeItQuack(realDuck);   // Output: Quack! Quack!

이 예제에서, Quackable 인터페이스는 quack 메서드를 요구합니다. makeItQuack 함수는 이 인터페이스를 맞춘 모든 객체를 받아들입니다. rubberDuckrealDuck 모두 quack 메서드를 가지고 있으므로, 둘 다 makeItQuack 함수로 사용할 수 있습니다. realDuck은 추가적인 swim 메서드를 가지고 있지만, 이는 문제가 되지 않습니다.

Benefits of Duck Typing

이제 Duck Typing이 무엇인지 이해했으므로, 왜 이것이 멋질까요? 다음은 몇 가지 주요 장점입니다:

1. Flexibility

Duck Typing은 더 유연한 코드를 가능하게 합니다. 객체를 비슷한 방식으로 사용하기 위해 엄격한 클래스 계층 구조를 만들 필요가 없습니다. 객체가 필요한 메서드나 속성을 가지고 있다면, 그것을 교차적으로 사용할 수 있습니다.

2. Code Reusability

Duck Typing을 사용하면, 필요한 속성이나 메서드를 가진 다양한 객체와 함께 작동하는 함수를 작성할 수 있습니다. 이는 코드 재사용을 촉진하고 코드베이스를 더 효율적으로 만듭니다.

3. Easier Testing

Duck Typing은 테스트 시 mock 객체를 만들기 쉽게 합니다. 테스트에서 실제로 사용되는 메서드만 구현하면 되므로, 클래스의 전체 구현을 만들 필요가 없습니다.

4. Intuitive Design

Duck Typing은 종종 더 직관적인 API 설계로 이어집니다. 객체가 무엇인지보다는 그 객체가 무엇을 할 수 있는지에 중점을 둡니다.

Examples of Duck Typing in TypeScript

Duck Typing을 TypeScript에서 더 잘 이해하기 위해 몇 가지 예제를 더 살펴보겠습니다.

Example 1: The Shape-Shifter

interface Drawable {
draw(): void;
}

class Circle {
draw() {
console.log("Drawing a circle");
}
}

class Square {
draw() {
console.log("Drawing a square");
}
}

function drawShape(shape: Drawable) {
shape.draw();
}

drawShape(new Circle()); // Output: Drawing a circle
drawShape(new Square()); // Output: Drawing a square

이 예제에서, CircleSquare은 다른 클래스입니다. 하지만 둘 모두 draw 메서드를 가지고 있습니다. drawShape 함수는 전달받은 객체의 특정 클래스에 대해 신경 쓰지 않고, 단지 draw 메서드를 가지고 있는지 확인합니다.

Example 2: The Noise Makers

interface NoiseMaker {
makeNoise(): string;
}

let dog = {
makeNoise: () => "Woof!"
};

let cat = {
makeNoise: () => "Meow!",
purr: () => "Purr..."
};

let car = {
makeNoise: () => "Vroom!",
drive: () => console.log("Driving...")
};

function makeNoise(thing: NoiseMaker) {
console.log(thing.makeNoise());
}

makeNoise(dog); // Output: Woof!
makeNoise(cat); // Output: Meow!
makeNoise(car); // Output: Vroom!

여기서 우리는 다른 객체들을 가지고 있습니다 - 개, 고양이, 그리고 자동차! 그들은 모두 makeNoise 메서드를 가지고 있으므로, makeNoise 함수로 사용할 수 있습니다. 그들이 매우 다른 것들이라도 상관하지 않습니다.

Example 3: The Fly-Swimmer

interface Flyable {
fly(): void;
}

interface Swimmable {
swim(): void;
}

class Duck implements Flyable, Swimmable {
fly() {
console.log("Duck is flying");
}
swim() {
console.log("Duck is swimming");
}
}

class Airplane implements Flyable {
fly() {
console.log("Airplane is flying");
}
}

class Fish implements Swimmable {
swim() {
console.log("Fish is swimming");
}
}

function makeFly(flyer: Flyable) {
flyer.fly();
}

function makeSwim(swimmer: Swimmable) {
swimmer.swim();
}

let duck = new Duck();
let airplane = new Airplane();
let fish = new Fish();

makeFly(duck);     // Output: Duck is flying
makeFly(airplane); // Output: Airplane is flying
makeSwim(duck);    // Output: Duck is swimming
makeSwim(fish);    // Output: Fish is swimming

이 예제에서 우리는 다른 클래스들이 다른 인터페이스를 구현하고 있습니다. Duck 클래스는 FlyableSwimmable 인터페이스를 모두 구현하므로, makeFlymakeSwim 함수로 사용할 수 있습니다.

Conclusion

이제 Duck Typing의 기본을 배웠습니다. 중요한 것은 이입니다: Duck Typing에서는 객체가 무엇을 할 수 있는지(메서드와 속성)보다는 그 객체가 무엇인지(타입이나 클래스)에 더 중점을 둡니다.

Duck Typing은 더 유연하고 재사용 가능하며 직관적인 코드를 작성할 수 있게 해줍니다. TypeScript 코드를 더 우아하고 효율적으로 만들 수 있는 강력한 개념입니다.

프로그래밍 여정을 계속하면서 Duck Typing을 적용할 기회를 찾아보세요. 그리고 기억하세요, 만약 그것이鸭子처럼 보이고,鸭子처럼游泳하고,鸭子처럼叽叽喳喳叫하면, 그것은鸭子일 가능성이 크거나, 적어도 코드에서는鸭子로 취급될 수 있습니다!

행복하게 코딩하시고, 코드가 항상 완벽하게 작동하시길 바랍니다!

Credits: Image by storyset