所以我编写了第一个超过微不足道的Python应用程序,我在程序上做了这个,因为这是我迄今为止最好的信息。我现在想重构它以尝试(最后)理解类和OOP。我的问题是关于设计课程的最佳实践,从一个简单的开始发展。
因此,假设我有一个模拟短期股票看跌期权投资组合的应用程序。这意味着在任何给定时间我都可以拥有一个由持有短期看跌期权组成的投资组合(集装箱),以及长期股票(来自后期行使/分配的空头期权)。
因此,就名词而言,有很多事情要发生。有适用于任何权益选项的数据/属性/方法,特定于看跌期权的数据/属性/方法(与看涨期权相比),以及特定于卖出看跌期权的数据/属性/方法(对长看跌期权) 。我可以从概念上说:
class Option(object):
"""Describes an Option"""
pass
class Put(option):
"""Describes an Option that is a Put"""
pass
class Call(option):
"""Describes an Option that is a Call"""
pass
class ShortPut(Put):
"""Describes a Put that is written (sold short)"""
pass
class LongPut(Put):
"""Describes a Put that is purchased"""
pass
class ShortCall(Call):
"""Describes a Put that is written (sold short)"""
pass
class LongCall(Call):
"""Describes a Put that is purchased"""
pass
# then more classes to describe stocks, holdings of a
# given quantity of a stock or option at a specific price
# on a specific date, and a portfolio to contain all the
# holdings etc etc
我正在走向完整的分类法。也许有太多的继承,我读过这可能是一件坏事。但它确实描述了一个对象,并允许添加不同的行为/数据,因为添加了额外的/不同的属性。然而,感觉我有大量的脚手架什么都不做,因为我只需要一个ShortPut对象就可以在这个阶段描述我需要的一切。
此外,我一直非常关注学习良好的单元测试,保持简单,并随时进行构建。所以实际上我觉得它应该是这样的:
class ShortPut(object):
"""Describes a short put"""
pass
这是我目前模拟中唯一的选项,所以我认为这是“界面”。在我看来,如果我正确地封装了这个对象,那么我可以从这个对象开始,并等待重构为一些更大的分类法,只有在保证的情况下。例如,如果我后来需要长期放置,那么我将在这个新对象中重复一个put描述,这将违反DRY。所以我会创建一个更高级别(base?)类来保存公共代码,我会用不同的继承类来描述行为的差异:
class Put(object):
"""Describes an Option that is a Put"""
pass
class ShortPut(Put):
"""Describes a Put that is written (sold short)"""
pass
class LongPut(Put):
"""Describes a Put that is purchased"""
pass
然后我可以继续使用递归过程,因为我的描述和用例变得更加复杂。但是,如果我正确封装,我的目标应该是永远不需要调整任何“下游”代码 - 当我想要一个ShortPut我应该能够继续发出相同的请求,无论它有多复杂和分布在引擎盖下。最后,我的代码中的所有内容实际上都与预期结果有关,因此单元测试对结果有意义。
我是否正确地考虑了这个问题?这对我来说似乎很明显,但后来我不得不撕掉我过去写的很多内容,因为我认为“显而易见”的结果证明是错误的。如果这看起来太基本,或者如果我没有确切地说明所有条款,我会道歉 - 我还在学习。我一直在阅读的所有内容都集中在如何设计类和说明继承或重载等概念的简短示例上,但我没有想到如何简单地设计以着眼于未来的复杂性或可扩展性。谢谢!
答案 0 :(得分:1)
我是否正确地考虑过这个问题?
差不多。特别是如果你是新手OOP,你不应该以概念纯度或完整性为由强迫你的代码结构。 “良好的单元测试,保持简单,随意构建”通常是编写(Python)程序的好方法。但是,不要指望永远不必更改接口并在整个代码库中追逐破碎的用法 - 它不仅仅是“构建”,而且有时“随着时间的推移而破坏和重建”。
答案 1 :(得分:1)
我认为设计的完整性在很大程度上取决于范围。我们可以谈论很多,所以我将分享一些现在让我想到的想法。
从我在这里看到的,您的主要关注点似乎是定价,因此这个OO模型可能就足够了(例如,您不必担心结算,盈亏和现金流)。否则它太简单了。
多头/空头看跌/看涨本质上是一种交易策略。被视为一种选择可能是不对的。沿着同样的路线,我希望看到像EuropeCall / Put或AmericanCall / Put这样的选项类。
这是我目前模拟中唯一的选项,所以我认为这是“界面”。在我看来,如果我正确地封装了这个对象,那么我可以从这个对象开始,并等待重构为一些更大的分类法,只有在保证的情况下。例如,如果我后来需要长期放置,那么我将在这个新对象中重复一个put描述,这将违反DRY。所以我会创建一个更高级别(base?)类来保存公共代码,我会用不同的继承类来描述行为的差异:
Mixin可以在这里作为答案
答案 2 :(得分:1)
由于你自我承认的脚手架倾向,我现在给你的建议是,用一个相当单一的设计编写实际的功能,尽早创建测试用例,然后回到重构到更小,更漂亮等等模块化只有当你看到一个独特的机会 - 重复的代码,类似的情况,没有吸引力的妥协,难闻的气味。当你有一个关于如何重做它们的相当具体的想法时修复它们(当你不这样做时,慷慨地将你的代码洒上诚实的TODO或FIXME评论。学会认识你什么时候试图欺骗自己。)
我知道存在pass
案例的常见做法,但如果你可以避免这样做并且一次只关注一件事,那么你可能会感觉更有效率。我,我总是害怕我会因为忘记而留下愚蠢的pass
。