Перейти к содержимому

Практика 8. Практика к лекции 8

Цель: закрепить механизмы наследования и полиморфизма в Swift — переопределение методов (override), модификатор final, вызов родительской реализации (super), designated/convenience инициализаторы, приведение типов (is, as?, as!), деинициализатор (deinit) — через серию упражнений от базовых к проектным.

Рекомендации по выполнению:

  • Выполняйте задания в отдельных файлах .swift (например, task1.swift, task2.swift).
  • Запускайте через терминал: swift task1.swift (или swift имя_файла.swift).
  • Для проектов из нескольких файлов используйте Swift Package Manager (swift package init --type executable).
  • Работа ведётся на Linux — Xcode не требуется, достаточно установленного Swift toolchain.
  • Для каждого задания добавляйте проверочные print/assert-выражения, демонстрирующие работу.

A. Разминка

  1. Создайте иерархию классов TransportCarElectricCar:
    • Transport: свойство name: String, метод describe() -> String (возвращает "Транспорт: <name>").
    • Car: добавьте свойство horsepower: Int, переопределите describe()"Автомобиль: <name>, <horsepower> л.с.".
    • ElectricCar: добавьте свойство batteryLevel: Int (0–100), переопределите describe()"Электромобиль: <name>, <horsepower> л.с., батарея: <batteryLevel>%".
let t = Transport(name: "Повозка")
let c = Car(name: "Toyota Camry", horsepower: 200)
let e = ElectricCar(name: "Tesla Model 3", horsepower: 283, batteryLevel: 85)
print(t.describe()) // Транспорт: Повозка
print(c.describe()) // Автомобиль: Toyota Camry, 200 л.с.
print(e.describe()) // Электромобиль: Tesla Model 3, 283 л.с., батарея: 85%
  1. Добавьте в иерархию из задания 1 метод move() -> String:

    • Transport: "<name> движется".
    • Car: "<name> едет со скоростью до <horsepower / 2> км/ч".
    • ElectricCar: если batteryLevel > 0"<name> едет бесшумно", иначе — "<name> не может ехать: батарея разряжена".

    Создайте массив [Transport] и вызовите move() для каждого элемента. Убедитесь, что работает полиморфизм.

let vehicles: [Transport] = [t, c, e]
for v in vehicles {
print(v.move())
}

B. Инициализаторы

  1. Создайте класс BankAccount:

    • Свойства: let owner: String, var balance: Double.
    • Designated инициализатор: init(owner: String, balance: Double).
    • Convenience инициализатор: init(owner: String) — создаёт счёт с нулевым балансом.
    • Методы: deposit(_ amount: Double), withdraw(_ amount: Double) -> Bool.

    Создайте дочерний класс SavingsAccount:

    • Дополнительное свойство: let interestRate: Double (процентная ставка, например 0.05).
    • Designated инициализатор, вызывающий super.init.
    • Метод addInterest() — увеличивает balance на balance * interestRate.
let acc = BankAccount(owner: "Алиса")
acc.deposit(10000)
print(acc.balance) // 10000.0
let savings = SavingsAccount(owner: "Борис", balance: 50000, interestRate: 0.05)
savings.addInterest()
print(savings.balance) // 52500.0
  1. Добавьте к BankAccount метод transfer(to other: BankAccount, amount: Double) -> Bool, который переводит деньги с одного счёта на другой (с валидацией). Продемонстрируйте работу с экземплярами разных подклассов.
let checking = BankAccount(owner: "Иван", balance: 3000)
let saving = SavingsAccount(owner: "Иван", balance: 10000, interestRate: 0.03)
print(checking.transfer(to: saving, amount: 1500)) // true
print(checking.balance) // 1500.0
print(saving.balance) // 11500.0

C. Приведение типов

  1. Создайте базовый класс Shape с методом area() -> Double. Реализуйте три дочерних класса:

    • Circle(radius: Double)
    • RectangleShape(width: Double, height: Double)
    • Triangle(base: Double, height: Double)

    Создайте массив [Shape] и с помощью is и as?:

    • Посчитайте количество фигур каждого типа.
    • Выведите площадь каждой фигуры с указанием типа.
    • Найдите суммарную площадь только кругов.
let shapes: [Shape] = [
Circle(radius: 5),
RectangleShape(width: 4, height: 6),
Triangle(base: 3, height: 8),
Circle(radius: 3),
RectangleShape(width: 10, height: 2)
]
// Кругов: 2, Прямоугольников: 2, Треугольников: 1
// Суммарная площадь кругов: ≈106.81
  1. Напишите функцию describeShape(_ shape: Shape) -> String, использующую switch с as? (или if let ... as?) для вывода подробного описания фигуры, включая специфичные свойства каждого подкласса.
print(describeShape(Circle(radius: 5)))
// "Круг с радиусом 5.0, площадь: 78.54"
print(describeShape(RectangleShape(width: 4, height: 6)))
// "Прямоугольник 4.0×6.0, площадь: 24.0"

D. deinit и жизненный цикл

  1. Создайте класс Logger с:

    • Свойством let label: String.
    • init(label:) — выводит "[<label>] Логгер создан".
    • deinit — выводит "[<label>] Логгер уничтожен".
    • Методом log(_ message: String) — выводит "[<label>] <message>".

    Продемонстрируйте вызов деинициализатора через блок do { }:

do {
let logger = Logger(label: "App")
logger.log("Приложение запущено")
logger.log("Обработка данных")
}
// [App] Логгер создан
// [App] Приложение запущено
// [App] Обработка данных
// [App] Логгер уничтожен
  1. Создайте класс Resource с let name: String и deinit. Создайте массив var resources: [Resource?] из трёх элементов. Последовательно присвойте элементам nil и пронаблюдайте вызовы deinit.
var resources: [Resource?] = [
Resource(name: "БД"),
Resource(name: "Файл"),
Resource(name: "Сеть")
]
resources[1] = nil // [Файл] Ресурс освобождён
resources[0] = nil // [БД] Ресурс освобождён
resources[2] = nil // [Сеть] Ресурс освобождён

E. Python → Swift

  1. Перепишите на Swift следующий код на Python, используя наследование, override и полиморфизм:
class Notification:
def __init__(self, msg):
self.msg = msg
def send(self):
print(f"Уведомление: {self.msg}")
class EmailNotification(Notification):
def __init__(self, msg, email):
super().__init__(msg)
self.email = email
def send(self):
print(f"Email на {self.email}: {self.msg}")
class SMSNotification(Notification):
def __init__(self, msg, phone):
super().__init__(msg)
self.phone = phone
def send(self):
print(f"SMS на {self.phone}: {self.msg}")
# Полиморфизм
notifications = [
Notification("Привет"),
EmailNotification("Отчёт готов", "user@mail.ru"),
SMSNotification("Код: 1234", "+79001234567")
]
for n in notifications:
n.send()

В Swift-версии:

  • Используйте override func send().
  • Вызовите super.init(msg:) в дочерних классах.
  • Создайте массив [Notification] и продемонстрируйте полиморфизм.

F. Мини-проект

  1. «Система управления сотрудниками» — реализуйте иерархию классов:
class Employee {
let name: String
let id: Int
var salary: Double
init(name: String, id: Int, salary: Double) { ... }
func describe() -> String { ... }
func bonus() -> Double { return 0 }
}

Дочерние классы:

  • Manager: дополнительное свойство department: String, переопределяет bonus() — возвращает salary * 0.2, переопределяет describe().
  • Developer: дополнительные свойства language: String, level: String ("junior", "middle", "senior"), переопределяет bonus() — зависит от уровня (junior — 5%, middle — 10%, senior — 15%), переопределяет describe().
  • Internfinal): свойство mentor: Employee?, bonus() всегда возвращает 0.

Реализуйте:

  • Массив [Employee] из 5–7 сотрудников разных типов.
  • Используя map и приведение типов, выведите describe() для каждого.
  • Используя filter и is, подсчитайте количество разработчиков.
  • Используя reduce, найдите общий фонд бонусов.
  • Используя compactMap и as?, соберите массив языков программирования всех разработчиков.
let team: [Employee] = [
Manager(name: "Алиса", id: 1, salary: 150000, department: "iOS"),
Developer(name: "Борис", id: 2, salary: 120000, language: "Swift", level: "senior"),
Developer(name: "Виктор", id: 3, salary: 90000, language: "Python", level: "middle"),
Intern(name: "Дарья", id: 4, salary: 40000, mentor: nil),
Manager(name: "Евгений", id: 5, salary: 160000, department: "Backend")
]
// Описания:
// Менеджер: Алиса, отдел: iOS, зарплата: 150000.0
// Разработчик: Борис, Swift (senior), зарплата: 120000.0
// ...
// Разработчиков: 2
// Общий фонд бонусов: 69000.0
// Языки: ["Swift", "Python"]

Критерии оценивания

  • Корректность и полнота реализации: 0–5 баллов
  • Грамотное использование наследования, override, super, приведения типов: 0–5 баллов
  • Качество и читаемость кода (именование, инкапсуляция, final где уместно): 0–3 балла
  • Наличие проверочных print/assert выражений: 0–3 балла

Максимум: 16 баллов. Бонус до +2 за дополнительные задания.


Дополнительно (по желанию)

  • Добавьте к Employee метод raise(by percent: Double), увеличивающий зарплату, и наблюдатель didSet на salary, логирующий изменения.
  • Реализуйте протокол CustomStringConvertible для всех классов иерархии (аналог __repr__ в Python).
  • Добавьте класс TeamLead, наследующий от Developer, с дополнительным свойством teamSize: Int и переопределённым bonus() (бонус разработчика + 1000 за каждого члена команды).