Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Композиция в Android и программировании
Композиция — это принцип проектирования, при котором объект содержит другие объекты (экземпляры других классов) вместо наследования от них. Это один из ключевых принципов объектно-ориентированного программирования: «Composition over Inheritance».
Основная идея
Вместо того чтобы создавать иерархию классов через наследование:
// ❌ Плохо — наследование
open class Vehicle(val speed: Int)
class Car(speed: Int) : Vehicle(speed) {
fun honk() = println("Beep!")
}
class Truck(speed: Int) : Vehicle(speed) {
fun loadCargo() = println("Loading...")
}
Мы используем композицию — объект содержит другие объекты:
// ✅ Хорошо — композиция
data class Engine(val speed: Int) {
fun start() = println("Engine started")
}
data class Horn(val sound: String) {
fun honk() = println(sound)
}
class Car(engine: Engine, horn: Horn) {
fun drive() = engine.start()
fun signal() = horn.honk()
}
Почему композиция лучше наследования
1. Гибкость Композиция позволяет комбинировать поведение различных объектов динамически. С наследованием вы привязаны к одной иерархии:
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println(message)
}
class FileLogger : Logger {
override fun log(message: String) {
// Запись в файл
}
}
class UserRepository(private val logger: Logger) {
fun saveUser(user: User) {
// Код сохранения
logger.log("User saved")
}
}
2. Избежание проблем множественного наследования Заботы о diamond problem и порядке инициализации исчезают.
3. Принцип единственной ответственности (SRP) Каждый класс отвечает за одно. Композиция помогает разделить ответственность:
data class User(val id: Int, val name: String)
class UserRepository(private val database: Database) {
fun save(user: User) = database.insert(user)
}
class UserValidator(private val emailValidator: EmailValidator) {
fun validate(email: String) = emailValidator.isValid(email)
}
4. Тестируемость Легче создавать mock-объекты:
class UserRepositoryTest {
@Test
fun testSave() {
val mockDatabase = mockk<Database>()
val repository = UserRepository(mockDatabase)
repository.save(User(1, "John"))
verify { mockDatabase.insert(any()) }
}
}
Практический пример в Android
// Компоненты
interface ApiService { suspend fun fetchUsers(): List<User> }
interface LocalDatabase { fun saveUsers(users: List<User>) }
interface Logger { fun log(msg: String) }
// Repository использует композицию
class UserRepository(
private val apiService: ApiService,
private val database: LocalDatabase,
private val logger: Logger
) {
suspend fun sync() {
try {
logger.log("Fetching users...")
val users = apiService.fetchUsers()
database.saveUsers(users)
logger.log("Sync complete")
} catch (e: Exception) {
logger.log("Error: ${e.message}")
}
}
}
Когда использовать наследование
Наследование полезно только для:**
- Определения общего интерфейса (абстрактные классы, интерфейсы)
- Полиморфизма через контракты
Заключение: композиция — основной инструмент для гибкого и поддерживаемого кода. Применяй её по умолчанию.