Перейти к содержанию

🗒 Задача

В игре используются типы игральных костей с разным количеством граней: 4, 6 и 8.

Опиши их иерархию, создав по классу на каждый тип. У каждой кости должен быть метод, бросающий кость и печатающий значение в консоль.

Для демонстрации полиморфизма “включения”:

  • создай несколько объектов с разными гранями;

  • собери из них список объектов, указав тип списка;

  • выполни итерацию по списку и вызови у каждого объекта метод броска кости.

❌ Ошибки

✏️ Комментарии

Да, принцип когда ставить open, а когда abstract запомнить очень просто. Обычно, если родительский класс не предназначен для создания объекта – можно делать его абстрактным.
Согласно ТЗ необходимо создать список из объектов и уже в нем на каждой итерации цикла вызывать метод броска кости
Обрати внимание, DiceN отличаются только цифрой в верхней границе диапазона. Давай в базовом классе сделаем свойство с количеством граней и будем использовать его.
В создании экземпляра базового класса нет потребности, а это значит, что лучше сделать его абстрактным.
Реализация с родительским open классом не совсем корректна. В ТЗ требуется использовать полиморфизм включения.

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

С помощью полиморфизма можно повторно использовать базовый класс, использовать любого потомка или методы, которые использует базовый класс.

Другими словами создать список с обобщающим типом требуется не случайно. Он не может быть типом конкретной кости. Он должен быть обобщенным.
Тут как-то с названиями не логично. Это, кстати, одна из сложных и многими нелюбимых частей в разработке - придумывать названия для классов/переменных. Но корректное название улучшает понимание кода. Думаю класс как раз таки уместнее назвать Dice (игральная кость). А количество граней - facesNumber или просто faces (поверхность, сторона).
При каждом броске (вызове trowCube()) ты создаешь range и потом из него выбираешь random().
Нужно ли создавать range каждый раз? или можно проинициализировать его в момент создание во внутреннюю переменную?
Всегда думай об экономии CPU, чтобы писать надежный код. Сейчас оно работает без проблем, потому что метод броска вызывается редко, но условия могут поменяться и твой код начнем грузить garbage collector (эта штука которая подчищает периодически оперативную память от твоих переменных)

🧾 Решение

abstract class Dice {
    abstract val number: Int
    fun throwDice() {
        println((1..number).random())
    }
}

class Dice4 : Dice() {
    override val number = 4
}

class Dice6 : Dice() {
    override val number = 6
}

class Dice8 : Dice() {
    override val number = 8
}

fun main() {
    val dice4: Dice = Dice4()
    val dice6: Dice = Dice6()

    val list: List<Dice> = listOf(dice4, dice6)
    list.forEach {
        it.throwDice()
    }
}
abstract class Dice(
    val number: Int
) {
    fun throwDice() {
        println((1..number).random())
    }
}

class Dice4 : Dice(number = 4)
class Dice6 : Dice(number = 6)
class Dice8 : Dice(number = 8)

fun main() {
    val dice4: Dice = Dice4()
    val dice6: Dice = Dice6()

    val list: List<Dice> = listOf(dice4, dice6)
    list.forEach {
        it.throwDice()
    }
}