← Назад к вопросам

Что такое полиморфизм?

1.0 Junior🔥 201 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое полиморфизм?

Полиморфизм (от греч. 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 и т.д.)

Полиморфизм — одна из трёх опор ООП (наряду с инкапсуляцией и наследованием), без которой невозможно написать хороший объектно-ориентированный код.