Есть ли драгоценный камень, который предоставляет поддержку для обнаружения изменений в собственных экземплярах типа ruby? - PullRequest
0 голосов
/ 27 октября 2018

Хотя я согласен с тем, что расширение нативных типов и объектов - плохая практика, наследование от них не должно быть.

В предположительно поддерживающем геме (который я не смог найти) способ, которым нативные типы былибудет использоваться следующим образом:

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

Я понимаю, что мы должны искать неизменяемые классы, но эти нативные типы поддерживают изменения в одном и том же объекте, и мы хотим сделать правильный способнаследство, которое является максимально коротким и простым.

Любые мысли, подходы или рекомендации, конечно, приветствуются.Я не думаю, что я единственный, кто задумался над этим.

Причина, по которой я ищу поддерживаемый гем, заключается в том, что разные версии ruby ​​могут предлагать разные поддерживаемые методы или опции для собственных типов / классов.

[Редактировать]

Цель вышеизложенного состоит в том, чтобы выяснить, какая модель работает.Я мог бы просто следовать правилам и предложениям других постов, но при этом не мог бы заставить вещи работать так, как я намеревался, и когда я это вижу правильно (язык кодирования создан людьми и для людей, а не люди созданы для языков кодирования).Я знаю, что каждый гордится своими достижениями в обучении, разработке и создании вещей в форме, хорошо известной в сообществе.

Цель вышесказанного состоит в том, что все methods из Array более чем приветствуются.Мне все равно, если в версии 20 Ruby они уберут некоторые методы Array.К тому времени мое приложение устареет, или кто-то достигнет того же результата с гораздо меньшим количеством кода.

Почему Array?

Поскольку порядок имеет значение.

Почему внутренний Hash?

Поскольку для использования, которое я хочу использовать, в целом, стоимость создания хеша компенсирует предлагаемую оптимизацию.

Почему бы просто не include Enumerable?

Поскольку мы просто сокращаем количество методов, которые изменяют объект, но у нас фактически нет шаблона, который позволяет изменять @caches_init до false, поэтому Hash перестраивается при следующем использовании (такая же проблема, как и с Array)

Почему бы просто не включить белый список и включить целевые Array методы ?

Потому что это не дает мне того, кем я хочу быть.Что если я хочу, чтобы кто-нибудь все еще использовал pop или shift, но я не хочу переопределять их, или даже беспокоиться о том, чтобы управлять моими миксинами и постоянно использовать responds_to??(возможно, это упражнение полезно для улучшения ваших навыков кодирования и чтения кода от других людей, но это не то, чем оно должно быть)

Где я хочу быть ?

Я хочу быть в том положении, чтобы я мог повторно использовать / наследовать любой, повторяю, любой класс (независимо от того, является ли он нативным или нет).Это является основным для языка ООП.И если мы не говорим о языке ООП (а просто о некотором сахаре наверху, чтобы он выглядел как ООП), то давайте оставим себя открытыми для анализа шаблонов, которые должны хорошо работать (независимо от того, являются ли они странными - для меняболее странно, что промежуточных уровней нет, что является признаком многих традиционных моделей, что, в свою очередь, является признаком плохой поддержки определенных функций, которые требуются более широко, чем то, что принято).

Почемудолжен ли драгоценный камень предлагать выше ?

Хорошо, давай смиримся.Выше приведен очень простой случай (и даже не охваченный).В какой-то момент вы можете добиться большей гибкости, используя то, что некоторые люди хотят назвать Ruby way .Но по цене, когда вы переходите на более крупные архитектуры.Что если я хочу создать промежуточные классы для наследования?Обогащенные нативные классы, которые улучшают простой код, сохраняя его в соответствии с языком.Проще сказать , что это не Ruby , чем пытаться приблизить язык к чему-то, что хорошо растет снизу.

Я не удивлен, что Rails и Ruby почти "невнятно "используется многими.Потому что в какой-то момент, без какой-либо поддержки Rails, то, что у вас есть с Ruby, - большая проблема.Следовательно, я не удивлен, что Rails так поддерживается.

Зачем мне переопределять методы pop, last или first?Для чего?Они уже реализованы.Почему я должен занести в белый список методы и создавать миксины?это объектно-ориентированное программирование?

Во всяком случае ... Я не ожидаю, что кто-то поделится своим мнением по этому поводу.Я вижу другие паттерны и буду позволять своему уму их находить.Если кто-то достаточно открыт, пожалуйста, не стесняйтесь поделиться.Кто-то может критиковать этот подход и быть правым, но если он у вас есть, значит, он сработал.

1 Ответ

0 голосов
/ 28 октября 2018

Чтобы ответить на ваш вопрос так, как написано, нет , для этого нет драгоценного камня.Это не возможно для языка, ни в чистом Ruby, ни в C, который используется внутри.

Нет механизма обнаружения, когда изменяется self, и нет способа определить, является ли метод чистым (не меняет себя) или нечистым (меняет себя).Кажется, вам нужен способ «автоматически» знать, когда метод является тем или иным, и это, проще говоря, просто невозможно, и при этом я не знаю ни одного языка, который мне известен.

Внутренне (используя ваш пример) Array поддерживается структурой RArray в C. Структура - это простое пространство хранения: способ посмотреть на произвольный блок памяти.C не заботится о том, как вы решите посмотреть на память, я мог бы так же легко привести указатель этой структуры и сказать, что теперь это указатель на массив целых чисел, и изменить его таким образом, он с радостью будет манипулировать памятью, когда яскажи это, и ничто не сможет обнаружить, что я так и сделал.Теперь добавьте тот факт, что любой, любой сценарий или любой драгоценный камень может сделать это, и вы не можете его контролировать, и это просто показывает, что это решение фундаментально и объективно ошибочно.

Именно поэтому большинство (все?) языки, которые необходимо уведомлять при изменении объекта, используют шаблон наблюдателя.Вы создаете функцию, которая «уведомляет», когда что-то меняется, и вызываете эту функцию вручную, когда это необходимо.Если кто-то решит создать подкласс для вашего класса, ему нужно только продолжить шаблон, чтобы поднять эту функцию, если он изменит состояние объекта.

Не существует такой вещи, как автоматический способ сделать это. Как уже объяснено, это решение "opt-in" или "белый список".Если вы хотите создать подкласс существующего объекта вместо того, чтобы использовать свой собственный с нуля, вам необходимо соответствующим образом изменить его поведение.

Тем не менее, добавление функциональности не так устрашающе, как вы можете подумать, если вы используете некоторые умные псевдонимы и метапрограммирование с 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...