Большинство приложений 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.Там не так много кривой обучения и не слишком много времени выполнения или шаблона времени кодирования.