Учитывая код 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[])
{
...
}
}
На самом деле в 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
.
И это как раз тот момент, когда приходит время думать о проблемах, а не о решениях. Чего вы хотите? Вы хотите указать, что
Вы можете добиться всего этого, но совершенно другим способом, чем в Java:
|| =
составное назначение для назначения только один раз Единственное, о чем вам нужно беспокоиться, это то, что вы не назначаете @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, это должно быть достаточно ясно. К тому же это очень элегантно.
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" :)
class DeviceController
MY_DEVICE = Device.get_device("mydevice")
end
И да, при необходимости требуется «устройство»
.
Хотя ничто не помешает вам переопределить константу в другом месте, кроме предупреждения :)
Самое близкое, что я могу придумать для конечной переменной, - это поместить рассматриваемую переменную в качестве переменной экземпляра модуля:
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