【问题标题】:OOP Design - In Python, is this a quality OO Design or an epic fail?OOP 设计 - 在 Python 中,这是一个优质的 OO 设计还是史诗般的失败?
【发布时间】:2010-01-20 22:08:03
【问题描述】:

在接受具有网关交易的付款的订单的系统中,对象应该是这样的:

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 交易发生之前存在。您认为这是否不足?我的想法是不是倒退了?

OR,是否应该更像这样:

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

【问题讨论】:

  • 您似乎有几个与 Python 相关的问题,并且遇到了一些困难 :-) 坚持下去,您的问题很好,他们会得到解答。
  • @e-satis 我真的不是菜鸟,虽然看起来我是。我花了数年时间研究认知科学,这种询问显而易见的想法是提高技能的好方法。例如,在这个问题中,Alex 用他的 weakref 示例回答了这个问题,而我从来没有使用过 weakref。我现在学到了一些关于记忆效率的好东西。

标签: python django oop


【解决方案1】:

您似乎将显然应该是 instance 变量的内容分配为 class 变量,这显然是一个非常错误的策略。 IOW,变量应该是self.total_in_dollars(例如Order)等等,在__init__中赋值,不是类变量,在class语句中赋值!

仅基于总数(可能是一些数字 ID,以便客户和 c 可以以后指具体的顺序)。

不要不必要地重复信息!由于Payment 实例将始终引用Order 实例,因此它应该self.order.total_in_dollars 复制到self.amount - 最好将这些信息放在one place(如果你想很好地访问它,你可以创建一个只读的property);对于事务实例更是如此。

如果 Order 实例携带了影响相应 Payment 实例的创建和行为方式的更多元数据,那没关系,但强烈建议将 Payment 实例的创建作为 Object 类的工厂方法的工作(然后也可以跟踪已经生成的实例,并确保一个给定的 Order 实例永远不会有多个 Payment 实例)。

编辑:既然 OP 已经对 A 进行了一些编辑,我可以确认第一个版本中的依赖项大致正确的观点(同样,数量不应被全部复制这个地方)和第二个版本中的那些,表面上看是不正确的(例如,相互/循环依赖的存在总是一种设计味道,除非特殊应用程序需要清楚明确地证明是合理的——即使需要导航回来并且存在,至少两个链接之一应该是弱引用)。

编辑:当 OP 明确要求提供有关我建议的工厂方法的更多详细信息时,我想到的是这样的:

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

【讨论】:

  • 糟糕,我刚刚编辑了我的错字(无论如何都是第一个示例)。谢谢。
  • 谢谢,这真的很有帮助。你能给我一个例子来说明你如何实现最后一段吗?我很感兴趣(即,我对工厂模式了解不多,但想学习)。
  • 感谢 Alex 提供的示例。我不完全理解它,因为我不明白 weakref 究竟做了什么(几个小时前我才刚刚了解了 weakref 并计划现在对其进行更多研究)。我假设它只是给你一个对象的引用而不增加该对象的引用计数。
  • 另外,*_ 在 ordergone() 方法中做了什么?
  • *whatever 作为def 中的最后一个参数意味着:接受任何进一步的位置参数。对于whatever,我使用_,因为我打算忽略这些参数,并且将_ 命名为您打算忽略的变量是一种常见的约定。是的,“弱引用”(引用和代理,只是细微的区别)用于访问一个对象,只要它还活着但没有保持它活着——在构建它们时,你也可以传递一个回调在对象消失后立即调用它(如果对象在 ref 或代理之前消失)。
【解决方案2】:

给对象构造函数,该构造函数采用您希望它引用的适当其他对象,然后使这些字段成为验证您分配给它们的类型的属性。

【讨论】:

  • 关于缺失部分的方面(即我创建一个没有付款的订单),以及我不知道如何创建付款的部分(即订单可能有一个payment_choice 属性,然后我创建一个 Payment 引用 Order.payment_choice 以确定如何处理它)。
  • 显然不需要一个不需要的值;如果将属性设置为 None 没有任何问题,则允许在属性中使用该属性并将其作为构造函数中的默认参数。
【解决方案3】:

我会以不同的方式处理这个问题 - 通过编写一些代码来处理订单、付款等。这将阐明我的设计需求,例如事实证明,Payment.amount 可能比 Order.total_in_dollars 更大,因为需要支付一些手续费。但是,结果可能是这些处理费用应该单独存储,或者甚至他们可以/应该有自己的模型。 是的,这就是 TDD

【讨论】:

  • 谢谢。那讲得通。换句话说,最有效的风格是根据一路上的需求确定的,而不是完美的面向对象设计的最终方法。
猜你喜欢
  • 1970-01-01
  • 2010-10-14
  • 1970-01-01
  • 2011-08-01
  • 2012-02-10
  • 1970-01-01
  • 2013-06-24
  • 1970-01-01
相关资源
最近更新 更多