ЛР-6 / ЯП №6
.pdfМинистерство науки и высшего образования Российской Федерации Федеральное государственное автономное образовательное учреждение высшего образования
ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР) Кафедра безопасности информационных систем (БИС)
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ Отчет по лабораторной работе №6
по дисциплине «Языки программирования»
Студент гр.
_______
_______
Принял:
Доцент каф. КИБЭВС,
д.т.н., доцент
_______ Романов А. С.
_______
Томск 2026
|
Оглавление |
Введение................................................................................................................... |
3 |
Ход работы............................................................................................................... |
4 |
Заключение .............................................................................................................. |
6 |
Приложение А ......................................................................................................... |
7 |
2
Введение
Цель работы – знакомство с основными концепциями и приемами объектно-ориентированного анализа и проектирования, выработка практических навыков в построении модели предметной области и элементов модели проектирования. Код написан на ЯП Python.
3
Ход работы
Для данной лабораторной работы необходимо было разработать программу по варианту 19: Система управления уличным освещением на основе датчиков освещённости (рис 1.1). Листинг программы представлен в приложении А. Также была разработана UML-диаграмма, показывающая все связи классов (рис 1.2).
Рисунок 1.1 – Вывод для программы по варианту
4
Рисунок 1.2 – UML-диаграмма связей классов
5
Заключение
В результате выполнения данной лабораторной работы были получены знания об объектно-ориентированном программировании и его основных концепциях, а также навыки для построения модели предметной области и элементов модели проектирования.
6
Приложение А
(обязательное)
Листинг кода для лабораторной работы
from abc import ABC, abstractmethod from typing import List, Set
from datetime import time
# интерфейс — контракт для классов, которые можно включать/выключать class Switchable(ABC):# абстрактный класс-интерфейс Switchable
@abstractmethod# декоратор: метод обязателен к реализации def turn_on(self) -> None:# метод turn_on: включить устройство
pass
@abstractmethod# декоратор: метод обязателен к реализации
def turn_off(self) -> None:# метод turn_off: выключить устройство pass
@abstractmethod# декоратор: метод обязателен к реализации
def is_on(self) -> bool:# метод is_on: проверить состояние включения pass
#АБСТРАКТНЫЙ КЛАСС
# базовый класс для всех устройств системы SOLID-I Если бы Switchable был частью Device — LightSensor был бы вынужден иметь пустые методы turn_on()/turn_off()
class Device(ABC):# абстрактный класс Device, наследник ABC
def __init__(self, device_id: str, name: str):# конструктор Device: принимает id и имя self._device_id = device_id
self._name = name # хочешь добавить новое устройство? Создаёшь класс, наследуешь Device, реализуешь get_status_report() — не трогаешь существующий код.
self.__status = "active"# SOLID-O
def get_id(self) -> str:# селектор get_id: получить идентификатор return self._device_id
def get_name(self) -> str:# селектор get_name: получить название return self._name
@abstractmethod# декоратор: абстрактный метод
def get_status_report(self) -> str:# метод get_status_report: отчёт о состоянии pass# реализация обязательна в подклассах
#ПРОИЗВОДНЫЕ КЛАССЫ(Наследование)
class LightFixture(Device, Switchable):# класс LightFixture: наследник Device, реализует Switchable def __init__(self, device_id: str, name: str, power_watts: float):# конструктор LightFixture
super().__init__(device_id, name)# вызов конструктора базового класса Device self._power_watts = power_watts
self.__is_on = False self._brightness = 100
def turn_on(self) -> None: self.__is_on = True
print(f"светильник '{self._name}' ВКЛЮЧЕН")
def turn_off(self) -> None: self.__is_on = False
print(f"светильник '{self._name}' ВЫКЛЮЧЕН")
def is_on(self) -> bool:# реализация метода интерфейса Switchable: проверка
7
return self.__is_on
def get_status_report(self) -> str:# реализация абстрактного метода Device: отчёт state = "ВКЛ" if self.__is_on else "ВЫКЛ"
return f"светильник {self._name}: {state}, {self._power_watts}Вт, яркость {self._brightness}%"
def set_brightness(self, level: int) -> None:# модификатор set_brightness: изменить яркость self._brightness = max(0, min(100, level))
print(f"яркость '{self._name}' установлена на {self._brightness}%")
def get_power(self) -> float:# селектор get_power: текущее потребление if not self.__is_on:
return 0.0
return self._power_watts * (self._brightness / 100.0)
class LightSensor(Device):# класс LightSensor: наследник Device
def __init__(self, device_id: str, name: str, threshold: float):# конструктор LightSensor super().__init__(device_id, name)# вызов конструктора базового класса Device self._threshold = threshold
self.__current_lux = 0.0
#ТОЛЬКО измеряет свет
#SOLID-S отвечает только за одно
def read_light_level(self) -> float:# селектор read_light_level: прочитать освещённость return self.__current_lux
def set_light_level(self, lux: float) -> None:# модификатор set_light_level: задать освещённость self.__current_lux = lux
def is_dark(self) -> bool:# метод is_dark: проверка "темно ли?" return self.__current_lux < self._threshold
def get_status_report(self) -> str:# реализация абстрактного метода Device: отчёт
return f"датчик {self._name}: {self.__current_lux:.1f} люкс (порог: {self._threshold} люкс)"
def calibrate(self, new_threshold: float) -> None:# модификатор calibrate: калибровка порога self._threshold = new_threshold
print(f"датчик '{self._name}' откалиброван: порог = {self._threshold} люкс")
#Композиция
# класс LightingSchedule: часть StreetLight, не существует отдельно
class LightingSchedule:# класс LightingSchedule: расписание работы фонаря
def __init__(self, on_time: time, off_time: time):# конструктор LightingSchedule: время вкл/выкл self._on_time = on_time
self._off_time = off_time self._enabled = True
def is_active_time(self, current_time: time) -> bool:# метод is_active_time: проверка попадания во временной интервал
if not self._enabled: return False
if self._on_time <= self._off_time:
return self._on_time <= current_time <= self._off_time else:
return current_time >= self._on_time or current_time <= self._off_time
#Ассоциация
#класс StreetLight: ассоциирован с LightFixture и LightSensor (1 к 1) class StreetLight:# класс StreetLight: уличный фонарь
def __init__(self, light_id: str, location: str, fixture: LightFixture, sensor: LightSensor):# конструктор self._light_id = light_id
8
self._location = location
self._sensor = sensor# ассоциация: ссылка на объект LightSensor self._fixture = fixture# ассоциация: ссылка на объект LightFixture
self._schedule = LightingSchedule(# композиция: создание LightingSchedule внутри StreetLight on_time=time(20, 0),
off_time=time(6, 0)
)
self._auto_mode = True
def update(self, current_time: time) -> None:# метод update: основная бизнес-логика обновления print(f"\nобновление фонаря '{self._light_id}' ({self._location})")
print(f"датчик: {self._sensor.get_status_report()}") if self._auto_mode:
if self._sensor.is_dark(): self._fixture.turn_on()
else: self._fixture.turn_off()
else:
if self._schedule.is_active_time(current_time): self._fixture.turn_on()
else: self._fixture.turn_off()
print(f"{self._fixture.get_status_report()}")
def get_power_consumption(self) -> float: return self._fixture.get_power()
#Агрегация
#класс LightingZone: агрегирует StreetLight (части могут существовать отдельно) class LightingZone:# класс LightingZone: зона освещения
def __init__(self, zone_id: str, zone_name: str):# конструктор LightingZone self._zone_id = zone_id
self._zone_name = zone_name
self._lights: Set[StreetLight] = set()# агрегация: множество StreetLight (пустое по умолчанию)
def add_light(self, light: StreetLight) -> None: self._lights.add(light)
print(f"фонарь '{light._light_id}' добавлен в зону '{self._zone_name}'")
def remove_light(self, light: StreetLight) -> None: self._lights.discard(light)
print(f"фонарь '{light._light_id}' удалён из зоны '{self._zone_name}'")
def get_total_power(self) -> float:
return sum(light.get_power_consumption() for light in self._lights)
def update_all(self, current_time: time) -> None: for light in self._lights:
light.update(current_time)
#Зависимость <<use>>
#класс LightingController: зависит от LightingZone (принимает как параметр)
class LightingController:# SOLID-D LightingController не зависит от LightFixture/LightSensor def __init__(self, controller_name: str):# конструктор LightingController
self._name = controller_name self._zones: List[LightingZone] = []
def add_zone(self, zone: LightingZone) -> None: self._zones.append(zone)
def monitor_zone(self, zone: LightingZone) -> None:
9
print(f"зона: {zone._zone_name} ({len(zone._lights)} фонарей)") print(f"общее потребление: {zone.get_total_power():.2f} Вт") for light in zone._lights:
print(f" {light._light_id}: {light._location}")
def system_report(self) -> None:# метод system_report: отчёт по всей системе (зависимость) total_power = 0.0# локальная переменная total_power: суммарная мощность
total_lights = 0# локальная переменная total_lights: суммарное число фонарей for zone in self._zones:# итерация по списку _zones
power = zone.get_total_power()# мощность текущей зоны total_power += power# накопление total_power total_lights += len(zone._lights)# накопление total_lights
print(f" {zone._zone_name}: {power:.2f} Вт")# вывод по зоне print(f"\nИТОГО: {total_lights} фонарей, {total_power:.2f} Вт")# итоговый вывод
#SOLID
#S Single Responsibility (Принцип единственной ответственности) Каждый класс должен отвечать только за одну вещь.
#O Open/Closed (Принцип открытости/закрытости) Открыт для расширения, закрыт для изменения.
#L Liskov Substitution (Принцип подстановки Лисков) Объект подкласса должен заменять объект базового класса без нарушения работы программы.
#I Interface Segregation (Принцип разделения интерфейса) Клиенты не должны зависеть от методов, которые они не используют.
#D Dependency Inversion (Принцип инверсии зависимостей) Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
#ДЕМОНСТРАЦИЯ
#создание светильников (экземпляры LightFixture)
fixture1 = LightFixture("LF-001", "LED-Уличный-150", 150.0) fixture2 = LightFixture("LF-002", "LED-Уличный-100", 100.0) fixture3 = LightFixture("LF-003", "LED-Парк-80", 80.0)
#создание датчиков (экземпляры LightSensor) sensor1 = LightSensor("SN-001", "Датчик-Центр", 30.0) sensor2 = LightSensor("SN-002", "Датчик-Север", 25.0) sensor3 = LightSensor("SN-003", "Датчик-Парк", 35.0)
#демонстрация полиморфизма: список объектов разных классов, общий базовый Device
devices: List[Device] = [fixture1, sensor1, fixture2, sensor2] # LightFixture и LightSensor — потомки
Device Оба реализуют get_status_report()
for device in devices: # Можно подставить LightSensor вместо Device — код for device in devices не сломается
print(f"{device.get_status_report()}") #SOLID-L
# создание фонарей (ассоциация: передача готовых объектов) street_light1 = StreetLight("SL-001", "ул. Ленина, 15", fixture1, sensor1) street_light2 = StreetLight("SL-002", "ул. Ленина, 45", fixture2, sensor2) street_light3 = StreetLight("SL-003", "парк Горького", fixture3, sensor3)
# создание зон (агрегация: фонари существуют отдельно) central_zone = LightingZone("Z-01", "Центральный район") north_zone = LightingZone("Z-02", "Северный район")
central_zone.add_light(street_light1) |
# агрегация: добавление street_light1 в central_zone |
central_zone.add_light(street_light2) |
# агрегация: добавление street_light2 в central_zone |
north_zone.add_light(street_light3) |
# агрегация: добавление street_light3 в north_zone |
# демонстрация низкая освещённость (темно) sensor1.set_light_level(10.0) sensor2.set_light_level(15.0) sensor3.set_light_level(5.0)
10
