Может ли класс ассоциации быть реализован в Python? - PullRequest
0 голосов
/ 07 июня 2018

Я только начал изучать разработку программного обеспечения и моделирую свою систему на диаграмме классов UML.Я не уверен, как бы я реализовать это в коде.

Для простоты давайте возьмем следующий пример: есть Комната и Гостевой класс с ассоциативной Комнатой (0 .. ) - Гость (0 .. ) и ассоциативный класс RoomBooking, который содержит детали бронирования.Как бы я смоделировал это в Python, если моя система хочет видеть все бронирования номеров, сделанные конкретным гостем?

Ответы [ 2 ]

0 голосов
/ 07 июня 2018

Большинство приложений Python, разработанных на основе UML, опираются на реляционную базу данных, обычно через ORM.В этом случае ваш дизайн довольно тривиален: ваш RoomBooking - это таблица в базе данных, а способ поиска всех RoomBooking объектов для данного Guest - это просто запрос ORM.Сохранение его расплывчатым, а не использование определенного синтаксиса ORM, что-то вроде этого:

bookings = RoomBooking.select(Guest=guest)

С RDBMS, но без ORM, это не сильно отличается.Примерно так:

sql = 'SELECT Room, Guest, Charge, Paid FROM RoomBooking WHERE Guest = ?'
cur = db.execute(sql, (guest.id))
bookings = [RoomBooking(*row) for row in cur]

И это указывает на то, что вы будете делать, если вы не используете RDBMS: любое отношение, которое будет храниться в виде таблицы свместо этого внешний ключ хранится в памяти как диктовка.

Например, у вас может быть диктовка, отображающая гостей на наборы номеров:

bookings = guest_booking[guest]

Или, еслиу вас нет большого количества отелей, у вас может быть это неявное отображение, так как в каждом отеле соотношение посетителей к бронированию 1: 1:

bookings = [hotel.bookings[guest] for hotel in hotels]

Поскольку выНачиная с UML, вы, вероятно, думаете в строгих терминах ОО, поэтому вы захотите инкапсулировать этот dict в некотором классе, за некоторыми методами мутатора и метода доступа, так что вы можете быть уверены, что случайно не нарушите никаких инвариантов.

Есть несколько очевидных мест для его размещения: объект BookingManager имеет смысл для сопоставления гостя с набором бронирований, а сам Hotel является таким очевидным местом дляотель-гость-бронирование, что я использовал егомы не думаем выше.

Но другое место для его размещения, более близкое к дизайну ORM, - это атрибут класса типа RoomBooking, доступ к которому осуществляется с помощью методов класса.Это также позволяет вам расширять вещи, если вам позже понадобится, например, искать вещи по отелю - тогда вы поместите два диктанта в качестве атрибутов класса и убедитесь, что один метод всегда обновляет их оба, так что вы знаете, что онивсегда согласованно.

Итак, давайте посмотрим на это:

class RoomBooking
    guest_mapping = collections.defaultdict(set)
    hotel_mapping = collections.defaultdict(set)
    def __init__(self, guest, room):
        self.guest, self.room = guest, room
    @classmethod
    def find_by_guest(cls, guest):
        return cls.guest_mapping[guest]
    @classmethod
    def find_by_hotel(cls, hotel):
        return cls.hotel_mapping[hotel]
    @classmethod
    def add_booking(cls, guest, room):
        booking = cls(guest, room)
        cls.guest_mapping[guest].add(booking)
        cls.hotel_mapping[room.hotel].add(booking)

Конечно, ваш экземпляр Hotel, вероятно, должен также добавить бронирование, поэтому он может вызвать исключение, если два разныхбронирования перекрывают одну и ту же комнату в перекрывающиеся даты, независимо от того, происходит ли это в RoomBooking.add_booking или в какой-либо функции более высокого уровня, которая вызывает как Hotel.add_booking, так и RoomBooking.add_booking.

И если это многопоточность (котораякажется хорошей возможностью, учитывая, что вы идете так далеко по пути разработки в стиле Java), вам понадобится большая блокировка или серия мелкозернистых блокировок вокруг всей транзакции.

Для настойчивости вы, вероятно, хотите хранить эти сопоставления вместе с общедоступными объектами.Но для достаточно маленького набора данных или для сервера, который редко перезапускается, может быть проще просто сохранить открытые объекты и перестроить сопоставления во время загрузки, выполнив несколько вызовов add_booking как часть процесса загрузки.


Если вы хотите сделать его еще более в стиле ORM, у вас может быть один метод find, который принимает аргументы ключевых слов и вручную выполняет «план запроса» тривиальным способом:

    @classmethod
    def find(cls, guest=None, hotel=None):
        if guest is None and hotel is None:
            return {booking for bookings in cls.guest_mapping.values()
                    for booking in bookings}
        elif hotel is None:
            return cls.guest_mapping[guest]
        elif guest is None:
            return cls.hotel_mapping[hotel]
        else:
            return {booking for booking in cls.guest_mapping[guest] 
                    if booking.room.hotel == hotel}

Но это уже подталкивает к тому, что вы можете захотеть вернуться назад и спросить, были ли вы правы, если вы вообще не использовали ORM.Если это звучит нелепо для вашего простого игрушечного приложения, взгляните на sqlite3 для базы данных (которая поставляется с Python и требует меньше работы, чем придумывает путь к pickle или json всем).ваши данные для постоянства) и SqlAlchemy для ORM.Там не так много кривой обучения и не слишком много времени выполнения или шаблона времени кодирования.

0 голосов
/ 07 июня 2018

Конечно, вы можете реализовать это на Python.Но нет единого пути.Довольно часто у вас есть уровень базы данных, где класс ассоциации используется с двумя внешними ключами (в вашем случае с основными цветами Room и Guest).Таким образом, для поиска вы должны просто написать соответствующий SQL для отправки.Если вы хотите кэшировать эту таблицу, вы должны кодировать ее следующим образом (или аналогично) с ассоциативным массивом:

from collections import defaultdict

class Room():
    def __init__(self, num):
        self.room_number = num

    def key(self):
        return str(self.room_number)

class Guest():
    def __init__(self, name):
        self.name = name

    def key(self):
        return self.name

def nested_dict(n, type):
    if n == 1:
        return defaultdict(type)
    else:
        return defaultdict(lambda: nested_dict(n-1, type))
room_booking = nested_dict(2, str)

class Room_Booking():
    def __init__(self, date):
        self.date = date


room1 = Room(1)
guest1 = Guest("Joe")

room_booking[room1.key()][guest1.key()] = Room_Booking("some date")

print(room_booking[room1.key()][guest1.key()])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...