是否有宝石可以提供支持以检测本地红宝石类型实例的更改?

时间:2018-10-27 01:38:56

标签: ruby class oop inheritance

尽管我同意扩展本机类型和对象是一种不好的做法,但不应继承它们。

在一个据称可以支持的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方法

因为那并不能使我到达想要的位置。如果我希望任何人仍然使用popshift但又不想重新定义它们,甚至不必费心管理我的mixins并不断使用responds_to?怎么办? (也许锻炼对提高您的编码技能和从其他人读取代码来说是很好的,但这不是应该的)

我想去哪里

我希望自己可以重用/继承任何类(重复)任何类(无论它是否是本机)。这是OOP语言的基础。而且,如果我们不是在谈论OOP语言(只是在它的顶部放了一些糖就可以使它显示为OOP),那么让我们保持开放的态度来分析应该有效的模式(无论它们是否奇怪-对我而言)更奇怪的是,没有中间级别;这是许多常规模式的征兆,而这又又是对某些功能的支持不力的征兆,而某些功能比公认的功能要广得多。

为什么宝石应该提供以上

好吧,让我们谦虚吧。上面是一个非常简单的情况(即使没有涉及)。通过使用某些人想称为 Ruby方式的方法,您可能会在某些时候获得灵活性。但是,当您迁移到更大的体系结构时,要付出代价。如果我想创建要继承的中间类怎么办?丰富的本机类,可增强简单的代码,但仍使其与语言保持一致。说这不是Ruby的方式要比尝试使语言更接近从底开始逐步升级的语言容易。

对于许多人几乎几乎“模糊地”使用Rails和Ruby,我并不感到惊讶。因为在某些时候,如果没有Rails的支持,使用Ruby会遇到很多麻烦。因此,对于Rails如此维护,我并不感到惊讶。

为什么要重新定义poplastfirst方法?为了什么?它们已经实施。 为什么要将方法列入白名单并创建mixins?是面向对象或方法的编程吗?

无论如何...我不希望任何人对此发表看法。我确实看到了其他模式,并且我将继续让我的思想找到它们。如果任何人都足够开放,请随时分享。有人可能会批评这种方法并且是正确的,但是如果您到达那儿是因为它有效。

1 个答案:

答案 0 :(得分:2)

要回答您写的问题,,对此没有任何帮助。在纯Ruby或内部使用的C语言中,这种语言都是不可能的。

检测self何时更改没有机制,也没有检测方法是纯方法(不更改自身)还是不纯方法(更改自身)的方法。似乎您想要一种“自动”知道一种方法何时是另一种方法的方法,简单地说,这是不可能的,也不是我所知道的任何语言。

在内部(使用您的示例),Array由C中的RArray结构支持。struct是简单的存储空间:一种查看任意内存块的方法。 C不在乎您如何选择查看内存,我可以轻松地强制转换此结构的指针,并说它现在是一个指向整数数组的指针并以这种方式更改它,它将像我一样愉快地操作内存告诉它,没有什么可以检测到我这样做的。现在再加上一个事实,任何人,任何脚本或任何gem都可以做到这一点,而您却无法控制它,这仅表明此解决方案从根本上和客观上都是有缺陷的。

这就是为什么在更改对象时需要通知的大多数(全部?)语言都使用观察者模式的原因。您创建了一个在某些情况发生变化时“通知”的函数,并在需要时手动调用该函数。如果有人决定对您的类进行子类化,那么只要它更改了对象状态,他们就只需继续执行该模式以引发该功能。

没有自动执行此操作的方法。正如已经说明的那样,这是一种“选择加入”或“白名单”解决方案。如果要继承现有对象而不是从头开始使用自己的对象,则需要相应地修改其行为。

也就是说,如果您使用一些聪明的别名和module_evalclass_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