...

Реализация моделей с UML-диаграммы классов: примеры кода на Python

UML и код, UML для аналитика, пример проектирования UML, UML диаграмма классов и объектов с примерами кода, обучение UML, курсы по UML для системных и бизнес-аналитиков, Школа прикладного бизнес-анализа Учебный Центр Коммерсант

Как классы и связи между ними с 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
адрес электронной почты (строка) email str
телефон (строка) phone str
физический адрес (строка) address str
Покупатель имя (строка) Customer name str
адрес электронной почты (строка) email str
телефон (строка) phone str

Далее составим UML-диаграмму классов.

UML-диаграмма классов пример, простой понятный пример проектирования UML
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
Ближайшая дата курса
9 декабря, 2024
Продолжительность
16 ак.часов
Стоимость обучения
36 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
Ближайшая дата курса
9 декабря, 2024
Продолжительность
16 ак.часов
Стоимость обучения
36 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-диаграмма объектов, UML моделирование примеры курсы обучение
UML-диаграмма объектов

Эта 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
Ближайшая дата курса
9 декабря, 2024
Продолжительность
16 ак.часов
Стоимость обучения
36 000 руб.

Проверить свои знания UML вы можете прямо на нашем сайте, выполнив бесплатный интерактивный тест.

Открытый тест по UML: основы бизнес-анализа для начинающих

Научитесь самостоятельно разрабатывать эти и другие UML-диаграммы на специализированном курсе «UML для бизнес-аналитиков». Только практика на реальных примерах. После 8-часового интенсива вы сможете дополнять моделировать программную систему, чтобы наглядно объяснить разработчикам, из чего она должна состоять и что делать.

 

Я даю свое согласие на обработку персональных данных и соглашаюсь с политикой конфиденциальности.

Добавить комментарий