Что такое паттерн Фабричный метод

Ответ

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

Пример на Kotlin:

interface Transport {
    fun deliver()
}

class Truck : Transport {
    override fun deliver() = println("Delivered by truck")
}

class Ship : Transport {
    override fun deliver() = println("Delivered by ship")
}

abstract class Logistics {
    abstract fun createTransport(): Transport

    fun planDelivery() {
        val transport = createTransport()
        transport.deliver()
    }
}

class RoadLogistics : Logistics() {
    override fun createTransport() = Truck()
}

class SeaLogistics : Logistics() {
    override fun createTransport() = Ship()
}

Использование:

val logistics: Logistics = RoadLogistics()
logistics.planDelivery() // Output: Delivered by truck

Преимущества: гибкость, расширяемость, соблюдение принципа открытости/закрытости.

Ответ 18+ 🔞

А, паттерны проектирования, ёпта! Ну это как на заводе: тебе нужна деталь, а ты не паришься, какой там станок её выточил — просто говоришь «дай деталь». Фабричный метод — это примерно такая же хитрая жопа.

Смотри, в жизни бывает: пишешь ты код, и там нужно создавать объекты, но заранее не ясно, какие именно. Типа, логистика: если груз по земле — нужен грузовик, если по морю — корабль. И вот чтобы не писать везде if (isLand) Truck() else Ship(), умные дядьки придумали этот паттерн.

Суть в чём, чувак? Есть у тебя главный абстрактный класс, типа Logistics. У него есть метод planDelivery() — это типа бизнес-логика, которая не меняется. Но создание конкретного транспорта (Truck или Ship) он выносит в отдельный абстрактный метод createTransport(). И уже его дети (RoadLogistics, SeaLogistics) говорят, какой именно объект создавать. Получается, что основная логика доставки (planDelivery) отвязана от конкретных классов транспорта. Удивление пиздец, как просто, а?

Вот смотри на код, тут всё понятно:

interface Transport {
    fun deliver()
}

class Truck : Transport {
    override fun deliver() = println("Delivered by truck")
}

class Ship : Transport {
    override fun deliver() = println("Delivered by ship")
}

Тут два транспорта — грузовик и корабль. Каждый умеет доставлять. Дальше магия:

abstract class Logistics {
    // Вот он, фабричный метод! Заглушка.
    abstract fun createTransport(): Transport

    // А это общий алгоритм, который использует фабрику.
    fun planDelivery() {
        val transport = createTransport() // Создаём что угодно!
        transport.deliver() // И работаем с ним.
    }
}

А теперь конкретные фабрики, которые решают, что создавать:

class RoadLogistics : Logistics() {
    override fun createTransport() = Truck() // Говорим: "Рожай грузовик!"
}

class SeaLogistics : Logistics() {
    override fun createTransport() = Ship() // А тут: "Рожай корабль!"
}

Использование — вообще песня:

val logistics: Logistics = RoadLogistics() // Сказали "земная логистика"
logistics.planDelivery() // Output: Delivered by truck

Хочешь морскую? SeaLogistics() подставил — и всё, код planDelivery() даже не чихнул. Он просто вызывает createTransport(), а какая фабрика там behind the scenes — ему похуй.

В чём соль, спросишь? А в том, что ты можешь добавлять новые типы транспорта (вертолёт, ракетный пони) и новые фабрики под них, вообще не трогая существующий клиентский код. Принцип открытости/закрытости в действии, ебать мои старые костыли! Система закрыта для модификации старого кода, но открыта для расширения новыми фабриками.

Главное не путать с Абстрактной фабрикой, та создаёт целые семейства связанных объектов. А это — один объект, но с возможностью подмены типа в наследниках. В общем, паттерн охуенно полезный, когда заранее неизвестно, с каким конкретным классом объектов придётся работать.