Практика 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. Разминка
- Создайте иерархию классов
Transport→Car→ElectricCar: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 метод
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. Инициализаторы
-
Создайте класс
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- Добавьте к
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)) // trueprint(checking.balance) // 1500.0print(saving.balance) // 11500.0C. Приведение типов
-
Создайте базовый класс
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- Напишите функцию
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 и жизненный цикл
-
Создайте класс
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] Логгер уничтожен- Создайте класс
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
- Перепишите на 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. Мини-проект
- «Система управления сотрудниками» — реализуйте иерархию классов:
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().Intern(сfinal): свойство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 за каждого члена команды).