Лучшие практики и реализации для методов доступа на макроуровне и уровне класса? - PullRequest
1 голос
/ 22 марта 2011

Я проектирую / строю систему классов, которые все наследуются от одного базового класса.

Цель состоит в том, чтобы иметь простые в использовании унаследованные макро-методы, которые выглядят примерно так:

class Something < A::Base
  full_name 'Something that goes bump in the night.'
end

Любой код должен иметь возможность запрашивать у класса эту информацию (или, вероятно, нормализованную / производную информацию) позже с помощью метода (ов) доступа уровня класса.

puts Something.full_name
  # => "Some kind of calculated value that may or may not go bump in the night."

Учитывая, что A::Base включает / расширяет / как-то иначе смешивает как модуль с макро-методом, который работает примерно так:

module MacroMethods
  private
  def full_name(full_name)
    # non-trivial, one-time-only set-up code exists here in actual usage
  end
end

, так и модуль с классом-метод доступа уровня, который работает примерно так:

module AccessorMethods
  public
  def full_name
    # a non-trivial, runtime-calculated value is returned here in actual usage
  end
end

независимо от того, как я их смешиваю, я постоянно сталкиваюсь с конфликтами имен (т. е. ' неверное количество аргументов (1 для 0) (ArgumentError) ') между двумя.

Примечание: full_name - самый простой пример того, что необходимо;другие, более сложные макросы / средства доступа обеспечивают негибкие ограничения макро-методов, которые необходимо объявлять внутри класса и устанавливать их один раз и только один раз.


MyВопрос в два раза:

  1. Есть ли способ заставить все это работать внутри класса A::Base?
  2. Этоправильный способ сделать это в Ruby? Есть ли лучший способ сделать это, достигнув того же результата?

Опции, которые были рассмотрены:

  • Вызов метода (методов) макроса или метода доступа к чему-либо еще.
    (например, в классе Something: set_up_full_name 'Something that …')
    Недостатком является то, что наименование сбивает с толкуи нетрадиционные.

  • Создание метода (ов) метода доступа на уровне экземпляра вместо уровня класса.
    (например, puts a_something.full_name')
    Недостатком является то, что черты, установленные макросами, присущи классу, а не каждому экземпляру (в некоторых случаях только ссылка накласс может быть доступен, но не экземпляр) .

  • Создание единого метода, который обрабатывает как макросы, так и функции доступа.
    (например, в классе A :: Base: def self.full_name(*args) …)
    Недостатком является то, что методы макросов больше не могут быть закрытыми, а RDoc выглядит как sh * t.

  • Вместо этого используются методы abstact / virtual-ish.
    (например, в классе Something: def self.full_name; 'Something that …'; end)
    Недостатком является то, что это больше кода в подклассах ибольше чем Objective-C (или C ++, или Java, ...) , чем хорошая парадигма Ruby.

Ответы [ 4 ]

1 голос
/ 26 января 2012

Слип, я внимательно прочитал твой вопрос. Вы не можете иметь 2 разных метода с именем full_name, определенных для одного и того же объекта одновременно. НО, вы могли бы сделать что-то вроде этого:

module MacroMethods
  private
  def full_name(full_name)
    # non-trivial, one-time-only set-up code exists here in actual usage
    # this method can only be called once in the definition of any given class,
    #   because after the first call, it will be redefined!
    extend AccessorMethods
  end
end

module AccessorMethods
  public
  def full_name
    # a non-trivial, runtime-calculated value is returned here in actual usage
  end
end

class Base
  extend MacroMethods
end

class Something < Base
  full_name 'after this call, I will get a new full_name method!'
end

class SomethingElse < Base
  full_name 'so will I!'
end
0 голосов
/ 23 марта 2011

Это кажется излишне сложным. Почему бы просто не использовать атрибут в родительском классе?

class Base
  class << self
    attr_accessor :full_name
  end
end

class A < Base; end

class B < Base; end

A.full_name = "The full name of A"
B.full_name = "The full name of B"

puts A.full_name # "The full name of A"
puts B.full_name # "The full name of B"
0 голосов
/ 25 марта 2011

Похоже, что большинство других ответов имеют правильную идею, но не имеют метода получения для #full_name.Этот пример может быть тем, что вы ищете:

class Thing
  class << self
    attr_writer :full_name

    def full_name
      "My full name is #{@full_name}"
    end
  end
end

С этим вы можете сделать что-то вроде этого:

> Thing.full_name = 'Thing Class'
=> "Thing Class"
> Thing.full_name
=> "My full name is Thing Class"
0 голосов
/ 23 марта 2011

Если вы хотите, чтобы макросы классов были доступны для определенных классов, то общий базовый класс не является решением Ruby. Вместо этого вы создаете модуль, расширяющий базовые классы функциональностью, которую вы хотите, чтобы они имели:

module Extensions
  def self.included(base_class)
    base_class.extend ClassMethods
  end

  module ClassMethods
    attr_accessor :full_name
  end
end

class Something
  include Extensions

  self.full_name = "Something that goes bump in the night"
end

puts Something.full_name    # => Something that goes bump in the night
thing = Something.new
puts thing.full_name        # Error

Переопределяет метод ловушки в Extensions, называемый Module#included, который передает любой класс, включающий модуль, в качестве аргумента. Затем новый метод вызывает Object#extend базового класса, чтобы поместить методы, доступные в ClassMethods , непосредственно в этот класс как методы класса . Это работает так же, как определение методов класса, но работает динамически. Это освобождает вас от необходимости использовать ваш единственный базовый класс в классе, который предоставляет макросы. Обратите внимание, что методы не определены в экземплярах классов, которые включают модуль.

...