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

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

2.0 Middle🔥 131 комментариев
#Архитектура приложений

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

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

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

Полиморфизм

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

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

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

Виды полиморфизма

1. Параметрический полиморфизм (Generic)

Позволяет функциям и классам работать с данными разных типов без потери типизации.

from typing import TypeVar, List

T = TypeVar('T')

def get_first(items: List[T]) -> T:
    return items[0]

# Работает с разными типами
first_int = get_first([1, 2, 3])      # int
first_str = get_first(['a', 'b', 'c']) # str

2. Полиморфизм подтипов (Subtyping)

Позволяет подклассам переопределять методы родительского класса.

class APIClient:
    def send_request(self, method: str, url: str) -> dict:
        raise NotImplementedError

class RESTClient(APIClient):
    def send_request(self, method: str, url: str) -> dict:
        # Реализация для REST
        return requests.request(method, url).json()

class SOAPClient(APIClient):
    def send_request(self, method: str, url: str) -> dict:
        # Реализация для SOAP
        return parse_soap_response()

# Использование полиморфизма
def test_api(client: APIClient):
    response = client.send_request('GET', '/users')
    assert response is not None

# Одна функция работает с разными клиентами
test_api(RESTClient())  # Работает
test_api(SOAPClient())  # Работает

3. Перегрузка методов (Method Overloading)

Возможность определить несколько методов с одним именем, но разными параметрами. В Python это реализуется через значения по умолчанию или *args, **kwargs.

class TestData:
    def create_user(self, name: str, email: str = None, age: int = None) -> dict:
        user = {'name': name}
        if email:
            user['email'] = email
        if age:
            user['age'] = age
        return user

data = TestData()

# Разные варианты вызова
user1 = data.create_user('John')
user2 = data.create_user('Jane', 'jane@example.com')
user3 = data.create_user('Bob', 'bob@example.com', 30)

4. Динамический полиморфизм (Duck Typing)

«Если ходит как утка и крякает как утка — это утка». Объекты разных типов, реализующие одинаковый интерфейс, могут использоваться одинаково.

class Logger:
    def log(self, message: str):
        raise NotImplementedError

class ConsoleLogger(Logger):
    def log(self, message: str):
        print(message)

class FileLogger(Logger):
    def log(self, message: str):
        with open('log.txt', 'a') as f:
            f.write(message + '\\n')

def run_test(logger: Logger):
    logger.log('Test started')
    # Выполнение теста
    logger.log('Test passed')

# Работает с любым логгером
run_test(ConsoleLogger())
run_test(FileLogger())

Полиморфизм в тестировании

Мокирование зависимостей через полиморфизм

class UserService:
    def __init__(self, repository):
        self.repository = repository
    
    def get_user(self, user_id: int):
        return self.repository.find_by_id(user_id)

class UserRepository:
    def find_by_id(self, user_id: int):
        # Реальное обращение к БД
        pass

class MockUserRepository:
    def find_by_id(self, user_id: int):
        # Фиксированные данные для тестирования
        return {'id': user_id, 'name': 'Test User'}

def test_get_user():
    # Используем мок вместо реального репозитория
    mock_repo = MockUserRepository()
    service = UserService(mock_repo)
    
    user = service.get_user(1)
    assert user['name'] == 'Test User'

Использование ABC (Abstract Base Classes)

from abc import ABC, abstractmethod

class HTTPClient(ABC):
    @abstractmethod
    def get(self, url: str) -> dict:
        pass
    
    @abstractmethod
    def post(self, url: str, data: dict) -> dict:
        pass

class RealHTTPClient(HTTPClient):
    def get(self, url: str) -> dict:
        return requests.get(url).json()
    
    def post(self, url: str, data: dict) -> dict:
        return requests.post(url, json=data).json()

class MockHTTPClient(HTTPClient):
    def __init__(self):
        self.requests = []
    
    def get(self, url: str) -> dict:
        self.requests.append(('GET', url))
        return {'status': 200}
    
    def post(self, url: str, data: dict) -> dict:
        self.requests.append(('POST', url, data))
        return {'status': 201}

def make_api_call(client: HTTPClient):
    return client.get('/api/users')

# Работает с обоими
result1 = make_api_call(RealHTTPClient())
result2 = make_api_call(MockHTTPClient())

Преимущества полиморфизма

  • Гибкость — код работает с интерфейсами, а не с конкретными реализациями
  • Переиспользуемость — один код работает с разными типами
  • Тестируемость — легко подменять реальные объекты на моки
  • Расширяемость — добавлять новые типы без изменения существующего кода
  • Maintainability — проще поддерживать и обновлять код

Практический пример для QA

class APITester:
    def __init__(self, client):
        self.client = client
    
    def test_users_endpoint(self):
        response = self.client.get('/api/users')
        assert response['status'] == 200
        assert isinstance(response['data'], list)

# Один тест работает с разными клиентами
tester = APITester(RealHTTPClient())  # Реальное тестирование
tester.test_users_endpoint()

tester = APITester(MockHTTPClient())  # Юнит тест
tester.test_users_endpoint()

Полиморфизм — это основа SOLID принципов и позволяет писать код, который остаётся гибким и поддерживаемым при росте сложности проекта.