你不能从一行代码中推断OOP
这个问题有点像是一个语义难题。
但并不是程序的每一行都可以是纯面向对象的。
是的,因为对象方向不能从一行代码中推断出来。这就像看着一块砖,问它是不是两层楼的一部分。砖头不知道。这块砖既不能证明也不能证明它是不是两层楼的一部分。
面向对象也不是一种体系结构,例如Clean architecture就是(这不是特定于CA的,我只是将其用作示例体系结构)。对于体系结构,有一个理想的代码库,大多数时候,你所谓的“干净体系结构”代码库是上述体系结构和你在实践中做出的一些妥协的混合。当我们争论某个东西是否是一个特定的体系结构时,我们真正不同意的是,代码库中有多少部分符合该体系结构。换言之,不同的人对他们认为可以接受的杂质百分比(即折衷)有不同的看法。
面向对象是一种建模哲学。我想从一个过于简单的例子开始。你可以对此吹毛求疵,但它的目的只是作为一个非常简单的基准,而不是一个非常精确的基准。
面向对象性的衡量标准并不是以哪些部分是(不是)面向对象的百分比来表示,而是对整体设计质量和实现深度的衡量。
考虑以下简要内容:
我们需要一个应用程序,在其中我们可以保存供应商,验证我们与他们的合同,并根据这些合同购买物品。
一个面向对象的程序员将会看到小贩
,合同
,项目
虽然这个答案的重点不是定义函数式编程,但函数式程序员最终会得到的相反结论是保存供应商
,确认合同
,购买商品
(我认为在这些名字中省略名词是正确的FP更为正确,但我更倾向于OOP阵营,所以我可能倾向于包括它们)。
这不是一个错误或正确的问题,甚至不是标记代码库的%是或不是面向对象的问题。
最终,面向对象的程序员仍然需要编写节约
,验证
和购买
逻辑,但他们会将其归类为他们设计的对象的一部分。类似地,函数式程序员仍然需要创建某种数据容器,其中包含例如供应商的信息,但他们将根据它如何插入到他们构建的功能中来设计它。
“Oriented”意思是“你用什么作为结构的第一顺序”。这并不意味着“只做这件事”。
静力学上的切线
就像你不能编写一个没有静态方法的程序一样(你至少需要main)
这是一个吹毛求疵的问题,但重要的是要与这个答案的其余部分联系起来。你没有需要主要。但只要你不告诉程序启动时要运行什么方法,那么您需要一个将要运行的默认方法。静态与汉兰达意义上的默认重叠:只能有一个。
静态操作与实例操作的特定性质与您如何决定启动时需要执行什么无关。有足够的相似性,一个可以代表另一个,但它们并没有内在地联系在一起。编译器也可以很容易地默认这样做(新程序())。电源()
与…相反程序。主要
,而不会真正改变正在调用的启动方法的默认性质。
OOP和共享状态
这将启用封装。国家不是要分享的东西,只是改变行为的东西。
这不是OOP的核心原则。这是一个很好的建议,但这并不是OOP概念最初存在的原因。经验丰富的OOP开发人员和FP开发人员都会同意这一点。
这里所做的关键区别,即前封装,是过程明显比静态定义更短暂。您可以更容易地调整流程的逻辑实现(同时保持其整体输入输出结构),而不是更改静态定义,同时保持其与以前的结构兼容。
换言之,函数/方法内在地需要将输入映射到流程,然后(可选)映射到输出,这一事实内在地加强了调用者和实现之间的松散耦合。使用静态定义(例如类周围的接口)可以实现类似的松散耦合,但很容易忘记这样做,这需要付出更多的努力。
特别是对于OOP,这里还有一个额外的考虑因素,即您希望按主题链接处理分组在一起例如,您将在小贩
类。
当您对外公开状态时,您是在让外部人员能够基于您的状态字段编写自己的逻辑。但是,如果这种逻辑依赖于您的状态字段,那么这种逻辑的可能性相当高应该与您分组而不是存在于类的其他使用者中。
这导致人们普遍反对将共享状态作为阻止其他人开发您应该保留所有权的流程的手段。通过禁止共享状态,您就阻止了这种情况的发生。
我们应该禁止进入国家吗?
现在,我也在回答你回答的问题时写下了这句话(这激发了你发帖),但我非常同意这种精神(不是在拥有相关字段的类之外开发逻辑),同时也非常不同意这样的观点,即用毯子包住某些东西是正确的执行方式。
所以我的反馈将更多地暴露一些状态是合理的,但在消费所述状态时,应该评估消费者是否是添加您正在创建的新逻辑的正确位置。
我提倡实用主义,而不是盲目的教条,这意味着我通常反对任何形式的总括规则,除非你能彻底证明没有任何反对它的理由。
然而,有些边界,如库边界,使方法无法移动,因此您可以移动数据。[…]因此OOP理想在现实面前受到了损害。
重申一下,在这个库的示例场景中挂起的东西并不是一个OOP理想,但它仍然是我们希望能够做到的。
库边界意味着在某种程度上失去了对源代码的访问权限,您可以根据扩展的需求对其进行调整。这就是将库与“我的代码库中的其他文件”区分开来的原因。这也是一个语义难题。
让我们探索一个我们可以改变这种情况的世界——开源库具有这种能力。虽然能够扩展它会很好,但这成为一个所有权问题。如果库现在有一个bug,那么最初的作者并不是特别关注它,因为他们既不理解也不积极支持您对其逻辑的扩展。同样,您也可能无法解决该问题。
以开放源代码为例,您可以自由创建自己的代码分支,但库的作者对您的分支没有责任慈善的足够帮你了。
这与良好开发实践的另一个考虑因素有关:明确所有权。只有强制将逻辑与不同作者分离,才能真正确保这一点。
请注意,我所说的“作者”并不是指一个特定的人,而是指一个实体,它承担着任何和所有在其之前或与他们一起工作并编写代码的人的责任。如果Bob离开了MyCompany,而Tom现在为他们工作,并且他继承了Bob的代码,那么MyCompany就是该代码的唯一作者。
还有其他方法可以将库集成到代码库中,而不需要打破所有权的边界。包装是这里的简单解决方案。扩展方法可以帮助您的逻辑感觉与库更加集成,但它们实际上只是只能访问公共数据的副加载帮助器逻辑。
通常经过作者的明确同意(通过他们的设计),可以提供可继承的类,这样您就可以访问受保护的状态并集成您的逻辑,而不仅仅是通过包装它。这很好,但它通常需要库作者进行一些设计考虑,因此他们决定是否真的想承担额外的工作(如果工作正常,还取决于库的新增用户)。
额外的学分和教条为什么不好的例子。
为了获得奖励,请向我展示OOP纯粹主义者如何设计一个没有getter的哈希表。
这更像是另一个语义难题。“Getter”对不同的人意味着不同的东西。在具有实际属性getter(例如C#)的语言中公共字符串MyProp{get;}
),“getter”是指具体的,还是指类似的任何方法公共字符串GetMyProp(){…}
?
如果是前者,那么简单的(但我认为可以忽略)答案是,您可以用一个私有字段替换每个属性,并附带访问它的方法,然后您就从技术上讲在没有吸气剂的情况下创造了一些东西。显然,这并不是对你问题的回答。
此外,这个问题描绘了一个场景,完全忽略了引发前一个问题的建议的目的(这反过来又引发了这个问题)。
如果我没有意识到外部使用者将如何利用我公开的内容,如果以及如何围绕它构建逻辑,以及该逻辑是否更适合添加到我的类中而不是消费者的类中,那么我就不可能最终告诉你如何设计[…]。
如果不知道使用者以及他们打算对您的对象做什么,就没有纯粹的OOP方法来解决这个问题。简单地说,如果你遵循“不公开状态,只公开行为”的准则(为简洁起见,缩写为),那么当你意识到OOP从对象的定义开始,然后在该定义中构建其行为时,你会遇到很多问题。这本质上意味着该对象定义的作者必须始终是该类型定义中包含的行为的作者。
如果它在类型定义之外,那么它违反了您公开行为的“将行为添加到类型”目标。
如果是不同的作者,那么让一个类型由两个不同的作者设计,而他们至少没有意识到对方,这将是不好的。正如我们所建立的那样,在这个场景中,您还不了解您的消费者,因此在这里不可能主动了解您的客户。
这是一个很好的例子,说明了为什么我讨厌盲目教条,并认为它是困扰软件开发的更大问题之一。你在这里提出的问题是对指南文字的盲目应用,你没有注意到你的问题实际上没有触及指南的精神。