Самое близкое представление Ruby 'частной статической заключительной' и 'общедоступной статической заключительной' переменной класса в Java?

Учитывая код Java ниже, что является самым близким, Вы могли представить эти два static final переменные в классе Ruby? И, это возможный в Ruby различать private static и public static переменные как существуют в Java?

public class DeviceController
{
  ...
  private static final Device myPrivateDevice = Device.getDevice("mydevice");
  public static final Device myPublicDevice = Device.getDevice("mydevice");
  ...
  public static void main(String args[])
  {
   ...
  }
}
16
задан Hosh 14 March 2010 в 08:25
поделиться

4 ответа

На самом деле в Ruby нет эквивалентной конструкции.

Однако похоже, что вы делаете одну из классических ошибок при переносе: у вас есть решение на языке A и вы пытаетесь перевести его на язык B, тогда как на самом деле вам нужно выяснить, проблема , а затем выяснить, как решить ее на языке B.

Я не могу точно сказать, какую проблему вы пытаетесь решить из этого небольшого кода-фрагмента, но вот . возможная идея того, как реализовать это в Ruby:

class DeviceController
  class << self
    def my_public_device;  @my_public_device  ||= Device['mydevice'] end

    private

    def my_private_device; @my_private_device ||= Device['mydevice'] end
  end
end

Вот еще одна:

class DeviceController
  @my_public_device  ||= Device['mydevice']
  @my_private_device ||= Device['mydevice']

  class << self
    attr_reader :my_public_device, :my_private_device
    private :my_private_device
  end
end

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

Давайте рассмотрим здесь некоторые концепции.

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

[Примечание: когда я пишу что-то вроде «ни в коем случае», «всегда», «единственный способ» и т. Д., На самом деле это не означает «ни в коем случае, кроме для размышлений ». В данном конкретном случае есть, например, Object # instance_variable_set .]

Другими словами: в Ruby переменные всегда являются частными, единственный способ получить к ним доступ - через метод получения и / или установки, или, как они называются в Ruby, через средство чтения и / или записи атрибутов.

Сейчас я продолжаю писать о переменных экземпляра , но в примере Java у нас есть статических полей , то есть переменных класса . Что ж, в Ruby, в отличие от Java, классы тоже являются объектами. Они являются экземплярами класса Class , поэтому, как и любой другой объект, они могут иметь переменные экземпляра. Итак, в Ruby эквивалент переменной класса - это просто стандартная переменная экземпляра, принадлежащая объекту, который оказывается классом.

(Существуют также переменные иерархии классов, обозначенные двойным знаком @@ sigil . Это действительно странно, и вам, вероятно, следует просто игнорировать их. Переменные иерархии классов используются во всей иерархии классов , т.е. класс, к которому они принадлежат, все его подклассы, и их подклассы, и их подклассы ... а также все экземпляры всех этих классов. На самом деле они больше похожи на глобальные переменные, чем на переменные класса. Их действительно следует называть $$ var вместо @@ var , поскольку они гораздо более тесно связаны с глобальными переменными, чем переменные экземпляра. Они не совсем бесполезны, но очень редко полезны.)

Итак, мы рассмотрели «поле» (поле Java == переменная экземпляра Ruby), мы рассмотрели «общедоступную» и «частную» части (в Ruby переменные экземпляра всегда являются частными, если вы хотите сделайте их общедоступными, используйте общедоступный метод получения / установки), и мы рассмотрели «статическую» часть (статическое поле Java == переменная экземпляра класса Ruby). А как насчет «финальной» части?

В Java «final» - это просто забавный способ написания «const», которого разработчики избегали, потому что ключевое слово const в таких языках, как C и C ++, является тонким сломаны, и они не хотели сбивать с толку людей. В Ruby есть константы (начинающиеся с заглавной буквы). К сожалению, они не совсем постоянны, потому что попытка изменить их при генерации предупреждения действительно работает. Таким образом, они являются скорее соглашением, чем правилом, применяемым компилятором. Однако более важным ограничением констант является то, что они всегда общедоступны.

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

И это как раз тот момент, когда приходит время думать о проблемах, а не о решениях. Чего вы хотите? Вы хотите указать, что

  1. принадлежит классу,
  2. может быть только прочитан, а не записан,
  3. инициализируется только один раз, а
  4. может быть частным или открытым.

Вы можете добиться всего этого, но совершенно другим способом, чем в Java:

  1. переменная экземпляра класса
  2. не предоставляет метод установки, только метод получения
  3. использует Ruby || = составное назначение для назначения только один раз
  4. метод получения

Единственное, о чем вам нужно беспокоиться, это то, что вы не назначаете @my_public_device нигде или, что еще лучше, не не получить к нему доступ вообще. Всегда используйте метод получения.

Да, это дыра в реализации.Ruby часто называют «языком взрослых» или «языком согласия взрослых», что означает, что вместо того, чтобы заставить компилятор применять определенные вещи, вы просто помещаете их в документацию и просто верите, что ваши коллеги-разработчики узнали, что касается других частные люди грубы ...


Совершенно совершенно другой подход к конфиденциальности - это тот, который используется в функциональных языках: используйте замыкания. Замыкания - это блоки кода, которые закрываются над своей лексической средой даже после того, как эта лексическая среда выходит за пределы области видимости. Этот метод реализации частного состояния очень популярен в Scheme, но недавно он был популяризирован Дугласом Крокфордом и др. для JavaScript. Вот пример на Ruby:

class DeviceController
  class << self
    my_public_device, my_private_device = Device['mydevice'], Device['mydevice']

    define_method :my_public_device  do my_public_device  end
    define_method :my_private_device do my_private_device end

    private :my_private_device
  end # <- here the variables fall out of scope and can never be accessed again
end

Обратите внимание на тонкое, но важное отличие версий в верхней части моего ответа: отсутствие сигилы @ . Здесь мы создаем локальные переменные, а не переменные экземпляра . Как только тело класса заканчивается, эти локальные переменные выпадают из области видимости и больше никогда не будут доступны. Только два блока, которые определяют два метода получения, все еще имеют к ним доступ, потому что они закрываются по телу класса. Теперь они на самом деле частные и они final , потому что единственное, что во всей программе все еще имеет к ним доступ, - это чистый геттер. метод.

Это, вероятно, не идиоматический Ruby, но для любого, кто имеет опыт работы с Lisp или JavaScript, это должно быть достаточно ясно. К тому же это очень элегантно.

45
ответ дан 30 November 2019 в 15:43
поделиться

private static в Ruby:

class DeviceController
    @@my_device = Device.get_device("mydevice")
end

public static в Ruby:

class DeviceController       
    def self.my_device; @@my_device; end

    @@my_device = Device.get_device("mydevice")
end

В Ruby не может быть "final" :)

0
ответ дан 30 November 2019 в 15:43
поделиться
class DeviceController
  MY_DEVICE = Device.get_device("mydevice")
end

И да, при необходимости требуется «устройство» .

Хотя ничто не помешает вам переопределить константу в другом месте, кроме предупреждения :)

1
ответ дан 30 November 2019 в 15:43
поделиться

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

class Device
    # Some static method to obtain the device
    def self.get_device(dev_name)
        # Need to return something that is always the same for the same argument
        dev_name
    end
end

module FinalDevice
    def get_device
        # Store the device as an instance variable of this module...
        # The instance variable is not directly available to a class that
        # includes this module.
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice
    def initialize
        # Creating an instance variable here to demonstrate that an
        # instance of Foo cannot see the instance variable in FinalDevice,
        # but it can still see its own instance variables (of course).
        @my_instance_var = 1
    end
end

p Foo.new

p (Foo.new.get_device == Foo.new.get_device)

Это выводит:

#<Foo:0xb78a74f8 @my_instance_var=1>
true

Уловка здесь в том, что, инкапсулируя устройство в модуль, вы можете получить доступ к устройству только через этот модуль. Из класса Foo невозможно изменить , к какому устройству вы обращаетесь, без прямого воздействия на класс Device или FinalDevice ] модуль. Вызов freeze в FinalDevice может или не может быть подходящим, в зависимости от ваших потребностей.

Если вы хотите сделать общедоступный и частный аксессор, вы можете изменить Foo следующим образом:

class Foo
    include FinalDevice

    def initialize
        @my_instance_var = 1
    end

    def get_device_public
        get_device
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

В этом случае вам, вероятно, потребуется изменить FinalDevice :: get_device чтобы принять аргумент.

Обновление: @banister указал, что @fin , как объявлено в FinalDevice , действительно доступен экземпляром Foo . Я лениво предположил, что, поскольку его не было в тексте по умолчанию, выводимом Foo # inspect , его не было внутри Foo .

Вы можете исправить это, более явно сделав @fin переменной экземпляра модуля FinalDevice :

class Device
    def self.get_device(dev_name)
        dev_name
    end
end

module FinalDevice
    def get_device
        FinalDevice::_get_device
    end

    protected
    def self._get_device
        @fin ||= Device.get_device(:my_device).freeze
    end
end

class Foo
    include FinalDevice

    def get_device_public
        get_device
    end

    def change_fin
        @fin = 6
        @@fin = 8
    end

    private
    def get_device_private
        get_device
    end

    private :get_device
end

f = Foo.new
x = f.get_device_public
f.change_fin
puts("fin was #{x}, now it is #{f.get_device_public}")

Что правильно выводит:

fin was my_device, now it is my_device
3
ответ дан 30 November 2019 в 15:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: