Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое полиморфизм?
Полиморфизм (от греч. poly — много, morph — форма) — это принцип объектно-ориентированного программирования, который позволяет объектам различных типов быть использованными через один и тот же интерфейс. Буквально это означает "много форм": один метод, но разные реализации в зависимости от типа объекта.
Основная идея полиморфизма
Полиморфизм позволяет писать код, который работает с объектами разных типов единообразно, без необходимости знать конкретный тип каждого объекта во время выполнения.
// Без полиморфизма: нужно проверять тип каждого объекта
function makeSound(animal) {
if (animal.type === 'dog') {
console.log('Гав!');
} else if (animal.type === 'cat') {
console.log('Мяу!');
} else if (animal.type === 'bird') {
console.log('Чирик!');
}
}
// С полиморфизмом: один вызов метода для всех
function makeSound(animal) {
animal.speak(); // Каждый животное знает, как издавать свой звук
}
Основные типы полиморфизма
1. Полиморфизм переопределения (Override Polymorphism)
Это самый распространённый тип, когда дочерний класс переопределяет метод родительского класса.
// Родительский класс
class Animal {
speak() {
return 'Издает звук';
}
move() {
return 'Движется';
}
}
// Дочерние классы переопределяют методы
class Dog extends Animal {
speak() {
return 'Лает: Гав!';
}
}
class Cat extends Animal {
speak() {
return 'Мяучит: Мяу!';
}
}
class Bird extends Animal {
speak() {
return 'Поёт: Чирик!';
}
move() {
return 'Летит';
}
}
// Полиморфное использование
const animals = [new Dog(), new Cat(), new Bird()];
animals.forEach(animal => {
console.log(animal.speak()); // Разные реализации одного метода
});
// Вывод:
// Лает: Гав!
// Мяучит: Мяу!
// Поёт: Чирик!
2. Полиморфизм перегрузки (Overload Polymorphism)
Одна функция/метод имеет несколько реализаций с разными параметрами.
// TypeScript поддерживает перегрузку методов
class Calculator {
// Сигнатуры методов (что может принимать)
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: number[], b: number[]): number[];
// Реальная реализация
add(a: any, b: any): any {
if (typeof a === 'number' && typeof b === 'number') {
return a + b; // Сложение чисел
}
if (typeof a === 'string' && typeof b === 'string') {
return a + b; // Конкатенация строк
}
if (Array.isArray(a) && Array.isArray(b)) {
return a.map((val, i) => val + b[i]); // Сложение массивов
}
}
}
const calc = new Calculator();
console.log(calc.add(5, 10)); // 15
console.log(calc.add('Hello', ' World')); // Hello World
console.log(calc.add([1, 2], [3, 4])); // [4, 6]
3. Параметрический полиморфизм (Generic Polymorphism)
Это использование Generic типов, которые работают с различными типами данных.
// Generic функция работает с любым типом
function getFirstElement<T>(array: T[]): T {
return array[0];
}
const firstNumber = getFirstElement<number>([1, 2, 3]);
const firstString = getFirstElement<string>(['a', 'b', 'c']);
const firstBoolean = getFirstElement<boolean>([true, false]);
// Generic класс
class Storage<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
getAll(): T[] {
return this.items;
}
}
const numberStorage = new Storage<number>();
numberStorage.add(42);
const stringStorage = new Storage<string>();
stringStorage.add('hello');
4. Утиная типизация (Duck Typing) — полиморфизм без наследования
Этот подход работает на основе "если это ходит как утка и крякает как утка, то это утка". Объект полиморфен, если он имеет нужные методы, независимо от класса.
// Никаких классов, никакого наследования
const dog = {
speak() {
return 'Гав!';
}
};
const cat = {
speak() {
return 'Мяу!';
}
};
const person = {
speak() {
return 'Привет!';
}
};
// Функция работает с любым объектом, который имеет метод speak
function makeAnimalSpeak(creature) {
console.log(creature.speak()); // Полиморфизм!
}
makeAnimalSpeak(dog); // Гав!
makeAnimalSpeak(cat); // Мяу!
makeAnimalSpeak(person); // Привет!
Практические примеры полиморфизма
Пример 1: Система платежей
// Базовый класс
class PaymentMethod {
process(amount) {
throw new Error('Метод должен быть переопределён');
}
}
// Различные способы оплаты
class CreditCardPayment extends PaymentMethod {
process(amount) {
console.log(`Оплачено ${amount} руб. с кредитной карты`);
return true;
}
}
class PayPalPayment extends PaymentMethod {
process(amount) {
console.log(`Оплачено ${amount} руб. через PayPal`);
return true;
}
}
class CryptoPayment extends PaymentMethod {
process(amount) {
console.log(`Оплачено ${amount} руб. криптовалютой`);
return true;
}
}
// Процесс оплаты работает с любым методом оплаты
function checkout(paymentMethod, amount) {
if (paymentMethod.process(amount)) {
console.log('Транзакция завершена');
}
}
// Использование
const creditCard = new CreditCardPayment();
const paypal = new PayPalPayment();
const crypto = new CryptoPayment();
checkout(creditCard, 1000); // Любой метод используется одинаково
checkout(paypal, 500);
checkout(crypto, 200);
Пример 2: Система логирования
// Интерфейс логгера
class Logger {
log(message) {
throw new Error('log должен быть переопределён');
}
}
// Различные реализации логирования
class ConsoleLogger extends Logger {
log(message) {
console.log(`[Console]: ${message}`);
}
}
class FileLogger extends Logger {
log(message) {
// Запись в файл
console.log(`[File]: ${message}`);
}
}
class RemoteLogger extends Logger {
log(message) {
// Отправка на удалённый сервер
console.log(`[Remote]: ${message}`);
}
}
// Приложение работает с любым логгером
class Application {
constructor(logger) {
this.logger = logger;
}
run() {
this.logger.log('Приложение запущено');
// ...
this.logger.log('Приложение завершено');
}
}
// Использование
const app1 = new Application(new ConsoleLogger());
const app2 = new Application(new FileLogger());
const app3 = new Application(new RemoteLogger());
app1.run(); // Логирует в консоль
app2.run(); // Логирует в файл
app3.run(); // Логирует на сервер
Пример 3: Обработка данных
// Различные форматы данных
class DataFormatter {
format(data) {
throw new Error('format должен быть переопределён');
}
}
class JSONFormatter extends DataFormatter {
format(data) {
return JSON.stringify(data, null, 2);
}
}
class CSVFormatter extends DataFormatter {
format(data) {
return data.map(row => Object.values(row).join(',')).join('\n');
}
}
class XMLFormatter extends DataFormatter {
format(data) {
// XML форматирование
return `<data>${JSON.stringify(data)}</data>`;
}
}
// Функция работает с любым форматером
function saveData(formatter, data, filename) {
const formatted = formatter.format(data);
console.log(`Сохранено в ${filename}:`);
console.log(formatted);
}
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
];
saveData(new JSONFormatter(), users, 'users.json');
saveData(new CSVFormatter(), users, 'users.csv');
saveData(new XMLFormatter(), users, 'users.xml');
Преимущества полиморфизма
// 1. Расширяемость: легко добавлять новые типы
class BitcoinPayment extends PaymentMethod {
process(amount) {
console.log(`Оплачено ${amount} руб. Bitcoin`);
return true;
}
}
// checkout() работает с новым типом без изменений!
// 2. Гибкость: можно менять реализацию
const app = new Application(new ConsoleLogger());
// Позже можно переключиться на другой логгер
app.logger = new FileLogger();
// 3. Тестируемость: легко создавать mock объекты
class MockLogger extends Logger {
log(message) {
// Просто игнорировать
}
}
// 4. Чистота кода: нет больших if/else или switch блоков
Полиморфизм в реальных приложениях
// React компоненты — это форма полиморфизма
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
function IconButton({ icon, onClick, children }) {
return <button onClick={onClick}><Icon src={icon} /> {children}</button>;
}
// Обе кнопки используются через один интерфейс
<Button onClick={handleClick}>Click me</Button>
<IconButton icon="icon.png" onClick={handleClick}>Click me</IconButton>
Итог
Полиморфизм — это мощный принцип, который позволяет:
- Писать более гибкий и расширяемый код
- Избежать дублирования кода
- Делать код более понятным и поддерживаемым
- Применять паттерны проектирования (Strategy, Factory, Observer и т.д.)
Полиморфизм — одна из трёх опор ООП (наряду с инкапсуляцией и наследованием), без которой невозможно написать хороший объектно-ориентированный код.