Пошаговое руководство по Low-Level Design: анализ требований, выделение классов, наследование, композиция и код на Python. Начни проектировать правильно!
Когда вы пишете код без плана, рано или поздно он превращается в спагетти. Low-Level Design (LLD) — это мост между требованиями и чистым, расширяемым кодом. В этой статье вы пройдёте 8 шагов от анализа требований до готового кода на Python. На примере редактора документов и парковки вы научитесь выделять классы, строить иерархии и применять композицию. Готовы? Поехали!
Прежде чем создавать классы, чётко поймите, что должна делать система. Задайте вопросы:
Пример: Редактор документов
Требования:
Никогда не прыгайте сразу к классам. Сначала поймите задачу.
Существительные из требований часто становятся классами.
Пример:
Document, TextElement, ImageElement, Storage, DocumentEditorДругой пример: Парковка
ParkingLot, Floor, Slot, Vehicle, TicketСпросите: «Можно ли сказать, что дочерний класс ЯВЛЯЕТСЯ родительским?»
Пример:
TextElement IS-A DocumentElementImageElement IS-A DocumentElementNewLineElement IS-A DocumentElementTabSpaceElement IS-A DocumentElementИерархия:
DocumentElement
▲
|
----------------------------------
| | | |
TextElement ImageElement NewLineElement TabSpaceElementКод на Python:
from abc import ABC, abstractmethod
class DocumentElement(ABC):
@abstractmethod
def render(self) -> str:
pass
class TextElement(DocumentElement):
def __init__(self, text: str):
self.text = text
def render(self) -> str:
return self.text
class ImageElement(DocumentElement):
def __init__(self, src: str):
self.src = src
def render(self) -> str:
return f'<img src="{self.src}" />'Правило: используйте наследование только когда дочерний класс ЯВЛЯЕТСЯ родительским. Примеры: Car IS-A Vehicle, Dog IS-A Animal, CreditCardPayment IS-A Payment.
Спросите: «Какие объекты содержатся внутри другого объекта?»
Пример: документ содержит элементы.
class Document:
def __init__(self):
self.elements: list[DocumentElement] = []Это означает: Document HAS-A collection of DocumentElement.
Другой пример: ParkingLot HAS-A Floor, Floor HAS-A Slot.
Правило: используйте композицию/агрегацию когда родитель ИМЕЕТ дочерний объект. Примеры: Car HAS-A Engine, Document HAS-A Elements.
Спросите: «Какие действия могут выполнять объекты?»
Пример редактора документов:
add_text(), add_image(), render(), save()Пример парковки:
park_vehicle(), unpark_vehicle(), generate_ticket()Правило: глаголы становятся методами. Существительные → классы, глаголы → методы.
Спросите: «Что может измениться в будущем?»
Пример сохранения документа: файл, база данных, облако. Это разные реализации. Создаём абстракцию:
class Persistence(ABC):
@abstractmethod
def save(self, data: str) -> None:
pass
class FileStorage(Persistence):
def save(self, data: str) -> None:
with open('document.txt', 'w') as f:
f.write(data)
class DBStorage(Persistence):
def save(self, data: str) -> None:
# сохранение в БД
passЗавтра мы можем добавить CloudStorage, не меняя существующий код.
Правило: когда есть несколько реализаций, создайте интерфейс/абстрактный класс. Примеры: Payment (UPI, Card, Cash), Storage (File, DB, Cloud), Notification (Email, SMS, Push).
После определения классов, методов, наследования и композиции нарисуйте диаграмму классов UML.
Пример для редактора:
Document
|
| содержит
v
DocumentElement
▲
|
-----------------------
| | |
Text Image NewLine
Persistence
▲
|
---------------
| |
FileStorage DBStorageПреобразуйте UML в код на Python. Полный пример:
from abc import ABC, abstractmethod
# Абстрактный класс элемента документа
class DocumentElement(ABC):
@abstractmethod
def render(self) -> str:
pass
# Конкретные элементы
class TextElement(DocumentElement):
def __init__(self, text: str):
self.text = text
def render(self) -> str:
return self.text
class ImageElement(DocumentElement):
def __init__(self, src: str):
self.src = src
def render(self) -> str:
return f'<img src="{self.src}" />'
class NewLineElement(DocumentElement):
def render(self) -> str:
return '\n'
class TabSpaceElement(DocumentElement):
def render(self) -> str:
return '\t'
# Композиция: документ содержит элементы
class Document:
def __init__(self):
self.elements: list[DocumentElement] = []
def add_element(self, element: DocumentElement) -> None:
self.elements.append(element)
def render(self) -> str:
return ''.join(el.render() for el in self.elements)
# Абстракция сохранения
class Persistence(ABC):
@abstractmethod
def save(self, data: str) -> None:
pass
class FileStorage(Persistence):
def save(self, data: str) -> None:
with open('document.txt', 'w') as f:
f.write(data)
# Редактор с внедрением зависимости
class DocumentEditor:
def __init__(self, storage: Persistence):
self.doc = Document()
self.storage = storage
def add_text(self, text: str) -> None:
self.doc.add_element(TextElement(text))
def add_image(self, src: str) -> None:
self.doc.add_element(ImageElement(src))
def save(self) -> None:
rendered = self.doc.render()
self.storage.save(rendered)
# Пример использования
if __name__ == '__main__':
storage = FileStorage()
editor = DocumentEditor(storage)
editor.add_text('Привет, мир!')
editor.add_image('logo.png')
editor.save()
print(editor.doc.render())Теперь, когда вы знаете 8 шагов LLD, примените их на практике. Возьмите любую задачу (например, проектирование корзины интернет-магазина) и пройдите шаги: требования → существительные → классы → IS-A → HAS-A → методы → вариации → UML → код. Чем больше практики, тем быстрее вы будете проектировать чистые системы. Удачи на собеседованиях и в реальных проектах!
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →