OOP设计 - 这在Python中是优质的OO设计还是一个巨大失误?

1 投票
3 回答
1799 浏览
提问于 2025-04-15 18:16

在一个处理订单的系统中,这些订单有支付,而支付又涉及到网关交易,应该把对象设计成这样:

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 个回答

2

给这些对象创建构造函数,也就是一种特殊的方法,用来初始化它们,并让它们可以引用你想要的其他对象。然后,把这些对象的字段变成属性,这样可以检查你给它们赋的类型是否正确。

2

我会用不同的方法来处理这个问题——我会先写一些代码来处理订单、支付等。这能帮助我更清楚地了解我的设计需求,比如可能会发现支付金额(Payment.amount)可能会大于订单总额(Order.total_in_dollars),这是因为有一些处理费用。但接着,我可能会发现这些处理费用应该单独存储,或者甚至应该有自己的模型来表示。

没错,这就是测试驱动开发(TDD)

6

你似乎把应该是实例变量的东西当成了变量,这显然是个错误。换句话说,这些变量应该是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

撰写回答