"Является" (is-a) отношение:
class Button extends View {
// Button это View
}
"Имеет" (has-a) отношение:
class CustomView {
private Button button; // CustomView содержит Button
}
Критерий | Наследование | Композиция |
---|---|---|
Гибкость | Жесткая связь | Гибкая связь |
Повторное использование | Бесплатное (все методы родителя) | Требует явного делегирования |
Тестируемость | Сложнее (зависимость от родителя) | Проще (можно мокировать компоненты) |
Изменяемость | Требует изменения иерархии | Легко заменить компоненты |
Количество классов | Меньше (но сложнее) | Больше (но проще) |
Доступ к полям | Прямой доступ к protected | Четкие интерфейсы |
Fragile Base Class проблема:
class Base {
void foo() { /* изменение ломает наследников */ }
}
Нарушение инкапсуляции:
Множественное наследование:
Следование SOLID:
Пример в Android:
class MyViewModel(
private val repository: UserRepository, // Внедрение зависимости
private val logger: ErrorLogger // Композиция
) {
fun loadData() {
try {
repository.getUsers()
} catch (e: Exception) {
logger.log(e) // Делегирование
}
}
}
Строгое отношение "является":
SaveToFileDialog
действительно является Dialog
Rectangle
действительно является Shape
Неизменяемая базовая функциональность:
Полиморфизм через переопределение:
abstract class Animal {
abstract void makeSound();
}
Отношение "содержит/использует":
Car
имеет Engine
, но не является двигателемИзменяемое поведение:
class PaymentProcessor {
private PaymentStrategy strategy; // Можно менять
}
Разделение ответственностей:
Предпочтение композиции:
Использование интерфейсов:
class MyClass(
private val dependency: MyInterface // Композиция + абстракция
) : MyInterface by dependency // Делегирование
В современной Android-разработке композиция предпочтительнее наследования в большинстве случаев, так как обеспечивает лучшую гибкость, тестируемость и соответствие принципам SOLID. Наследование следует использовать осознанно только для истинных отношений "является", когда полиморфизм действительно необходим.