В чем разница между композицией и наследованием в ООП?

Ответ

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

Наследование (is-a)

Создает отношение «является». Дочерний класс наследует свойства и методы родительского, может их расширять или переопределять.

class Vehicle {
    func move() { print("Moving") }
}

class Car: Vehicle { // Car IS-A Vehicle
    func honk() { print("Beep!" ) }
}
let myCar = Car()
myCar.move() // Унаследованный метод

Композиция (has-a)

Создает отношение «имеет». Класс содержит экземпляр другого класса в качестве свойства.

class Engine {
    func start() { print("Vroom!" ) }
}

class Car { // Car HAS-A Engine
    let engine = Engine()
    func drive() {
        engine.start()
    }
}
let myCar = Car()
myCar.drive() // Делегирует работу объекту Engine

Ключевые отличия и рекомендации

Критерий Наследование Композиция
Связь Жесткая (родитель-потомок) Гибкая (владелец-компонент)
Поведение Определяется на этапе компиляции Можно менять во время выполнения
Множественность Одиночное наследование (в Swift) Множественная композиция
Тестируемость Сложнее из-за тесной связи Проще (можно подменить mock-объект)

Практическое правило: предпочитайте композицию наследованию. Она снижает связанность кода, повышает гибкость и упрощает модульное тестирование. Используйте наследование, только когда есть четкое отношение «является разновидностью» и необходимо полное повторное использование кода родителя.

Ответ 18+ 🔞

Э, слушай, смотри, вот эта вся хуйня про классы и объекты — она, блядь, как в жизни, понимаешь? Два главных способа, как одно к другому прикрутить: либо ты от папочки что-то взял, либо ты у соседа в долг занял, но так, чтобы потом не отдавать. Вот.

Наследование (is-a) — это типа «я твой сын, дай денег» Отношение «является». То есть, если твой класс — это такой же ушастый, как и родительский, только с дополнительными прибамбасами. Всё, что у папы было, теперь и твоё, можно даже переписать завещание (переопределить методы).

class Транспорт {
    func ехать() { print("Поехали...") }
}

class Тачка: Транспорт { // Тачка — это Транспорт, ёпта!
    func бибикнуть() { print("Би-бип!" ) }
}
let мояТачка = Тачка()
мояТачка.ехать() // Метод-то папин, но уже мой, блядь

Композиция (has-a) — это «у меня есть раб, который всё делает» Отношение «имеет». Не ты сам, а у тебя в подвале сидит какой-то чувак, который умеет то, что тебе надо. Ты ему команду — он работает. Гибко, сука!

class Двигатель {
    func завестись() { print("Вжжжжужжу!" ) }
}

class Тачка { // У Тачки ЕСТЬ Двигатель, вот и вся философия
    let движок = Двигатель()
    func погнать() {
        движок.завестись()
    }
}
let мояТачка = Тачка()
мояТачка.погнать() // Сам-то я нихуя не умею, но у меня есть спец

Так в чём, блядь, разница-то, и что лучше?

Смотри табличку, а то мозг сейчас взорвётся:

Критерий Наследование Композиция
Связь Жёсткая, как с роднёй — не открепишься Свободная, как с наёмником — сегодня один, завтра другого нанял
Поведение Всё решено при рождении, изменить — только мутировать Меняй на ходу, хоть каждый день нового «движок» вставляй
Множественность В Swift только один папа (одиночное наследование) А компонентов можешь навтыкать овердохуища
Тестируемость Сложно, потому что папаша всегда лезет не в своё дело Легко, блядь! Подсунул заглушку (mock) вместо движка и тестируй

Главное правило, которое все забывают, а потом охуевают: если сомневаешься — выбирай композицию, нахуй! Наследование — это как татуха с именем бывшей. Используй его только тогда, когда на 146% уверен, что это навсегда и есть чёткое отношение «является разновидностью». А так — плоди независимые модули и собирай из них конструктор. Код станет гибким, а жизнь — проще. В рот меня чих-пых, вот и вся наука!