diff --git a/src/main/kotlin/ru/otus/cars/Car.kt b/src/main/kotlin/ru/otus/cars/Car.kt index 559978c..6eb2f63 100644 --- a/src/main/kotlin/ru/otus/cars/Car.kt +++ b/src/main/kotlin/ru/otus/cars/Car.kt @@ -19,6 +19,13 @@ interface Car : CarInput { */ val carOutput: CarOutput + /** + * Топливная система + */ + val tank: Tank + val mouth: TankMouth + fun refuel(amount: Double) + /** * Получить оборудование */ diff --git a/src/main/kotlin/ru/otus/cars/CarOutput.kt b/src/main/kotlin/ru/otus/cars/CarOutput.kt index 875339f..0c19e88 100644 --- a/src/main/kotlin/ru/otus/cars/CarOutput.kt +++ b/src/main/kotlin/ru/otus/cars/CarOutput.kt @@ -8,4 +8,9 @@ interface CarOutput { * Скажи текущую скорость */ fun getCurrentSpeed(): Int + + /** + * Скажи сколько топлива + */ + fun getFuelLevel(): Double } \ No newline at end of file diff --git a/src/main/kotlin/ru/otus/cars/CarRefill.kt b/src/main/kotlin/ru/otus/cars/CarRefill.kt new file mode 100644 index 0000000..832772b --- /dev/null +++ b/src/main/kotlin/ru/otus/cars/CarRefill.kt @@ -0,0 +1,49 @@ +package ru.otus.cars + +class CarRefill { + /** + * Заправить одну машину. + * @param car машина (любая реализация Car) + * @param liters сколько литров лить + */ + fun refuel(car: Car, liters: Double) { + // Определяем тип топлива по горловине + val fuelType = when (car.mouth) { + is GasolineMouth -> "benzin" + is GasMouth -> "gas" + else -> "kakoe-to eshe toplivo" + } + val plateNumber = try { car.plates.number } catch (e: NotImplementedError) { "без номера" } + println("Zapravka: liyem $liters litrov ($fuelType) v mashinu ${car::class.simpleName} nomer $plateNumber") + + try { + car.refuel(liters) + println(" → Zapravleno uspesno") + } catch (e: TankOverflowException) { + println(" → Error: ${e.message}") + println(" → Bak polomalsya, no mojno prodoljit s drugimi mashinami") + } + } + + /** + * Заправить коллекцию машин + * Если с баком проблема — остальные всё равно заправим. + */ + fun refuelAll(cars: List, litersPerCar: Double) { + println("\n=== Massovaya zapravka ($litersPerCar litrov kajdoy) ===\n") + + for ((index, car) in cars.withIndex()) { + println("[$index] ${car.plates.number}") + refuel(car, litersPerCar) + println() + } + + println("=== Itogo ===") + cars.forEach { car -> + val gasoline = car.tank.getGasolineLevel() + val gas = car.tank.getGasLevel() + println("${car.plates.number}: Benzin=$gasoline litrov, Gas=$gas litrov") + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/ru/otus/cars/CarTankSystem.kt b/src/main/kotlin/ru/otus/cars/CarTankSystem.kt new file mode 100644 index 0000000..09139b5 --- /dev/null +++ b/src/main/kotlin/ru/otus/cars/CarTankSystem.kt @@ -0,0 +1,93 @@ +package ru.otus.cars + +/** + * Топливная система + */ +// --- Типы топлива --- +sealed class Fuel { + abstract val amount: Double + + data class Gasoline(override val amount: Double) : Fuel() + data class LiquefiedGas(override val amount: Double) : Fuel() +} + +// --- Бак (реализация спрятана от пользователя) --- +class Tank private constructor( + private var gasoline: Double = 0.0, + private var gas: Double = 0.0, + private val capacity: Double = 100.0, +) { + override fun toString() : String { + return "currentFuel Gaz = $gas, Benzin = $gasoline)" + } + fun getGasolineLevel(): Double = gasoline + fun getGasLevel(): Double = gas +//Не знаю как сделать исключение и условие в inline +// fun addFuel(fuel: Fuel) { +// when (fuel) { +// is Fuel.Gasoline -> gasoline = (gasoline + fuel.amount).coerceAtMost(capacity) +// is Fuel.LiquefiedGas -> gas = (gas + fuel.amount).coerceAtMost(capacity) +// } +// } + + fun addFuel(fuel: Fuel) { + when (fuel) { + is Fuel.Gasoline -> { +// println("Zalivaetsya v bak ${fuel.amount} litrov") + val newAmount = gasoline + fuel.amount +// println("Posle zapravki v bake stanet ${newAmount} litrov") + if (newAmount > capacity) { + println("Bak perepolnen! Zalivaetsya ${fuel.amount}, est benzina $gasoline | gaza $gas, limit $capacity") + } + if (gas > 0) { + throw TankOverflowException( + "V bake est gaz = $gas! Zalivaetsya benzin ${fuel.amount}, popytka zalit oba vida topliva vyzvala iskluchenie!" + ) + } + gasoline = (newAmount).coerceAtMost(capacity) +// println("V bake posle zapravki $gasoline litrov gasoline") + } + is Fuel.LiquefiedGas -> { +// println("Zalivaetsya v bak ${fuel.amount} litrov") + val newAmount = gas + fuel.amount +// println("Posle zapravki v bake stanet ${newAmount} litrov") + if (newAmount > capacity) { + println("Bak perepolnen! Zalivaetsya ${fuel.amount}, est benzina $gasoline | gaza $gas, limit $capacity") + } + if (gasoline > 0) { + throw TankOverflowException( + "V bake est benzin = $gasoline! Zalivaetsya gaz ${fuel.amount}, popytka zalit oba vida topliva vyzvala iskluchenie!" + ) + } + gas = (newAmount).coerceAtMost(capacity) +// println("V bake posle zapravki $gas litrov gas") + } + } + } + + companion object { + fun create(): Tank = Tank() + } +} + +// ────────────────────────────────────────────── +// Исключение: с баком проблемы +// ────────────────────────────────────────────── +class TankOverflowException(message: String) : Exception(message) + +// --- Горловина бака --- +sealed class TankMouth(protected val tank: Tank) { + abstract fun refuel(amount: Double) +} + +class GasolineMouth(tank: Tank) : TankMouth(tank) { + override fun refuel(amount: Double) { + tank.addFuel(Fuel.Gasoline(amount)) + } +} + +class GasMouth(tank: Tank) : TankMouth(tank) { + override fun refuel(amount: Double) { + tank.addFuel(Fuel.LiquefiedGas(amount)) + } +} diff --git a/src/main/kotlin/ru/otus/cars/Taz.kt b/src/main/kotlin/ru/otus/cars/Taz.kt index 49df937..8a8ee34 100644 --- a/src/main/kotlin/ru/otus/cars/Taz.kt +++ b/src/main/kotlin/ru/otus/cars/Taz.kt @@ -1,5 +1,7 @@ package ru.otus.cars +import kotlin.random.Random + object Taz: Car { /** * Номерной знак @@ -12,12 +14,30 @@ object Taz: Car { */ override val color: String = "Ржавый" + /** + * Топливная система: бак и горловина. + * Бак спрятан — доступ только через горловину и CarOutput. + */ + override val tank: Tank = Tank.create() +// для taz ставим любую горловину + override val mouth: TankMouth = when (Random.nextInt(0, 2)) { + 0 -> GasolineMouth(tank) + else -> GasMouth(tank) + } + + + + /** + * Заправка через горловину + */ + override fun refuel(amount: Double) { + mouth.refuel(amount) + } /** * Следит за машиной */ override val carOutput: CarOutput get() = throw NotImplementedError("Приборов нет") - /** * Получить оборудование */ diff --git a/src/main/kotlin/ru/otus/cars/Vaz2107.kt b/src/main/kotlin/ru/otus/cars/Vaz2107.kt index be857d2..670c911 100644 --- a/src/main/kotlin/ru/otus/cars/Vaz2107.kt +++ b/src/main/kotlin/ru/otus/cars/Vaz2107.kt @@ -6,6 +6,21 @@ import kotlin.random.Random * Семёрочка */ class Vaz2107 private constructor(color: String) : VazPlatform(color) { + + /** + * Топливная система: бак и горловина. + * Бак спрятан — доступ только через горловину и CarOutput. + */ + override val tank: Tank = Tank.create() + override val mouth: TankMouth = GasMouth(tank) + + /** + * Заправка через горловину + */ + override fun refuel(amount: Double) { + mouth.refuel(amount) + } + /** * Сам-себе-сборщик ВАЗ 2107. */ @@ -20,6 +35,8 @@ class Vaz2107 private constructor(color: String) : VazPlatform(color) { override fun build(plates: Car.Plates): Vaz2107 = Vaz2107("Зеленый").apply { this.engine = getRandomEngine() this.plates = plates + this.tank + this.mouth.refuel(30.0) // Залили топлива при сборке } /** @@ -28,6 +45,9 @@ class Vaz2107 private constructor(color: String) : VazPlatform(color) { fun test(vaz2107: Vaz2107) { println("Проверяем, едет ли ВАЗ 2107...") vaz2107.currentSpeed = Random.nextInt(0, 60) + vaz2107.refuel(Random.nextDouble(0.0, 60.0)) // долить для теста еще топлива + // println("Lyem toplivo v Vaz2107, itogo ${vaz2107.tank.getGasLevel()} litrov") // не очень понятно почему так работает, ведь tank приватный + println("Zapravili Vaz2107 na ${vaz2107.carOutput.getFuelLevel()} litrov") } /** @@ -59,7 +79,7 @@ class Vaz2107 private constructor(color: String) : VazPlatform(color) { // Выводим состояние машины override fun toString(): String { - return "Vaz2107(plates=$plates, wheelAngle=$wheelAngle, currentSpeed=$currentSpeed)" + return "Vaz2107(plates=$plates, wheelAngle=$wheelAngle, currentSpeed=$currentSpeed, currentFuel=${carOutput.getFuelLevel()})" } /** @@ -74,5 +94,8 @@ class Vaz2107 private constructor(color: String) : VazPlatform(color) { override fun getCurrentSpeed(): Int { return this@Vaz2107.currentSpeed } + override fun getFuelLevel(): Double { + return this@Vaz2107.tank.getGasLevel() + } } } \ No newline at end of file diff --git a/src/main/kotlin/ru/otus/cars/Vaz2108.kt b/src/main/kotlin/ru/otus/cars/Vaz2108.kt index 27b83b8..5303e61 100644 --- a/src/main/kotlin/ru/otus/cars/Vaz2108.kt +++ b/src/main/kotlin/ru/otus/cars/Vaz2108.kt @@ -6,6 +6,19 @@ import kotlin.random.Random * Восьмерка */ class Vaz2108 private constructor(color: String) : VazPlatform(color) { + /** + * Топливная система: бак и горловина создаются вместе. + * Бак спрятан — доступ только через горловину и CarOutput. + */ + override val tank: Tank = Tank.create() + override val mouth: TankMouth = GasolineMouth(tank) + + /** + * Заправка через горловину + */ + override fun refuel(amount: Double) { + mouth.refuel(amount) + } /** * Сам-себе-сборщик ВАЗ 2108. */ @@ -21,11 +34,16 @@ class Vaz2108 private constructor(color: String) : VazPlatform(color) { override fun build(plates: Car.Plates): Vaz2108 = Vaz2108("Красный").apply { this.engine = getRandomEngine() this.plates = plates + this.tank + this.mouth.refuel(10.0) //зальем топлива в первый раз } fun alignWheels(vaz2108: Vaz2108) { println("Ваз 2108 выравнивает колёса... ") vaz2108.wheelAngle = 0 + vaz2108.refuel(Random.nextDouble(70.0, 100.0)) +// println("Lyem toplivo v Vaz2108, itogo ${vaz2108.tank.getGasolineLevel()} litrov") // не очень понятно почему так работает, ведь tank приватный + println("Zapravili Vaz2108 na ${vaz2108.carOutput.getFuelLevel()} litrov") } /** @@ -53,7 +71,6 @@ class Vaz2108 private constructor(color: String) : VazPlatform(color) { } private var currentSpeed: Int = 0 // Скока жмёт - /** * Доступно сборщику * @see [build] @@ -63,7 +80,7 @@ class Vaz2108 private constructor(color: String) : VazPlatform(color) { // Выводим состояние машины override fun toString(): String { - return "Vaz2108(plates=$plates, wheelAngle=$wheelAngle, currentSpeed=$currentSpeed)" + return "Vaz2108(plates=$plates, wheelAngle=$wheelAngle, currentSpeed=$currentSpeed, currentFuel=${carOutput.getFuelLevel()})" } /** @@ -78,5 +95,8 @@ class Vaz2108 private constructor(color: String) : VazPlatform(color) { override fun getCurrentSpeed(): Int { return this@Vaz2108.currentSpeed } + override fun getFuelLevel(): Double { + return this@Vaz2108.tank.getGasolineLevel() + } } } \ No newline at end of file diff --git a/src/main/kotlin/ru/otus/cars/main.kt b/src/main/kotlin/ru/otus/cars/main.kt index 978d0ef..cb32c45 100644 --- a/src/main/kotlin/ru/otus/cars/main.kt +++ b/src/main/kotlin/ru/otus/cars/main.kt @@ -1,12 +1,15 @@ package ru.otus.cars +import kotlin.collections.List + fun main() { + println("\n===> drive cars...") - driveCars() + val carsList = driveCars() println("\n===> inner test...") innerNestedCheck() println("\n===> garage make...") - garageMake() + val vazzap = garageMake() println("\n===> model special...") println("\n===> get equipment...") getEquipment() @@ -15,18 +18,86 @@ fun main() { println("\n===> tech checks...") techChecks() println("\n===> Taz...") - println(Taz.color) + val taz = Taz + println(taz.color) + println("=== Таз ===") + println("Color: ${taz.color}") + println("Oborudovanie: ${taz.getEquipment()}") + println("Gorlovina: ${taz.mouth::class.simpleName}\n") +// ТАЗ может иметь любую систему, но его бак взрывается при попытке заправить (бросает исключение) + +// Задание 2 + val station = CarRefill() + + station.refuel(vazzap, 80.0) // заправляем запиленный vaz + println("\nFinally: $vazzap") + + println("Zapravlyaem taz") + station.refuel(taz, 40.0) // заправляем taz по заданию сразу должен взрываться, по логике если залить другой вид топлива :) +// Заливаем другой вид топлива, чтобы проверить + println("\n Zalivaem gaz napryamuyu v bak tank.addFuel()...") + try { + if (taz.mouth::class.simpleName == "GasolineMouth") { + taz.tank.addFuel(Fuel.LiquefiedGas(50.0)) } + else + {taz.tank.addFuel(Fuel.Gasoline(50.0))} + println("OK! Toplivo v bake: ${taz.tank.getGasLevel()} л") + } catch (e: TankOverflowException) { + println("Error: ${e.message}") + } + println("\nFinally: $taz") + + + println("\nZapravlyaem neskolko mashin. Mashini privezli na zapravku:") + carsList.forEach { println(it) } + + println("Zalivka topliva na stancii (neskolko mashin)") + carsList.forEach { car -> + station.refuel(car, 20.0) + } + + println("\nPosle zapravki:") + carsList.forEach { println(it) } + + +// Задание 1 +// // ── 1. Заливаем бензин (через refuel — горловина бензиновая) ── +// println("Zalivaem 40 l benzina...") +// try { +// Taz.refuel(40.0) +// println("OK! Бензина в баке: ${Taz.tank.getGasolineLevel()} л") +// } catch (e: TankOverflowException) { +// println("ОШИБКА: ${e.message}") +// } +// +// // Переливаем бензин (бак 100 л, уже 40, льём 80) +// println("\nЗаливаем ещё 80 л бензина (перелив!)...") +// try { +// Taz.refuel(80.0) +// println("OK! Бензина в баке: ${Taz.tank.getGasolineLevel()} л") +// } catch (e: TankOverflowException) { +// println("Error in Taz bak: ${e.message}") +// println("Benzina v bake: ${Taz.tank.getGasolineLevel()} л") +// } +// // Заливаем газ напрямую в бак (в обход горловины) +// println("\n Zalivaem gaz napryamuyu v bak tank.addFuel()...") +// try { +// Taz.tank.addFuel(Fuel.LiquefiedGas(50.0)) +// println("OK! Газ в баке: ${Taz.tank.getGasLevel()} л") +// } catch (e: TankOverflowException) { +// println("Error: ${e.message}") +// } } -fun driveCars() { +fun driveCars(): List { val vaz1 = Togliatti.buildCar(Vaz2107, Car.Plates("123", 77)) val vaz2 = Togliatti.buildCar(Vaz2108, Car.Plates("321", 78)) - println("Экземпляры класса имеют разное внутреннее состояние:") vaz1.wheelToRight(10) println(vaz1.toString()) // Выводит 10 и случайную скорость vaz2.wheelToLeft(20) println(vaz2.toString()) // Выводит -20 и случайную скорость + return listOf(vaz1, vaz2) } fun innerNestedCheck() { @@ -38,7 +109,7 @@ fun innerNestedCheck() { println("Скорость после проверки: ${output.getCurrentSpeed()}") // Выводит случайную скорость } -fun garageMake() { +fun garageMake(): Car { val maker = "Дядя Вася" val garage = object : CarFactory { override fun buildCar(builder: CarBuilder, plates: Car.Plates): Car { @@ -50,6 +121,7 @@ fun garageMake() { val vaz = garage.buildCar(Vaz2107, Car.Plates("500", 50)) println(vaz.toString()) + return vaz } fun getEquipment() { @@ -87,7 +159,13 @@ fun repairEngine(car: VazPlatform) { // В зависимости от типа двигателя выполняем разные действия // when обеспечивает обход всех вариантов перечисления when (car.engine) { - is VazEngine.LADA_2107 -> println("Чистка карбюратора у двигателя объемом ${car.engine.volume} куб.см у машины $car") - is VazEngine.SAMARA_2108 -> println("Угол зажигания у двигателя объемом ${car.engine.volume} куб.см у машины $car") + is VazEngine.LADA_2107 -> { + println("Чистка карбюратора у двигателя объемом ${car.engine.volume} куб.см у машины $car") + println("Ustanovlen bak i gorlovina ${car.mouth::class.simpleName} ${car.tank.toString()} у машины $car") + } + is VazEngine.SAMARA_2108 -> { + println("Угол зажигания у двигателя объемом ${car.engine.volume} куб.см у машины $car") + println("Ustanovlen bak i gorlovina ${car.mouth::class.simpleName} ${car.tank.toString()} у машины $car") + } } } \ No newline at end of file