之前已经提出过与此类似的问题,但我特别要求使用合成作为使用模块混合的替代方法。
class Helper
def do_somthing
end
end
如果我需要'使用'一个类而不是继承它,我会简单地编写并使用它。
class MyStuff
def initialize
helper = Helper.new
helper.do_something
end
end
为什么我要为此创建一个模块:
module Helper
def do_something
end
end
class MyStuff
include Helper
end
我看到的唯一区别是,如果我使用模块,那么就不会有很多Helper
个物体。但是我没有看到任何东西,周围有更多物体,而不是更大的物体。
此外,我不知道将来是否需要继承它。那么我该如何判断我的库的用户是想使用模块mixin,还是想要使用合成?
答案 0 :(得分:15)
如果Helper
和MyStuff
类之间的关系属于所有权,请使用合成。这被称为“has-a”关系。例如,假设您有Person
类和Car
类。你会使用作文,因为一个人有车:
class Person
def initialize
@car = Car.new
end
end
class Car
def accelerate
# implementation
end
end
当Helper
“的行为类似于” MyStuff
时,请使用模块mixin 。在这种情况下,Helper
采用MyStuff
的角色。这与“is-a”关系略有不同,这意味着您应该使用传统继承。例如,假设我们有一个Person
类和一个Sleeper
模块。一个人有时扮演一个睡眠者的角色,但其他对象也是如此 - Dog
,Frog
或甚至Computer
的实例。其他每一类都代表着可以入睡的东西。
module Sleeper
def go_to_sleep
# implementation
end
end
class Person
include Sleeper
end
class Computer
include Sleeper
end
Sandi Metz的实用Object-Oriented Design in Ruby是这些主题的优秀资源。
答案 1 :(得分:2)
这是“Duck Typing”的问题。如果您希望自己的课程行为类似于Helper
,则可以include
。无论您是encapsulate Helper
行为,正确的选择都是require
。
混合Enumerable
,您可以通过实施唯一的each
方法为您的课程提供大量方法。包装Array
您可以隐藏其他人的迭代并将其用于仅保留您的数据。反之亦然。
答案 2 :(得分:0)
模块mixin更像是多重继承,因此遵循通常的继承与组合规则 - 是-a或has-a。顺便说一句,它是include Helper
,而不是require 'Helper'
。
答案 3 :(得分:0)
我可以分享一些内容:
如果您需要在不同的类和模块中共享共同的行为,您应该将其转换为模块,以便以后只需include
模块就可以了。它也有助于测试,因为它已经干了。
在责任的情况下,您可以将其转换为新模块,以便清楚地理解并提高代码库的可读性。这将有助于减少主要类的大小,这应该易于遵循和维护。
您可能会注意到include
向instance
添加了extend
Class
对{{1}}本身的功能。