OOP设计 - 这在Python中是优质的OO设计还是一个巨大失误?
在一个处理订单的系统中,这些订单有支付,而支付又涉及到网关交易,应该把对象设计成这样:
class Order(object):
... Inside init ...
self.total_in_dollars = <Dollar Amount>
self.is_paid = <Boolean Value>
class Payment(object):
... Inside init ...
self.order = order_instance
self.amount = order.total_in_dollars
class GatewayTransaction(object):
... Inside init ...
self.payment = payment_instance
self.amount = <Dollar Amount>
看起来这样做是对的(当然这不是实际的代码,没有具体的金额等等,但你大概明白我的意思)。我这样做是因为订单可以在没有支付的情况下存在,而支付可以在实际的PayPal交易发生之前就存在。你觉得这样设计有什么不足吗?我是不是想错了?
或者,应该更像这样:
class GatewayTransaction(object):
payment = payment_instance
amount = <Dollar Amount>
class Payment(object):
amount = <Dollar Amount>
gateway_transaction = gateway_transaction_instance
class Order(object):
amount_in_dollars = <Dollar Amount>
payment = payment_instance
3 个回答
给这些对象创建构造函数,也就是一种特殊的方法,用来初始化它们,并让它们可以引用你想要的其他对象。然后,把这些对象的字段变成属性,这样可以检查你给它们赋的类型是否正确。
我会用不同的方法来处理这个问题——我会先写一些代码来处理订单、支付等。这能帮助我更清楚地了解我的设计需求,比如可能会发现支付金额(Payment.amount)可能会大于订单总额(Order.total_in_dollars),这是因为有一些处理费用。但接着,我可能会发现这些处理费用应该单独存储,或者甚至应该有自己的模型来表示。
没错,这就是测试驱动开发(TDD)。
你似乎把应该是实例变量的东西当成了类变量,这显然是个错误。换句话说,这些变量应该是self.total_in_dollars
(针对Order
的实例)之类的,在__init__
里赋值,而不是在class
声明中作为类变量赋值!
仅仅创建一个Order
的实例,而没有对应的Payment
实例是可以的(而且应该把is_paid
设置为False
),这完全可以根据总金额来决定(还有一些数字ID,方便将来客户等能引用特定的订单)。
不要不必要地重复信息!因为一个Payment
实例总是会有一个指向Order
实例的引用,所以不应该把self.order.total_in_dollars
复制到self.amount
中——最好把这些信息放在一个地方(如果你想方便访问,可以做一个只读的property
);对于交易实例来说,更是如此。
如果一个Order实例携带了更多的元数据,这些数据会影响相应的Payment实例的创建和行为,那也没问题,但这强烈建议把Payment实例的创建交给一个工厂方法来处理(这个方法可以跟踪已经生成的实例,确保每个Order实例只会有一个Payment实例)。
编辑:现在提问者稍微修改了答案,我可以确认第一版中的依赖关系大致是正确的(除了金额不应该到处复制),而第二版中的依赖关系,表面上看是不正确的(例如,存在相互/循环依赖通常是设计上的问题,除非有特殊的应用需求明确说明——即使需要来回导航,两个链接中至少有一个应该是弱引用)。
编辑:因为提问者明确要求更多关于我提到的工厂方法的细节,我想的内容是这样的:
import weakref
class Payment(object):
def __init__(self, order):
self.order = weakref.proxy(order, self.ordergone)
def ordergone(self, *_):
self.order = None
@property
def amount(self):
if self.order is None: return None
else: return self.order.total_in_dollars
class Order(object):
def __init__(self, amount):
self.total_in_dollars = amount
self.is_paid = False
self._payment = None
@property
def payment(self):
if self._payment is None:
self._payment = Payment(self)
return self._payment