Что такое одноэлементный класс в рубине?

85

Является ли одноэлементный класс в Ruby классом сам по себе? Это причина того, что все объекты принадлежат «классу»? Концепция нечеткая , но я считаю, что она как-то связана с тем, почему я вообще могу определить метод класса ( class foo; def foo.bar ...).

Что такое одноэлементный класс в Ruby?

ключ ночи
источник

Ответы:

154

Во-первых, небольшое определение: одноэлементный метод - это метод, который определен только для одного объекта. Пример:

irb(main):001:0> class Foo; def method1; puts 1; end; end
=> nil
irb(main):002:0> foo = Foo.new
=> #<Foo:0xb79fa724>
irb(main):003:0> def foo.method2; puts 2; end
=> nil
irb(main):004:0> foo.method1
1
=> nil
irb(main):005:0> foo.method2
2
=> nil
irb(main):006:0> other_foo = Foo.new
=> #<Foo:0xb79f0ef4>
irb(main):007:0> other_foo.method1
1
=> nil
irb(main):008:0> other_foo.method2
NoMethodError: undefined method `method2' for #<Foo:0xb79f0ef4>
        from (irb):8

Методы экземпляра - это методы класса (т. Е. Определенные в определении класса). Методы класса являются одноэлементными методами Classэкземпляра класса - они не определены в определении класса. Вместо этого они определены в одноэлементном классе объекта.

irb(main):009:0> Foo.method_defined? :method1
=> true
irb(main):010:0> Foo.method_defined? :method2
=> false

Вы открываете одноэлементный класс объекта с помощью синтаксиса class << obj. Здесь мы видим, что в этом одноэлементном классе определены одноэлементные методы:

irb(main):012:0> singleton_class = ( class << foo; self; end )
=> #<Class:#<Foo:0xb79fa724>>
irb(main):013:0> singleton_class.method_defined? :method1
=> true
irb(main):014:0> singleton_class.method_defined? :method2
=> true
irb(main):015:0> other_singleton_class = ( class << other_foo; self; end )
=> #<Class:#<Foo:0xb79f0ef4>>
irb(main):016:0> other_singleton_class.method_defined? :method1
=> true
irb(main):017:0> other_singleton_class.method_defined? :method2
=> false

Таким образом, альтернативным средством добавления одноэлементных методов к объекту было бы определение их с открытым одноэлементным классом объекта:

irb(main):018:0> class << foo; def method3; puts 3; end; end
=> nil
irb(main):019:0> foo.method3
3
=> nil
irb(main):022:0> Foo.method_defined? :method3
=> false

В итоге:

  • методы всегда должны принадлежать к классу (или: быть методами экземпляра некоторого класса)
  • обычные методы принадлежат классу, в котором они определены (т.е. являются методами экземпляра класса)
  • методы класса - это просто одноэлементные методы Class
  • одиночные методы объекта не являются методами экземпляра класса объекта; скорее, они являются методами экземпляра одноэлементного класса объекта.
Пистос
источник
17
На моем надгробии будет написано: «Покойся с миром, Руби Синглтон. Пистос спас мое рассудок».
rmcsharry
1
@sawa Я ценю намерение ваших правок, но я чувствую, что они слишком сильно меняют смысл и коммуникацию моего сообщения, поэтому я отменил ваши правки.
Pistos
33

Ruby предоставляет способ определения методов, специфичных для конкретного объекта, и такие методы известны как методы Singleton. Когда объявляется одноэлементный метод для объекта, Ruby автоматически создает класс для хранения только одноэлементных методов. Вновь созданный класс называется Singleton Class.


    foo = Array.new
    def foo.size
      "Hello World!"
    end
    foo.size  # => "Hello World!"
    foo.class # => Array
    #Create another instance of Array Class and call size method on it
    bar = Array.new
    bar.size  # => 0
Синглтон-класс - это анонимный класс для конкретного объекта, который автоматически создается и вставляется в иерархию наследования.

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

    foo.singleton_methods  # => [:size]
    bar.singleton_methods  # => []

Эта статья действительно помогла мне понять классы Singleton в Ruby, и в ней есть хороший пример кода.

Bedasso
источник
4
Хотя этому ответу больше года, и ссылка полезна, было бы лучше, если бы вы разместили основные части ответа здесь, на этом сайте, иначе ваш пост рискует быть удаленным См. FAQ, где упоминаются ответы, которые `` чуть больше '' чем ссылка ». Вы все еще можете включить ссылку, если хотите, но только в качестве «ссылки». Ответ должен стоять сам по себе, без ссылки.
Тарин
согласен с @bluefeet здесь
Саурабй
Спасибо @bluefeet, обновил ответ в соответствии с вашим комментарием.
Бедассо
7

Просто обновите ответ @Pistos, начиная с версии 1.9.2 ruby ​​добавляет новый синтаксис для получения одноэлементного класса

 singleton_class = ( class << foo; self; end )

можно заменить на:

singleton_class = foo.singleton_class

https://apidock.com/ruby/Object/singleton_class

Петр Галас
источник
4

Самый прагматичный / ориентированный на действия способ думать об этом (IMHO): как цепочка наследования или порядок поиска / разрешения метода. Эта картинка может помочь

http://www.klankboomklang.com/2007/11/25/modules-part-i-enter-the-include-class/

Это версия 1.9, контрастирующая со встроенными и пользовательскими классами: я все еще перевариваю этот.

http://d.hatena.ne.jp/sumim/20080111/p1

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

Джин Т
источник
Одна из ссылок мертва. А другой - японец!
Ulysse BN
0

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

class User; end
user = User.new
def user.age
  "i'm a unique method"
end
user1 = User.new 
user.age #"i'm a unique method"
user1.age # NoMethodError (undefined method `age' for #<User:0x0000559c66ab7338>)

Как вы можете видеть выше, объект user1 не отвечает на метод age, потому что это одноэлементный метод, метод, однозначно определенный для объекта пользователя. Для этого Ruby создает специальный класс, называемый singleton class, или eigenclass, для размещения этого уникального метода. В этом можно убедиться, выполнив следующие действия:

user.singleton_class # #<Class:#<User:0x0000559c66b47c58>>

Вы также можете спросить Ruby, найден ли здесь метод age, используя объект метода, чтобы узнать, где определен метод age. Когда вы это сделаете, вы увидите, что у класса singleton есть этот метод.

user_singleton_class = user.method(:age).owner # #<Class:#<User:0x0000559c66b47c58>>
user.method(:age).owner == user.singleton_class # true
user_singleton_class.instance_methods(false) # [:age]

Также обратите внимание, что что касается одноэлементного класса, одноэлементные методы на самом деле являются методами экземпляра.

user.singleton_methods == user_singleton_class.instance_methods(false) # true
Паа Яу
источник