Как классы и связи между ними с UML-диаграммы классов реализуются в программном коде, почему для разработчика нет особой разницы между агрегацией и композицией, чем публичные методы отличаются от приватных, и когда в конструкторе инициализации перечисляются не все поля. Пишем на Python классы и объекты интернет-магазина.
От UML-моделей к программному коду: пример реализации
Продолжая говорить про проектирование информационных систем с помощью UML, сегодня рассмотрим, как модели, представленные в виде диаграммы классов, реализуются в программном коде. Как обычно, для демонстрации выберем практический пример интернет-магазина. Сперва сделаем сопоставление сущностей предметной области с классами в таблице.
| Сущность домена | Свойства | Класс | Поле | Тип данных |
| Товар | название (строка) | Product | name | str |
| категория (строка) | category | str | ||
| поставщик (ссылка на поставщика) | provider | Provider | ||
| составные части (ссылки на другие товары) | elements | массив [Product] | ||
| стоимость (вещественное число) | price | float | ||
| Заказ | номер (целое число) | number | int | |
| отметка времени (дата) | timestamp | date | ||
| клиент (ссылка на клиента) | customer | Customer | ||
| список заказанных товаров (ссылка на товар) и их количество (целое число) | products | массив [Product, int;] | ||
| сумма (вещественное число) | sum | float | ||
| Поставщик | название компании (строка) | Provider | company | str |
| ИНН (строка) | inn | str | ||
| адрес электронной почты (строка) | str | |||
| телефон (строка) | phone | str | ||
| физический адрес (строка) | address | str | ||
| Покупатель | имя (строка) | Customer | name | str |
| адрес электронной почты (строка) | str | |||
| телефон (строка) | phone | str |
Далее составим UML-диаграмму классов.

Скрипт PlantUML для создания этой диаграммы приведен в прошлой статье.
Напишем класс, определяющий сущность домена Товар (Product):
class Product:
def __init__(self, name: str, category: str, provider: Provider, price: float)
self.name = name
self.category = category
self.provider = provider
self.elements = []
self.price = price
def add_element(self, product: Product):
self.elements.append({
"element": product
})
В Python метод __init__() инициализирует значения атрибутов при создании нового экземпляра класса, т.е. конкретного объекта, созданного на основе этого шаблона. Таким образом, объект является конкретной реализацией класса, которая имеет свое состояние и поведение. Каждый атрибут получает свое значение из аргументов, переданных в метод __init__().
Конструктор инициализации класса — это специальный метод __init__(), который вызывается при создании объекта этого класса и инициализирует его атрибуты: все или часть из них. Например, при создании нового Товара совсем не обязательно добавлять к нему составные части, т.е. ссылки на другие товары, если их нет. Поэтому в параметрах метода __init__() класса Product не перечислен атрибут elements.
Значения атрибутов сохраняются в экземпляре класса с помощью синтаксиса self.атрибут = значение. Метод add_element() добавляет другой товар со ссылкой на другой объект класса Товар, если добавляемый товар представляет собой комбинацию нескольких частей, например, как брючный костюм состоит из брюк и жакета, причем каждый из них является отдельным, самостоятельным товаром.
DDD, ООП и UML для аналитика
Код курса
BUML
Ближайшая дата курса
8 декабря, 2025
Продолжительность
22 ак.часов
Стоимость обучения
48 000 руб.
Python-код для определения класса Заказ (Order) будет следующий:
class Order:
def __init__(self, number: int, timestamp: date, customer: Customer):
self.number = number
self.timestamp = timestamp
self.products = []
self.customer = customer
self.sum = 0
def add_product(self, product: Product, quantity: int):
self.products.append({
"product": product,
"quantity": quantity
})
self.sum += product.price * quantity
Метод add_product() добавляет товар к заказу и его количество в список товаров, при этом каждый товар представлен в виде словаря, содержащего название товара и его количество в заказе. Также в методе add_product() вычисляется общая стоимость добавляемого товара как произведение его атрибута цены на количество, что потом добавляется к атрибуту суммы заказа self.sum. Так сумма заказа рассчитывается автоматически по мере добавления товаров, а не задается вручную в инициализаторе класса.
Также создадим класс, определяющий поставщика (Provider) с набором полей, в которых будут храниться название компании, ИНН, email, телефон и адрес.
class Provider:
def __init__(self, company: str, inn: str, email: str, phone: str, address: str):
self. company = company
self.inn = inn
self.email = email
self.phone = phone
self.address = address
У покупателя тоже есть email, телефон и адрес, а также имя вместо названия. Код, определяющий сущность Покупатель (Customer), будет следующим:
class Customer:
def __init__(self, name: str, email: str, phone: str, address: str):
self.name = name
self.email = email
self.phone = phone
self.address = address
Как видно из рассмотренных определений классов, все ассоциативные связи в коде реализуются через указание внешнего (по отношению к определяемому) класса как типа данных для конкретного атрибута. Причем, разницы между простой или направленной ассоциацией, агрегацией или композицией в коде нет, хотя на UML-диаграмме классов она присутствует.
Опытный разработчик отметит, что определять атрибуты классов публичными (без использования префикса __), считается плохим тоном, поскольку так они могут быть доступны для чтения и записи за пределами их классов. Обычно программист не позволяет себе такие вольности, делая атрибуты приватными, т.е. ограничивая доступ к ним за пределами класса. Это значит, что их можно изменить или получить значения только через методы доступа, которые обычно называются set() и get() и реализуются разработчиком вручную. Приватные атрибуты позволяют управлять доступом к данным в классе, предотвращая случайные изменения или ошибки, которые могут привести к непредсказуемому поведению программы. Впрочем, такие тонкости следует знать, прежде всего, разработчику, а не аналитику.
Тем не менее, аналитику полезно уметь читать код, даже если он его не пишет. Поэтому далее рассмотрим, как будет выглядеть Python-код для создания объектов этих классов.
DDD, ООП и UML для аналитика
Код курса
BUML
Ближайшая дата курса
8 декабря, 2025
Продолжительность
22 ак.часов
Стоимость обучения
48 000 руб.
Создание объектов: код на Python
Создадим несколько товаров категории женская одежда (ladieswear) от производителя СуперШмот (SuperShmot) под названиями жакет (jacket), брюки (slacks) и брючный костюм (pantsuit):
jacket = Product(name='jacket', category='ladieswear', provider=SuperShmot, price=3.00) slacks = Product(name='slacks', category='ladieswear', provider=SuperShmot, price=5.00) pantsuit = Product(name='pantsuit', category='ladieswear', provider=SuperShmot, price=7.00)
Покажем, что брючный костюм состоит из жакета и брюк, вызвав метод add_element () у объекта pantsuit класса Product:
pantsuit.add_element(jacket) pantsuit.add_element(slacks)
Также создадим несколько объектов класса Заказ:
Order_1 = Order(number=12345, timestamp = date(2023, 20, 7), customer=customer_1) Order_2 = Order(number=6790, timestamp = date(2023, 30, 7), customer=customer_2)
Добавление товаров в заказы реализуется через вызовы метода add_product() у объектов класса Заказ, например, один покупатель добавил себе в заказ 1 пиджак и 1 брюки, а второй покупатель выбрал 3 костюма:
Order_1.add_product(jacket, 1) Order_1.add_product(slacks, 1) Order_2.add_product(pantsuit, 3)
Разумеется, чтобы этот код со ссылками на поставщиков и покупателей, работал, необходимо создать объекты классов Provider и Customer. Создадим поставщика с названием компании СуперШмот, которая находится в Москве с ИНН 1234567890, телефоном +79999999999 и адресом электронной почты sales@super.shmot:
SuperShmot = Provider(
company='СуперШмот',
inn='1234567890',
email='sales@super.shmot',
phone='+79999999999',
address='г. Москва, ул. Проспект, д.1'
)
Наконец, создадим пару клиентов customer_1 и customer_2, чтобы ссылки на них в ранее представленном коде работали корректно:
customer_1 = Customer(
name='Маша',
email='masha@example.com',
phone='+7123456789',
address='г. Москва, ул. Красная, д.1'
)
customer_2 = Customer(
name='Катя',
email='katya@example.com',
phone='+7987654321',
address='г. Москва, ул. Ленина, д.10'
)
Визуализируем созданные объекты на UML-диаграмме объектов, указав кратности связей там, где это имеет смысл. В частности, покажем, что в заказ Order_2, сделанный покупателем по имени Katya (customer_2) входит 3 костюма. И в состав товара костюм (pantsuit) входят одни брюки и 1 жакет, которые также сами являются товарами.

Эта UML-диаграмма объектов получена с помощью следующего скрипта PlantUML:
@startuml
object customer_1 {
name = "Маша"
email = "masha@example.com"
phone = "+7123456789"
address = "г. Москва, ул. Красная, д.1"
}
object customer_2 {
name = "Катя"
email = "katya@example.com"
phone = "+7987654321"
address = "г. Москва, ул. Ленина, д.10"
}
object SuperShmot {
company = "СуперШмот"
inn = "1234567890"
email = "sales@super.shmot"
phone = "+79999999999"
address = "г. Москва, ул. Проспект, д.1"
}
object jacket {
name = "jacket"
category = "ladieswear"
provider = SuperShmot
price = 3.00
}
object slacks {
name = "slacks"
category = "ladieswear"
provider = SuperShmot
price = 5.00
}
object pantsuit {
name = "pantsuit"
category = "ladieswear"
provider = SuperShmot
elements = [jacket, slacks]
price = 7.00
}
object Order_1 {
number = 12345
timestamp = date(2023, 20, 7)
customer = customer_1
products = [(jacket, 1), (slacks,1)]
}
object Order_2 {
number = 6790
timestamp = date(2023, 30, 7)
customer = customer_2
products = [(pantsuit,3)]
}
jacket --> SuperShmot
slacks --> SuperShmot
pantsuit --> SuperShmot
pantsuit "1" *--> "1" jacket
pantsuit "1" *--> "1" slacks
Order_1 --> customer_1
Order_2 --> customer_2
Order_2 "1" o-> "3" pantsuit
Order_1 "1" o--> "1" slacks
Order_1 "1" o--> "1" jacket
@enduml
В заключение подчеркну еще раз, что все ассоциативные связи в коде реализуются через указание внешнего (по отношению к определяемому) класса как типа данных для конкретного атрибута, без учета того, как это было обозначено (простая/направленная ассоциация, агрегация или композиция) на UML-диаграмме классов.
DDD, ООП и UML для аналитика
Код курса
BUML
Ближайшая дата курса
8 декабря, 2025
Продолжительность
22 ак.часов
Стоимость обучения
48 000 руб.
Проверить свои знания UML вы можете прямо на нашем сайте, выполнив бесплатный интерактивный тест.
Научитесь самостоятельно разрабатывать эти и другие UML-диаграммы на специализированном курсе «UML для бизнес-аналитиков». Только практика на реальных примерах. После 8-часового интенсива вы сможете дополнять моделировать программную систему, чтобы наглядно объяснить разработчикам, из чего она должна состоять и что делать.


