🗒 Задача¶
В игре используются типы игральных костей с разным количеством граней: 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()
}
}