尽管我同意扩展本机类型和对象是一种不好的做法,但不应继承它们。
在一个据称可以支持的gem中(我找不到),使用本机类型的方式如下:
require 'cool-unkown-light-gem'
class MyTypedArray < CoolArray # would love to directly < Array
def initialize(*args)
super(*args)
# some inits for DataArray
@caches_init = false
end
def name?(name)
init_caches unless !@caches_init
!!@cache_by_name[name]
end
def element(name)
init_caches unless !@caches_init
@cache_by_name[name]
end
private
# overrides the CoolArray method:
# CoolArray methods that modify self will call this method
def on_change
@caches_init = false
super
end
def init_caches
return @cache_by_name if @caches_init
@caches_init = true
@cache_by_name = self.map do |elem|
[elem.unique_name, elem]
end.to_h
end
end
父类的任何方法都不会被子类重写,而子类会修改self,例如,在这种情况下,将调用on_change
函数。这样一来,不必重新定义这些方法中的每一个方法,就可以避免跟踪更改。
让我们说MyTypedArray
会排列Foo
个对象:
class Foo
attr_reader :unique_name
def initialize(name)
@unique_name = name
end
end
使用预期行为的简短示例:
my_array = MyTypedArray.new
my_array.push( Foo.new("bar") ).push( Foo.new("baz") )
my_array.element("bar").unique_name
# => "bar"
my_array.shift # a method that removes the first element from self
my_array.element("bar").unique_name
# => undefined method `unique_name' for nil:NilClass (NoMethodError)
my_array.name?("bar")
# => false
我知道我们应该搜索不可变的类,但是那些本机类型支持对同一对象的更改,我们希望有一种适当的方法来进行尽可能简短和轻松的继承。
任何想法,方法或建议当然都是受欢迎的。我不认为我是唯一对此进行过思考的人。
之所以要搜索维护的gem,是因为不同的ruby版本可能为本机类型/类提供不同的支持方法或选项。
[编辑]
以上内容的目的是找出可行的模式。我可以遵循其他帖子的规则和建议,但不会按我预期的方式以及当我认为合适时就可以使事情正常工作(一种编码语言是由人为人制作的,而不是由人类为编码语言而制作的)。我知道每个人都以他们在社区中众所周知的学习,开发和制作事物的成就而感到自豪。
上述目标是因为methods
中的所有Array
都受到欢迎。我不在乎在Ruby 20版本中是否删除了Array的某些方法。到那时,我的应用程序将过时,或者有人将用更少的代码获得相同的结果。
为什么Array
?
因为顺序很重要。
为什么要使用内部Hash
?
由于要使用它,总的来说,构建哈希的成本补偿了它提供的优化。
为什么不只是include Enumerable
?
因为我们只是减少了更改对象的方法的数量,但是实际上没有允许将@caches_init
更改为false
的模式,所以Hash
被重新构建下次使用(与Array
一样的问题)
为什么不只将白名单并包括目标Array
方法?
因为那并不能使我到达想要的位置。如果我希望任何人仍然使用pop
或shift
但又不想重新定义它们,甚至不必费心管理我的mixins并不断使用responds_to?
怎么办? (也许锻炼对提高您的编码技能和从其他人读取代码来说是很好的,但这不是应该的)
我想去哪里?
我希望自己可以重用/继承任何类(重复)任何类(无论它是否是本机)。这是OOP语言的基础。而且,如果我们不是在谈论OOP语言(只是在它的顶部放了一些糖就可以使它显示为OOP),那么让我们保持开放的态度来分析应该有效的模式(无论它们是否奇怪-对我而言)更奇怪的是,没有中间级别;这是许多常规模式的征兆,而这又又是对某些功能的支持不力的征兆,而某些功能比公认的功能要广得多。
为什么宝石应该提供以上?
好吧,让我们谦虚吧。上面是一个非常简单的情况(即使没有涉及)。通过使用某些人想称为 Ruby方式的方法,您可能会在某些时候获得灵活性。但是,当您迁移到更大的体系结构时,要付出代价。如果我想创建要继承的中间类怎么办?丰富的本机类,可增强简单的代码,但仍使其与语言保持一致。说这不是Ruby的方式要比尝试使语言更接近从底开始逐步升级的语言容易。
对于许多人几乎几乎“模糊地”使用Rails和Ruby,我并不感到惊讶。因为在某些时候,如果没有Rails的支持,使用Ruby会遇到很多麻烦。因此,对于Rails如此维护,我并不感到惊讶。
为什么要重新定义pop
或last
或first
方法?为了什么?它们已经实施。
为什么要将方法列入白名单并创建mixins?是面向对象或方法的编程吗?
无论如何...我不希望任何人对此发表看法。我确实看到了其他模式,并且我将继续让我的思想找到它们。如果任何人都足够开放,请随时分享。有人可能会批评这种方法并且是正确的,但是如果您到达那儿是因为它有效。
答案 0 :(得分:2)
要回答您写的问题,否,对此没有任何帮助。在纯Ruby或内部使用的C语言中,这种语言都是不可能的。
检测self
何时更改没有机制,也没有检测方法是纯方法(不更改自身)还是不纯方法(更改自身)的方法。似乎您想要一种“自动”知道一种方法何时是另一种方法的方法,简单地说,这是不可能的,也不是我所知道的任何语言。
在内部(使用您的示例),Array
由C中的RArray
结构支持。struct是简单的存储空间:一种查看任意内存块的方法。 C不在乎您如何选择查看内存,我可以轻松地强制转换此结构的指针,并说它现在是一个指向整数数组的指针并以这种方式更改它,它将像我一样愉快地操作内存告诉它,没有什么可以检测到我这样做的。现在再加上一个事实,任何人,任何脚本或任何gem都可以做到这一点,而您却无法控制它,这仅表明此解决方案从根本上和客观上都是有缺陷的。
这就是为什么在更改对象时需要通知的大多数(全部?)语言都使用观察者模式的原因。您创建了一个在某些情况发生变化时“通知”的函数,并在需要时手动调用该函数。如果有人决定对您的类进行子类化,那么只要它更改了对象状态,他们就只需继续执行该模式以引发该功能。
没有自动执行此操作的方法。正如已经说明的那样,这是一种“选择加入”或“白名单”解决方案。如果要继承现有对象而不是从头开始使用自己的对象,则需要相应地修改其行为。
也就是说,如果您使用一些聪明的别名和module_eval
,class_eval
之类的元编程功能,那么添加功能并不像您想象的那么艰巨。
# This is 100% untested and not even checked for syntax, just rough idea
def on_changed
# Do whatever you need here when object is changed
end
# Unpure methods like []=, <<, push, map!, etc, etc
unpure_methods.each do |name|
class_eval <<-EOS
alias #{name}_orig #{name}
def #{name}(*args, &block)
#{name}_orig(*args, &block)
on_changed
end
EOS
end