在下面的父类下面定义一个子类是否常规?
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
似乎每个库都选择不同的命名空间/命名约定。
最好使用哪种?
各自的优点和缺点是什么?
答案 0 :(得分:9)
TL; DR :最常规和最好的方法是使用包含基类及其子类的模块。但是,让我们重温一切。
这方面没有多少官方消息来源;但是,使用模块来包含库,代码和类组是一种风格。
<强>赞成强>:
<强>缺点:强>
将子类置于超类中实际上取决于具体情况。但是,该方法的任何好处也可通过模块方法实现。但实际上,这还没有完成。类在这里包含方法,实例变量,类方法等。但是类可以被认为是最后一级嵌套 - 除非它是一个非常具体的环境,否则你不会在类中有一个类。
我认为这有意义的一种情况是使用子类的唯一方法是通过超类,例如具有内部子类的Formatter
类,如XML
,{ {1}}等等。我们假设您只使用PDF
之类的内容来使用这些类。但是,如果我们这样做,子类应该是私有的,无论如何都不能被外界访问。在这一点上,继承是一种非常C ++的方式,而不是Rubyish。
<强>优点:强>
<强>缺点:强>
这种方法非常不自然。这看起来好像Formatter.new(:xml)
与它的孩子没有任何关系,或者如果看得不一样,那么它的孩子的内部实施细节是无法处理的。 。无论哪种方式,它看起来像破旧,草率命名和糟糕的代码结构规划。如果我正在使用此代码阅读代码,我必须查看Element
模块的内容,看看Elements
完全是子类 - 而且这不是最多的自然要做的事。
<强>优点:强>
Element
添加到任何代码中。include
和子类之间存在明显的关联。它们显然是一组功能。 <强>缺点:强>
Element
这类非常模糊的类命名。这是最好的方法。它使类成为一个整洁的包,同时仍然显示出明显的关联和显而易见的&#34;在这里,使用我的Base
类&#34; (而不是类中的子类策略)。此外,这对于元编程非常有用,其中包含模块中的所有内容对于使事情有效至关重要。最后,这适用于Div
,autoload
,require_relative
等构造。这表明这是设计使用语言的方式。
<强>优点:强>
include
和Div
转换为Para
和DivElement
等短名称,消除歧义。<强>缺点:强>
这是非常,非常糟糕的解决方案。它包含了编写草率,C风格的代码,几乎没有组织和意义。这些命名约定只能在没有更好解决方案的语言中使用,Ruby有很多更好的解决方案。即使定义数组中的所有类也比命名约定更好。
注意:但是,如果您真的想要,可以在ParaElement
或Div
这样的短名称上定义命名约定,只要您仍然将它们保留在模块中,这样就可以了; s Para
。但是,这违反了DRY,我不会暗示它。
所以,你真的有两个选择。把所有东西放在一个模块中:
Elements::DivElement
或者,将所有内容放在具有命名约定的模块中:
module Elements
class Element; end
class Div < Element; end
#etc...
end
我为了清晰,使用标准方法和元编程原因而采用前者。
答案 1 :(得分:3)
这是我多次遇到的一个问题 - 你有一些共享功能和一些使用它的不同实现类 - 共享功能和实现类的命名空间自然具有相同的名称。
我从来没有像在你的第一个例子中那样在其父类下定义一个子类 - 除了,因为它有时会混淆我团队中的其他开发人员。因此,根据您与谁合作以及他们的偏好,我认为这种方法没有任何问题。
如果名称有意义的话,我仍然更喜欢第二种方法,命名空间的名称不同。如果没有,我会使用第一种方法。
我会避免使用像Base
这样的名字,因为在我看来这种通用名称会鼓励你把任何旧东西扔进那里,而一个描述这个类所做的名字会(希望)会让你想想每次添加一个方法,如果它真的属于那里。
而且我真的不喜欢复合名称的粉丝,就像你的命名约定例子一样,我用一种模糊的感觉证明它就像数据库规范化 - 每个字段(或类名)应该只包含一个部分信息。
无论如何,我希望这会对你有所帮助。除了我自己的经验之外,我无法从任何来源中获取(这是由你来决定你是否认为'可信'),因为我认为这个问题没有绝对的答案。这在很大程度上是主观的。
答案 2 :(得分:1)
名称空间和继承用于不同目的。使用名称空间将模块封装在另一个模块中。使用继承来定义模块,使用另一个的方法/变量/常量作为默认值。
原则上,您可能希望模块同时位于同一个模块的名称空间内并从其继承,但这取决于您的用例。如果不讨论特定的用例,就不能确定你提出的那种方式中哪种方式最好。