如何区分组合和自我输入用例

11
Scala有两种表达对象组合的方法:原始的self-type概念和众所周知的简单组合。我想知道在哪些情况下应该使用哪个。
它们的适用性显然有所不同。Self-type要求您使用traits。对象组合允许您在运行时使用var声明更改扩展。
撇开技术细节不谈,我可以想出两个指标来帮助分类使用情况。如果某个对象被用作复杂结构(如树)的组合器或者只有几个类似类型的部分(1个汽车对4个轮子的关系),那么就应该使用组合。这里有一个极端相反的用例。假设一个trait变得太大以至于无法清晰地观察它并且被分裂了。很自然,你应该使用self-types来处理这种情况。
这些规则并不是绝对的。您可以为代码转换使用额外的工作。例如,您可以用Product4的self-typing替换4个轮子的组合。您可以使用`Cake[T <: MyType] {part: MyType}`而不是`Cake{this: MyType =>}`来处理蛋糕模式依赖项。但是,这两种情况都似乎违反直觉,并给您增加了额外的工作。
还有很多边界用例。一对一的关系非常难以决定。有没有简单的规则来决定哪种技术更可取?
self-type使您的类变成抽象的,组合使您的代码冗长。self-type给您带来了命名空间混合的问题,并且还让您免费获得了额外的类型(您不仅有两个元素的鸡尾酒,还有汽油机油鸡尾酒,即汽油弹)。
我该怎么选择它们?有什么提示吗?
更新:
让我们讨论以下示例:
适配器模式。使用自我类型和组合方法有哪些好处?

看起来你已经涵盖了大部分的权衡。我想要补充的是,对于技能较低的团队来说,组合可能更为熟悉。你可以将这视为使用组合的原因,或者是在你所提到的边界情况下不鼓励使用组合,以促进学习和成长。其他条件相等的情况下,出于冗长的原因,我倾向于选择自我类型化。很抱歉这里没有太多帮助,这也是为什么这是一条评论而不是答案。非常好的问题。 - Dave Griffith
你找到满意的答案了吗? - Optimight
我有一些哲学性的答案,没有技术精度,应该用于计算机科学。也许问题本身不恰当。 - ayvango
你能把那些哲学性的答案放在这里吗?这将会非常有帮助。 - Optimight
你在回答中已经涵盖了大部分内容。 - ayvango
1个回答

3
以下提示来自启发式方法(一种在算法方法不可行时使用的试错解决问题的方法),并不受任何公式(基于数学推理)的支持。
*** 这里给出的提示应根据相应提示进行评估,没有提示是区分组合和自我输入用例的完美规则。
(在遵循下面提到的提示时,我不关心代码的冗长程度、行数或编程工作量。)
组成(字典意义):将部分或元素组合成一个整体的行为(平凡组合)
特性(字典意义):一个独特的特点或品质
平凡组合提示(可以通过超-子类机制或关联关系实现)(例如汽车和车轮):
可以离散计算的内容(例如车轮)。
可以根据不同标准进一步分类的内容(例如车轮-铝合金车轮、钢轮等)。
可以添加或删除的内容(注:当我们说车轮停止时,实际上是指车轮的旋转速度停止了,当我们说心脏停止时,实际上是指心脏的跳动速度为零)。
普遍适用于少数(在宇宙中有些车辆和机械有轮子)(少数可以是10-15或数百万-为了说明,让我们了解这个声明:当地质学家谈论时间并说不久前,它指的是几百万年前,这取决于实际主题)。
自我输入提示(特性)(例如汽车和速度):
单向量的内容(不是物理意义上的),可以在数字线上绘制(无论物理单位是什么)(例如速度)。
不能进一步自然分类的内容(例如速度)(或者至少你不会进一步分类)。在这里,“自然”一词用于传达这样的含义,即要对其进行分类,您必须依赖于自己的标准,并且将有一个明确的可能性将其分类为数百万个子类型。以移动为例,您可以拥有数百万个移动子特征...像Zigzag移动、旋转和前进等(各种排列组合的数百万种可能性)。
可以增加、减少或停止的内容(例如速度、愤怒、爱情等)。
通常在非常远离的类别中看到/可以看到的内容(例如光速、地球速度、奔跑者速度)。
通常适用于许多(在宇宙中,大多数(此处每个)物体都有速度)。
软件开发就像制造自己的宇宙一样,作为创造者,您定义了一切。在您的领域(您自己的宇宙)中,Trait将在远离的类别中看到。
请注意,我没有在任何语言中(我只知道很少的语言)看到用于Trivial Composition的特定单词(这里是trait的对应词)。
进一步解释:
要得到答案,您需要在面向类或面向对象的软件开发方法的哲学中找到某个地方,并需要理解编程语言的创建者(如Java和Scala等)的思维和逻辑,这些语言已经将面向类或面向对象范式融入了其中。
另一件事情是深刻理解语义学(研究意义或通过分类和检查意义和形式的变化来研究语言发展),我们用它来描述现实世界以及程序员使用的关键字(在编程语言中)背后的语义。
我认为,当我们创建类时,我们想将真实世界显现在软件中。该类成为真实世界中某些东西的表示,无论是汽车、人类、星星、梦想、思想还是想象等。
当有人说“轮子”时,你会清楚地想到它的形状和用途,你可以想到驾驶车轮或滚动在路上的轮子。轮子总是某物的一部分。它可以被离散数字计数。轮子可以根据材料、应用、大小等标准进一步分类。类似轮子的东西符合Trivial Composition。
当有人说“速度”时,你不会有任何清晰的形象……没有形状……没有颜色……但你可以将其与宇宙中的任何移动(相对性)部分联系起来。这是一个特征,特质。速度不是任何东西的一部分。它可能存在,也可能不存在。它可以在单个线上绘制(正方向或负方向)。很难对“速度”进行分类。像速度这样的东西符合trait。
在我看来,
如果我们将汽车作为类(对象),那么类似于“速度”的特征应该作为scala中的trait。而像“轮子”这样的零件、组件应该作为“Trivial Composition”。 “速度”这样的特征将没有自然分类,而“轮子”可以有许多类别,它们本身是独立的对象(在现实中)。
如果我们将人类作为类(对象),那么像“愤怒、哭泣、笑等”这样的行为应该作为trait,“手、腿、大脑、心脏等”应该作为“Trivial Composition”,因为它们本身是独立的对象(在现实中)。
如果我们考虑名称,它可以赋予任何事物和任何人,即我们最近的恒星有一个名字“太阳”,最高的山脉有一个名字“喜马拉雅山”,我的狗有一个名字“Rocky”,河流有一个名字“亚马逊”…… “名称”是特征,不应考虑为“Trivial Composition”。
如果我们考虑心脏,动物的心脏是它们的一部分。它必须被视为“Trivial Composition”,而不是trait。
什么是类?
类是特定对象的描述或蓝图。
什么是对象?
对象是可以由类定义描述的现实。
(Egg or hen? Which came first?) 我认为,软件工程师首先考虑对象,然后 (描述或创建它们) 从蓝图定义类。(请注意,在面向对象建模和设计中,类和对象相互补充存在。) (“鸡蛋还是鸡先到?”是关于类和对象的共存,并且与著名的圆-椭圆问题(http://en.wikipedia.org/wiki/Circle-ellipse_problem)没有关联,因为后者与继承或子类型多态性有关。)
接口:一种使分离的、有时不兼容的元素有效协调的东西。
软件开发就像创造你自己的宇宙,作为创造者,您定义一切。组合应优先于继承。(四人帮-设计模式)

"对象是可以通过类定义来描述的现实。 (鸡蛋还是鸡?哪个先出现?)由于并非所有现实关系都可以用面向对象编程语言同样地表达,因此这相当值得怀疑,可以参考著名的椭圆-圆问题。" - ayvango
类可以被实例化。Trait 只需要一个字符串来扩展匿名对象,使其具有 Trait 的特性。类和 Trait 都不仅仅专注于数据或行为,它们都将数据与行为封装在一起。 - ayvango
类和特质的主要区别在于,类封装了构造函数,而特质需要明确定义伴生对象,并具有apply和unapply方法。对于类,可以定义相同的对象,但这不是强制性的。 - ayvango
@ayvango 首先,我谈论的是纯面向对象建模概念,因为考虑用例是与任何编程语言无关的阶段。 "许多流行使用的对象系统都基于这样一种设计,即假定一个对象在其整个生命周期中都具有相同的类型,从构造到完成。这不是OOP的限制,而是特定实现的限制。" 请尝试理解我的陈述:“软件开发就像创造自己的宇宙,作为创造者,您定义一切。” - Optimight
我还没有掌握足够的英语,以便在我的语言列表中给它任何位置。所以我没有理解你的话的问题。问题在于Scala本身。我相信它比Java更强大,在这种情况下,Scala可能不接受Java中适用的所有技术。 - ayvango
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接