【问题标题】:Object oriented design?面向对象设计?
【发布时间】:2009-10-22 00:25:16
【问题描述】:

我正在尝试学习面向对象的编程,但我很难克服我的结构化编程背景(主要是 C,但随着时间的推移还有许多其他)。我想我会写一个简单的检查寄存器程序作为练习。我很快就把一些东西放在一起(python 是一门很棒的语言),我的数据在一些全局变量和一堆函数中。我不知道是否可以通过创建多个类来封装一些数据和函数来改进这种设计,如果可以,如何更改设计。

我的数据基本上是一个账户列表 ['checking'、'saving'、'Amex']、一个类别列表 ['food'、'shelter'、'transportation'] 和代表交易的字典列表 [ {“日期”:xyz,“猫”:xyz,“数量”:xyz,“描述”:xy]。每个帐户都有一个关联的字典列表。

然后我在帐户级别(create-acct()、display-all-accts() 等)和交易级别(display-entries-in-account()、enter-a-transaction( )、edit-a-transaction()、display-entries-between-dates() 等)

用户看到一个账户列表,然后可以选择一个账户并查看底层交易,并能够添加、删除、编辑等账户和交易。

我目前在一个大类中实现所有内容,这样我就可以在整个过程中使用 self.variable,而不是显式的全局变量。

简而言之,我试图弄清楚将其重新组织成一些类是否有用,如果有用,如何设计这些类。我读过一些 oop 书籍(最近是面向对象的思维过程)。我喜欢认为我现有的设计是可读的,不会重复自己。

任何建议将不胜感激。

【问题讨论】:

  • 你读了什么?你读过哪些 OO 设计书籍?
  • @S.Lott,除了我提到的对象入门、Effective C++、Gang of 4,还有一些我无法立即回忆起来的东西。我刚开始看你的在线书。我试过 Head First 书,但不喜欢它们

标签: python oop


【解决方案1】:

您不必放弃结构化编程来进行面向对象编程。代码仍然是结构化的,它只是属于对象而不是与它们分开。

在经典编程中,代码是对数据进行操作的驱动力,导致二分法(以及代码可能对错误数据进行操作的可能性)。

在 OO 中,数据和代码密不可分——一个对象既包含数据又包含对这些数据进行操作的代码(尽管从技术上讲,代码(有时是一些数据)属于类而不是单个对象)。任何想要使用这些对象的客户端代码都只能通过使用该对象中的代码来实现。这可以防止代码/数据不匹配问题。

对于簿记系统,我会按如下方式处理它:

  1. 低级对象是帐户和类别(实际上,在会计中,它们之间没有区别,这是一种错误的分离,只有 Quicken 等人将资产负债表项目与 P&L 分开时才会加剧 - 我将它们称为帐户只要)。一个帐户对象由(例如)一个帐户代码、名称和起始余额组成,尽管在我工作过的会计系统中,起始余额始终为零 - 我一直使用“启动”事务来设置余额.
  2. 交易是一个平衡的对象,由一组具有相关变动(美元价值变化)的账户/类别组成。通过平衡,我的意思是它们的总和必须为零(这是复式记账的关键)。这意味着它是一个日期、描述和一个数组或元素向量,每个元素都包含一个帐户代码和值。
  3. 然后,整个会计“对象”(分类帐)就是所有帐户和交易的列表。

请记住,这是系统的“后端”(数据模型)。希望您有单独的类来查看数据(视图),这将允许您根据用户偏好轻松更改它。例如,您可能需要整个分类账、资产负债表或损益表。或者您可能需要不同的日期范围。

我要强调的一件事是建立一个好的会计系统。你确实需要像簿记员一样思考。我的意思是失去“账户”和“类别”之间的人为差异,因为它会让你的系统更干净(你需要能够在两个资产类别账户之间进行交易(例如银行转账),这赢了如果每个事务都需要一个“类别”,那它就行不通了。数据模型应该反映数据,而不是视图。

唯一的困难是记住资产类别账户的符号与您预期的相反(您的银行现金为负值意味着您在银行有钱,而您的非常高正数 例如,该公司跑车的价值贷款是债务)。这将使复式记账方面完美运作,但您必须记住在显示或打印资产负债表时反转资产类别账户(资产、负债和权益)的符号。

【讨论】:

  • w.r.t. “代码操作错误数据的可能性”:你知道类型系统是干什么用的,对吧?
  • 所以,@MaD70,在您的 C 代码中,all 您的类型将完全类型化,并且您不会在任何地方使用整数或浮点数,是吗?因为,否则,您可能会将错误的数据传递给函数。
  • 我不会用 C 编程,但我当然会尽量使用编程语言 (PL) 类型系统,无论它是面向对象的、过程式的、函数式的、逻辑的(或基于任何范例)PL。这种类型的安全性是由类型系统授予的,而不是因为 PL 是否为 OO。
  • 不流利的 C 语言,我没有意识到即使你的反问问题指的是一个错误的事实:typedef 引入了一个纯粹的类型同义词,相当于它的基本类型类型检查器,因此作为安全措施是无用的。对于其他具有良好标称类型系统的 PL,情况并非如此:新类型标识符引入了与具有相同基本类型/结构的类型不兼容的新类型。
  • 无论如何,类型检查器可以防止您在调用不存在的方法或将错误类型的参数传递给方法时使程序崩溃。如果没有类型检查器,则在编译时(对于静态类型的 PL)或运行时(对于动态类型的 PL)没有消息:程序只会崩溃或开始以一种奇怪的方式运行。
【解决方案2】:

不是直接回答您的问题,但 O'Reilly 的 Head First Object-Oriented Analysis and Design 是一个很好的起点。

后跟Head First Design Patterns

【讨论】:

  • 我知道它们很受欢迎,但我无法阅读这些书
【解决方案3】:

“我的数据基本上是一个帐户列表”

帐户是一个类。

“表示交易的字典”

Transaction 似乎是一个类。您碰巧选择将其表示为字典。

这是您第一次通过 OO 设计。关注责任和合作者。

你至少有两类对象。

【讨论】:

  • 但不要让实现细节(例如由 dict 表示)决定如何建模问题。
  • Accounts 会有 account 相关的方法和 transactions 的 transaction 方法,每个都有方法将相关数据传递给另一个?
  • @rdp:正确。这就是协作问题。
  • 如何编程协作?目前,主程序创建一个应用程序类,其中包含所有方法并根据用户输入调用它们。我是否设置了 Application 类以根据需要创建 Accounts 和 Transactions 实例并调解它们之间的通信?
  • @rdp:正确。 dict 是一个类。您正在用具有专注于字典成员的方法的类替换愚蠢的字典。
【解决方案4】:

您可以采用许多“思维方式”来帮助设计过程(其中一些指向 OO,而另一些则不指向)。我认为从问题而不是答案开始通常会更好(即,与其说“我如何将继承应用到这个”,你应该问这个系统随着时间的推移会如何变化)。

这里有几个问题可能会引导您了解设计原则:

  • 其他人会使用这个 API 吗?他们有可能打破它吗? (信息隐藏
  • 我需要在多台机器上部署它吗? (状态管理、生命周期管理
  • 我需要与其他系统、运行时、语言互操作吗? (抽象和标准
  • 我的性能限制是什么? (状态管理、生命周期管理
  • 这个组件存在于什么样的安全环境中? (抽象、信息隐藏、互操作性
  • 如果我使用了一些对象,我将如何构造我的对象? (配置、控制反转、对象解耦、隐藏实现细节

这些不是对您问题的直接答案,但它们可能会让您处于正确的心态来自己回答问题。 :)

【讨论】:

  • +1 指出它倒退到接受解决方案,然后开始寻找要解决的问题。
  • 有用的心态,尤其是我的第一个问题是类是否有用,而不是如何 oop。
  • Rdp,对不起,如果我冒犯了,我并不是在暗示您将 OOP 视为寻找问题的解决方案,但这对于来自其他领域的人来说可能是个问题。我总是发现 OO 域完全直观,当我开始拥抱功能域时,我正是这样做的,因为 C# 中出现了 LINQ 等。关于 Python,我注意到的一件事(比如说)是函数式语法比 OO 语法要尴尬得多。也许函数式实现可能比 Python 中的 OO 更干净?
  • @Andrew,对不起,如果我似乎被冒犯了。我的部分问题是 OO 是否有用,您为考虑 OO 是否有用提供了一些非常有用的建议。
【解决方案5】:

与其使用字典来表示您的事务,更好的容器应该是集合模块中的命名元组。 namedtuple 是 tuple 的子类,它允许您通过名称和索引号来引用它的项目。

由于您的日志列表中可能有数千笔交易,因此将这些项目保持尽可能小和轻量是值得的,这样处理、排序、搜索等就可以尽可能快速和响应迅速。与命名元组相比,字典是一个相当重量级的对象,命名元组占用的内存并不比普通元组多。与 dict 不同,namedtuple 还具有保持项目有序的额外优势。

>>> import sys
>>> from collections import namedtuple
>>> sys.getsizeof((1,2,3,4,5,6,7,8))
60
>>> ntc = namedtuple('ntc', 'one two three four five six seven eight')
>>> xnt = ntc(1,2,3,4,5,6,7,8)
>>> sys.getsizeof(xnt)
60
>>> xdic = dict(one=1, two=2, three=3, four=4, five=5, six=6, seven=7, eight=8)
>>> sys.getsizeof(xdic)
524

因此,您可以看到,对于 8 项事务而言,这几乎可以节省 9 倍的内存。 我使用的是 Python 3.1,所以你的里程可能会有所不同。

【讨论】:

  • 命名元组从 2.6 开始。无论如何,这更像是一个低级的实现问题而不是设计问题。好记。
猜你喜欢
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-27
  • 2015-09-06
相关资源
最近更新 更多