首页 > 解决方案 > 用于将嵌套数据添加到模型内部或外部的函数?

问题描述

假设我有一个类/模型建筑,它与类/模型墙有关,这又与类/模型窗口相关,其形式是一个建筑物可以有很多表面,一个表面可以有很多窗户(一对多)。现在,当我想在该建筑物中添加窗口时,也许也只是在某些表面上,是否应该在模型中编写功能(也包括搜索功能/循环)?还是在从模型调用或从外部调用的单独类/脚本中?

我可以想象,当功能是模型的一部分时,从长远来看,当需要进行更改时,它可能会导致问题。

什么是更清洁的架构/标准,因为两者都可以工作?如果可能的话,你能给我一个资源来阅读更多关于这个特定问题的信息吗?

在我的情况下,我将 python 与 sqlalchemy 和 postgres 一起使用,但这个问题对于其他编程语言也可能是合法的。

(我希望这个问题不是太宽泛/基于意见)

标签: pythonpostgresqlsqlalchemy

解决方案


对于初学者,我认为在Softwareengineering中提出这个问题可能会更好。但是,我不妨给你我的几分钱。

通常,这取决于...

一般来说,封装是面向对象编程的核心概念之一。对象状态的任何更改都应由对象本身完成(尽管可能由外部触发),因此保证符合您为对象定义的条款和条件。对象的行为应该在对象内部而不是外部实现。

你不想公开你Window的 's 属性wall让全世界直接访问它。你想把它隐藏在 getter 和 setter 后面。如果说碰巧是“内部” ,您希望Window拒绝被放置在Wall传递给其设置者的对象上。您不希望对象直接将's从 'open' 更改为 'close',反之亦然,您希望调用's resp。方法,例如在内部确保关闭的窗口不会再次关闭。wallWallPersonWindowstatePersonWindowopen()close()

此外,隐藏实现细节可以帮助维护您的界面并使对类的更改变得透明。例如,假设您决定,除了不允许内墙外,您现在还想防止“普通”窗户被放置在地下室的外墙上。您可以在现有的wall设置器中实现该检查Window,外部代码的唯一可见更改将是拒绝的另一个潜在原因(“window=normal and wall=basement”以及“wall=interior”)。或者您想添加一个表示您的清洁状态的属性,Window并且为了正确区分cleanliness_state新旧 'open'/'close' state,您希望将旧属性重命名为open_close_state.open()close()(并且可能is_open()is_closed())读取和写入您的“打开”/“关闭”state属性,此更改只会影响您的类实现,而不是使用它的每一段代码。

然而!

您可能拥有仅用作某种集合的类,即数据 。它们几乎没有实现任何功能,并公开将它们的属性公开给全世界读写,因此广泛地忽略了封装的概念。有人可能会争辩说,在对象关系映射层(例如 SQLAlchemy)中实现的类/模型更像是数据对象/数据类,而不是 OOP 意义上的对象,尤其是在主要用于持久化和检索结构化数据时。让外部代码更改此类对象的状态或实现其功能并不罕见,例如 Django 框架中的视图使用自己的 ORM 层来实现和持久化模型。

所以?

它归结为您的具体案例。您已经提到您考虑限制窗口的放置;可能基于所涉及的窗户和墙壁的属性。

如果您认为您的 SQLAlchemy 模型不仅仅是一种持久化对象的方式,请继续并立即在您的模型中实现行为和更改逻辑。但是请记住,a)您最终可能会与模型基类的方法/属性产生冲突,并且 b)模型的属性必须保持公开暴露以维护 ORM 层的功能(尽管 SQLAlchemy 可能能够工作只要定义了 getter 和 setter 的属性;我从未测试过)。

如果您希望模型成为保存和检索结构化数据的一种相当方便的方法,请保持它们干净并使用一些实用函数或类来实现对象的行为并确保其在代码中使用时的契约;例如place_window_on_wall(window: Window, wall: Wall),当您尝试在' 的属性Wall上引用对象时,具有处理验证和限制的功能。但请记住,对模型的更改也必须反映在这些函数/类中。Windowwall

我认为这两个选项都有效;无论您选择什么,都与您的决定保持一致。


推荐阅读