可以用Python实现关联类吗?

时间:2018-06-06 21:43:11

标签: python class oop uml

我刚开始学习软件开发,我正在使用UML类图表对系统进行建模。我不确定如何在代码中实现它。

为了简单起见,让我们假设以下示例: 有一个房间和一个客人舱与协会厅(0 .. ) - 客人(0 .. )和一个协会类RoomBooking,其中包含预订详情。如果我的系统想要查看特定客人的所有房间预订,我将如何在Python中对此进行建模?

2 个答案:

答案 0 :(得分:1)

当然可以在Python中实现它。但没有一种方法。通常你有一个数据库层,其中关联类与两个外键一起使用(在你的情况下是RoomGuest的原色)。因此,为了搜索,您只需编写要发送的相应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()])

答案 1 :(得分:0)

从UML设计开发的大多数Python应用程序都由关系数据库支持,通常通过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 ,那么这就指出了您要做的事情:将存储为带外键的表的任何关系都存储为某些关系记忆中的一种词典。

例如,您可能有一个dict将客人映射到客房预订集:

bookings = guest_booking[guest]

或者,或者,如果您没有大量的酒店,您可能会隐含这种映射,每家酒店都有一对一的客人预订映射:

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

既然您已经开始使用UML,那么您可能会考虑严格的OO术语,因此您希望将此dict封装在某些类中,在一些mutator和accessor方法后面,所以你可以确保你不会意外地破坏任何不变量。

有一些显而易见的地方 - BookingManager对象对于客户到预订的映射是有意义的,Hotel本身就是这样一个显而易见的地方。每个酒店客人预订,我没有考虑上面使用它。

但另一个放置它的地方,更靠近ORM设计,在RoomBooking类型的类属性中,由classmethods访问。这也允许你扩展一些东西,如果你以后需要,例如,通过酒店查找 - 你然后把两个dicts作为类属性,并确保一个方法总是更新它们,所以你知道他们& #39;始终保持一致。

所以,让我们来看看:

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_bookingRoomBooking.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。没有太多的学习曲线,也没有太多的运行时开销或编码时间样板。