像下面这样在父类下定义一个子类是不是很传统?
class Element
class Div < Element
end
class Paragraph < Element
end
end
或者创建一个包含子类的模块更合适?
class Element
end
module Elements
class Div < Element
end
class Paragraph < Element
end
end
或者在模块中创建“基类”并在同一模块中定义子类?
module Element
class Base
end
class Div < Base
end
class Paragraph < Base
end
end
还是强制使用命名约定更好?
class Element
end
class DivElement < Element
end
class ParagraphElement < Element
end
似乎每个库都选择了不同的命名空间/命名约定。
哪一个是最好用的?
每种方法的优缺点是什么?
发布于 2013-02-24 00:12:59
TL;DR:最传统也是最好的方法是使用包含基类及其子类的模块。但是让我们来回顾一下所有的事情。
关于这一点的官方来源并不多;然而,它是一种使用模块来包含库、代码和类组的风格。
超类中的子类
Pros
下
缺点:
将子类放在超类中确实取决于具体情况。然而,这种方法的任何好处也是通过模块方法实现的。但在实践中,这并没有完成。类在这里包含方法、实例变量、类方法等。但是类可以被认为是嵌套的最后一层--除非是非常特殊的情况,否则类中不会有类。
我能想到的有意义的一种情况是,使用子类的唯一方式是通过超类,例如,具有XML
、PDF
等内部子类的Formatter
类。假设您只通过执行Formatter.new(:xml)
等操作来使用这些类。但如果我们这样做,子类应该是私有的,并且无论如何都不能被外部世界访问。在这一点上,继承是一种非常C++y的方式,根本不是Rubyish。
基类在模块外,子类在模块内
优势:
缺点:
这种方法非常不自然。这让它看起来好像Element
与它的子代没有任何关系,或者如果换个角度来看,它的子代是内部实现的细节,不需要处理。无论哪种方式,它看起来都像是破旧的、草率的命名和糟糕的代码结构规划。如果我使用这种方法阅读代码,就必须查看Elements
模块的内容,才能看到Element
被子类化了--这不是最自然的做法。
模块中的类和子类(最佳解决方案)
优势:
include
:超类和所有的元素类都包含在一个命名空间中,允许它们轻松地导入、请求、迭代等。同时也有助于将这些类轻松地include
到任何代码中。Element
和子类之间有一个明显的关联。它们显然是functionality.的捆绑包
缺点:
Base
这样非常模糊的类。这是最好的方法。它使类成为一个整洁的捆绑包,同时仍然显示出明显的关联和明显的“在这里,使用我的Div
类”(与类中的子类策略相反)。此外,这对元编程非常有帮助,在元编程中,将所有内容都放在一个模块中对于让事情正常工作至关重要。最后,这可以很好地与autoload
、require_relative
、include
等构造一起工作。这些构造表明,这就是设计语言的使用方式。
强制使用命名约定
优势:
Div
或Para
等短名称转换为DivElement
和ParaElement
.来消除它们的歧义
缺点:
这是一个非常,,非常糟糕的解决方案。它充斥着编写草率的C风格的代码,几乎没有组织和意义。这些命名约定应该只在没有更好解决方案的语言中使用,而Ruby有很多更好的解决方案。即使在数组中定义所有类也比命名约定更好。
注意:但是,如果您真的想要这样做,您可以在短名称上定义一个命名约定,比如Div
或Para
,只要您仍然将它们保留在一个模块中,那么它就是Elements::DivElement
。然而,这违反了DRY,我不建议这样做。
结论
所以,你确实有两个选择。只需将所有内容放在一个模块中:
module Elements
class Element; end
class Div < Element; end
#etc...
end
或者,将所有内容放在一个具有命名约定的模块中:
module Elements
class Element; end
class DivElement < Element; end
#etc...
end
出于清晰度、标准方法的使用和元编程的原因,我建议使用前者。
发布于 2013-02-23 23:00:09
这是我多次遇到的问题-您有一些共享功能和几个使用它的不同实现类-共享功能和实现类的命名空间具有相同的名称是很自然的。
在您的第一个示例中定义父类下的子类时,我从来没有遇到过问题--除了它有时会使我团队中的其他开发人员感到困惑。因此,根据你的合作伙伴和他们的喜好,我认为这种方法不会有任何问题。
如果有一个有意义的名称,我仍然倾向于使用第二种方法,为名称空间使用不同的名称。如果没有,我会使用第一种方法。
我会避免使用像Base
这样的名称,因为在我看来,这种泛型名称鼓励您在其中添加任何旧的东西,而描述类的功能的名称将(希望)让您在每次添加方法时考虑它是否真的属于那里。
我真的不喜欢你的命名约定示例中的复合名称,我有一种模糊的感觉,认为这就像数据库规范化-每个字段(或类名)应该只包含一条信息。
不管怎样,我希望这对你有帮助。除了我自己的经验之外,我真的不能从任何来源得出结论(这取决于你是否认为这是“可信的”),因为我确实认为这个问题没有绝对的答案。这在很大程度上是主观的。
发布于 2013-02-20 05:55:46
名称空间和继承的用途不同。使用名称空间将一个模块封装在另一个模块中。使用继承来定义一个模块,使用另一个模块的方法/变量/常量作为默认值。
原则上可能会发生这样的情况:您可能希望一个模块既位于同一个模块的名称空间中,又继承自同一个模块,但这取决于您的用例。如果不讨论特定的用例,就无法确定您提出的方法中哪一种是最好的。
https://stackoverflow.com/questions/14967751
复制相似问题