【发布时间】:2019-03-31 18:19:51
【问题描述】:
虽然我同意扩展原生类型和对象是一种不好的做法,但不应该从它们继承。
在一个所谓的支持 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 版本可能会为原生类型/类提供不同的支持方法或选项。
[编辑]
上面的目的是找出一个有效的模式。我可以遵循其他帖子的规则和建议,但不会让事情按照我的预期和我认为正确的方式工作(编码语言是由人类创造并为人类创造的,而不是人类为编码语言创造的)。我知道每个人都为自己在学习、开发和制作社区中众所周知的模式方面取得的成就感到自豪。
上述目标是因为Array 的所有methods 都非常受欢迎。我不在乎是否在 Ruby 版本 20 中删除了一些 Array 方法。到那时我的应用程序将过时,或者有人会用更少的代码实现相同的结果。
为什么是Array?
因为顺序很重要。
为什么是内部Hash?
因为我想要使用它,总的来说,构建哈希的成本补偿了它提供的优化。
为什么不只是include Enumerable?
因为我们只是减少了更改对象的方法的数量,但实际上我们并没有允许将@caches_init 更改为false 的模式,所以Hash 在下次使用时重建(所以同样的问题和Array一样)
为什么不将目标 Array 方法列入白名单并包括在内?
因为那不会让我到达我想去的地方。如果我希望任何人仍然使用pop 或shift 但我不想重新定义它们,或者甚至不得不费心管理我的mixin 并不断地使用responds_to? 怎么办? (也许锻炼有助于提高你的编码和阅读他人代码的技能,但这不是应该的)
我想去哪里?
我想处于一个可以重用/继承任何类的位置,我重复一遍,任何类(无论它是否是本地的)。这是 OOP 语言的基础。如果我们不是在谈论 OOP 语言(只是在其顶部添加一些糖以使其看起来像 OOP),那么让我们保持开放的态度来分析应该运作良好的模式(不管它们是否奇怪 - 对我来说更奇怪的是没有中间级别;这是许多传统模式的症状,而这反过来又是对某些功能的支持不足的症状,这些功能比被接受的更广泛需要)。
为什么宝石要提供上述功能?
好吧,让我们谦虚一下。以上是一个非常简单的案例(尽管没有涵盖)。通过使用某些人想要调用的Ruby 方式,您可能会在某些时候获得灵活性。但是,当您迁移到更大的架构时,这是有代价的。如果我想创建中间类来继承怎么办?增强了简单代码的丰富本机类,同时使其与语言保持一致。说 这不是 Ruby 的方式 比试图让语言更接近从底层升级的东西更容易。
Rails 和 Ruby 几乎被许多人“模糊”地使用,我并不感到惊讶。因为在某些时候,如果没有 Rails 支持,使用 Ruby 会带来很多麻烦。因此,我对 Rails 得到如此维护并不感到惊讶。
我为什么要重新定义 pop、last 或 first 方法?为了什么?它们已经实施。
为什么我应该将方法列入白名单并创建 mixin?那是面向对象还是面向方法的编程?
无论如何...我不希望有人分享我对此的看法。我确实看到了其他模式,我会继续让我的大脑找到它们。如果有人足够开放,请随时分享。有人可能会批评这种方法并且是正确的,但如果你做到了,那是因为它有效。
【问题讨论】:
-
您的设计模式并不真正遵循 Ruby 中被视为“正常”的内容。据我所知,您正在尝试实现一个观察者模式,标准库已经有一个帮助模块。 Observable
-
我通常也不认为从
Array继承是好的做法。同样,ruby 的核心库提供了一种(通常)更好的方法来构造具有遍历和搜索方法的类:您在类中包含Enumerable模块。 -
@TomLord 都很好...这不是对其他观点的批评。这是一个问题。你的答案是包含 Enumerable 模块;它仍然有修改自我的方法(即
drop)。我无法将您的答案与我的问题联系起来,尽管我希望您能得到任何反馈以了解我问错了什么,因此问题不会被理解。 -
@ForeverZer0 您能否指定正常模式是什么?感谢您对 Observable 的反馈(看起来很有趣)。但是,我认为它并不比我上面绘制的方法更简单或更好(正常与否)
-
您可能希望澄清实际需要的最终结果行为,因为这看起来可能是XY Problem。即使手动实现它,这也不会是一个强大的解决方案。一旦有人扩展了该类或其祖先之一,使用了混入、别名、鸭子类型等,所有这一切都将被打破,这超出了您的预期,因为可能性是无穷无尽的。
标签: ruby class oop inheritance