大多数从 UML 设计开发的 Python 应用程序都由关系数据库支持,通常通过 ORM。在这种情况下,您的设计非常简单:您的RoomBooking 是数据库中的一个表,而您查找给定Guest 的所有RoomBooking 对象的方式只是一个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,您会做什么:任何将存储为具有外键的表的关系都将存储为某种 dict记忆。
例如,您可能有一个将客人映射到房间预订集的字典:
bookings = guest_booking[guest]
或者,或者,如果您没有大量酒店,您可能会隐含这种映射,每个酒店都有客人到预订的一对一映射:
bookings = [hotel.bookings[guest] for hotel in hotels]
由于您是从 UML 开始的,您可能会以严格的 OO 术语进行思考,因此您需要将此 dict 封装在某个类中,在一些 mutator 和 accessor 方法后面,这样您就可以确保您不这样做不要意外破坏任何不变量。
有几个明显的地方可以放置它 - BookingManager 对象对于客人到预订集的映射是有意义的,而 Hotel 本身对于每个酒店来说是一个如此明显的地方 -我没有考虑以上考虑就使用它。
但是另一个更接近ORM设计的地方是在RoomBooking类型上的一个类属性中,通过类方法访问。如果您以后需要,这也允许您扩展事物,例如,按酒店查找事物 - 然后您将两个 dicts 作为类属性,并确保单个方法始终更新它们,所以您知道它们是始终一致。
那么,让我们看看:
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 all 的方法相比,它的使用工作更少用于持久性的数据)和SqlAlchemy 用于 ORM。没有太多的学习曲线,也没有太多的运行时开销或编码时间样板。