是否有一个gem提供支持来检测对本机ruby类型实例的更改?

问题描述 投票:-1回答:1

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

在一个所谓的支持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?是一个面向对象或方法的编程?

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

ruby class oop inheritance
1个回答
2
投票

要回答你写的问题,不,没有宝石。这不是语言的可能性,无论是纯Ruby还是内部使用的C语言。

没有任何机制可以检测self何时发生变化,也没有任何方法可以检测方法是纯粹的(不改变自我)还是不纯净(改变自我)。看起来你想要一种“自动”能够知道方法何时是一种方法的方法,并且简单地说,这是不可能的,也不是我所知道的任何语言。

在内部(使用您的示例),Array由C中的RArray结构支持。结构是简单的存储空间:一种查看任意内存块的方法。 C不关心你如何选择查看内存,我可以轻松地转换这个结构的指针,并说它现在是一个指向整数数组的指针并改变它的方式,它会愉快地操纵内存,因为我告诉它,没有任何东西可以检测到我这样做了。现在添加一个事实,任何人,任何脚本或任何宝石都可以做到这一点,你无法控制它,它只是表明这个解决方案从根本上和客观上有缺陷。

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

没有这样做的自动方式。如前所述,这是一种“选择加入”或“白名单”解决方案。如果要子类化现有对象而不是从头开始使用自己的对象,则需要相应地修改其行为。

也就是说,如果你使用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
© www.soinside.com 2019 - 2024. All rights reserved.