Yuvalanmış sınıflar ve modüllerde yuvalanmış sınıflar ne zaman kullanılır?


144

Alt sınıfları ve modülleri ne zaman kullanacağımı çok iyi biliyorum, ancak son zamanlarda böyle iç içe sınıflar görüyorum:

class Foo
  class Bar
    # do some useful things
  end
end

Bunun gibi modüllerde yuvalanmış sınıfların yanı sıra:

module Baz
  class Quux
    # more code
  end
end

Ya belgeler ve makaleler seyrek ya da doğru arama terimleri için grope için yeterince eğitimli değilim, ama konu hakkında fazla bilgi bulamıyorum görünmüyor.

Birisi, bu tekniklerin neden / ne zaman kullanılacağına ilişkin mesajlara örnekler veya bağlantılar verebilir mi?

Yanıtlar:


140

Diğer OOP dilleri, bir üst düzey sınıfa bağlanmadan somutlaştırılamayan iç sınıflara sahiptir. Örneğin, Java’da,

class Car {
    class Wheel { }
}

yalnızca Carsınıftaki yöntemler Wheels oluşturabilir .

Ruby bu davranışa sahip değil.

Ruby'de,

class Car
  class Wheel
  end
end

farklı

class Car
end

class Wheel
end

yalnızca sınıf adına Wheelvs. Car::Wheel. İsimdeki bu fark, programcılara, Car::Wheelsınıfın genel bir tekerleğin aksine sadece bir araba tekerleğini temsil edebileceğini açıkça belirtebilir. Sınıf tanımlarını Ruby'de yerleştirmek bir tercih meselesidir, ancak iki sınıf arasında bir sözleşmeyi daha güçlü bir şekilde uyguladığı ve bu şekilde onlar ve kullanımları hakkında daha fazla bilgi ilettiği anlamında bir amaca hizmet eder.

Ancak Ruby tercümanı için bu sadece isimde bir fark.

İkinci gözleminize gelince, modüllerin içine yerleştirilmiş sınıflar genellikle sınıfları adlandırmak için kullanılır. Örneğin:

module ActiveRecord
  class Base
  end
end

farklı

module ActionMailer
  class Base
  end
end

Modüllerin içine yerleştirilmiş sınıfların tek kullanımı olmasa da, genellikle en yaygın olanıdır.


5
@rubyprince, Car.newve arasında bir ilişki kurarak ne demek istediğinizden emin değilim Car::Wheel.new. Ruby'de Carbir Car::Wheelnesneyi başlatmak için kesinlikle bir nesne başlatmanız gerekmez , ancak kullanılabilir Carolması için sınıfın yüklenmesi ve yürütülmesi gerekir Car::Wheel.
Pan Thomakos

30
@Pan, Java iç sınıflarını ve ad boşluklu Ruby sınıflarını karıştırıyorsunuz. Bir statik olmayan Java iç içe sınıf bir denir iç sınıf ve sadece dış sınıfının bir örneğini içinde bulunmaktadır. Dış referanslara izin veren gizli bir alan var. Ruby iç sınıfı basitçe adlandırılır ve çevreleyen sınıfa hiçbir şekilde "bağlı" değildir. Java statik (iç içe) sınıfına eşdeğerdir . Evet, cevabın çok oyu var ama tam olarak doğru değil.
DigitalRoss

7
Bu cevabın OP tarafından nasıl kabul edileceği konusunda nasıl 60 oy aldığını bilmiyorum. Burada tam anlamıyla tek bir doğru ifade yok. Ruby'nin Beta veya Newspeak gibi iç içe sınıfları yoktur. Arasında kesinlikle hiçbir ilişki olursa olsun yoktur Carve Car::Wheel. Modüller (ve dolayısıyla sınıflar) sadece sabitler için ad alanlarıdır, Ruby'de iç içe sınıf veya iç içe modül diye bir şey yoktur.
Jörg W Mittag

4
İkisi arasındaki tek fark sabit çözünürlüktür (bu sözcüksel ve dolayısıyla açıkça farklıdır çünkü iki snippet sözcüksel olarak farklıdır). Bununla birlikte, söz konusu sınıflarla ilgili ikisi arasında kesinlikle hiçbir fark yoktur. Sadece tamamen ilgisiz iki sınıf vardır. Dönemi. Ruby'nin iç / iç sınıfları yoktur. İç içe sınıfların Kişisel tanımı doğrudur, ancak trivially test edebilirsiniz olarak basitçe, Ruby için geçerli değildir: Car::Wheel.new. Boom. Bir Wheelnesnenin içinde yuvalanmayan bir nesne inşa ettim Car.
Jörg W Mittag

10
Bu gönderi, yorum dizisinin tamamını okumadan oldukça yanıltıcıdır.
Nathan

50

Ruby'de, iç içe bir sınıf tanımlamak, bir modülde bir sınıf tanımlamaya benzer. Aslında sınıflar arasındaki bir ilişkiyi zorlamaz, sadece sabitler için bir ad alanı oluşturur. (Sınıf ve Modül adları sabittir.)

Kabul edilen cevap hiçbir şey için doğru değildi. 1 Aşağıdaki örnekte, çevrelenen sınıfın bir örneği olmadan, sözcük olarak eklenmiş bir sınıf örneği oluşturuyorum.

class A; class B; end; end
A::B.new

Avantajlar modüller için olanlarla aynıdır: kapsülleme, sadece bir yerde kullanılan gruplama kodu ve kodu kullanıldığı yere daha yakın yerleştirme. Büyük bir projede, her kaynak dosyada tekrar tekrar ortaya çıkan ve çok sayıda sınıf tanımı içeren bir dış modül olabilir. Çeşitli çerçeveler ve kütüphane kodlarının hepsi bunu yaptığında, her biri en üst seviyeye sadece bir isim ekleyerek çatışma olasılığını azaltır. Prosaic, emin olmak için, ama bu yüzden kullanılıyorlar.

Dış ad alanını tanımlamak için modül yerine sınıf kullanmak tek dosyalı bir programda veya komut dosyasında veya bir şey için zaten üst düzey sınıf kullanıyorsanız veya sınıfları birbirine bağlamak için kod ekleyecekseniz mantıklı olabilir. gerçek iç sınıf tarzında. Ruby'nin iç sınıfları yoktur, ancak hiçbir şey koddaki aynı davranış hakkında yaratmanızı engellemez. Dış nesnelerin iç nesnelerden referans alması için yine de dış nesnenin örneğinden noktalama yapılması gerekecektir, ancak sınıfları iç içe yerleştirmek, bunun yapabileceğiniz şey olduğunu düşündürecektir. Dikkatle modüler hale getirilmiş bir program her zaman ilk olarak ek sınıfları yaratabilir ve makul şekilde iç içe veya iç sınıflarla ayrıştırılabilir. Sen diyemezsin newbir modül üzerinde.

Genel kalıbı, ad alanının çok fazla ihtiyaç duyulmadığı komut dosyaları için bile, sadece eğlence ve pratik için kullanabilirsiniz ...

#!/usr/bin/env ruby

class A
  class Realwork_A
    ...
  end
  class Realwork_B
    ...
  end

  def run
    ...
  end

  self
end.new.run

15
Lütfen, lütfen, buna iç sınıf demeyin. Değil. Sınıf Bolduğunu değil içeride sınıf A. Sabit B iç sınıfı alanlı olup A, ancak tarafından başvurulan nesne arasındaki ilişki kesinlikle yoktur B(bu durumda sadece bir sınıf olması umulur) ve tarafından başvurulan sınıfa A.
Jörg W Mittag

2
Tamam, "iç" terminoloji kaldırıldı. İyi bir nokta. Yukarıdaki argümanı takip etmeyenler için, anlaşmazlığın nedeni, örneğin Java'da, iç sınıfın nesnelerinde (ve burada kanonik olarak kullanıyorum) bir şey yaptığınızda, dış sınıf ve dış örnek değişkenlerine iç sınıf yöntemleriyle başvurulabilir. Bunları kodla bağlamadığınız sürece hiçbiri Ruby'de olmaz. Ve bilirsiniz, eğer bu kod, ahem, çevreleyen sınıfta mevcutsa, o zaman Bar'a makul bir iç sınıf
DigitalRoss

1
Bana göre bir modül ve bir sınıf arasında karar verirken en çok yardımcı olan bu ifade: You can't call new on a module.- yani temel olarak sadece bazı sınıfları adlandırmak ve aslında dış "sınıf" bir örneğini oluşturmak gerekmiyorsa , o zaman Bir dış modül kullanırdım. Ama eğer sarma / dış "sınıf" bir örneğini başlatmak istiyorsanız o zaman bir Modül yerine bir Sınıf yapmak istiyorum. En azından bu bana mantıklı geliyor.
FireDragon

@FireDragon Ya da başka bir kullanım durumu, alt sınıfların miras aldığı Fabrika olan bir sınıf istemeniz olabilir ve alt sınıf örnekleri oluşturmaktan fabrikanız sorumludur. Bu durumda, Fabrikanız bir modül olamaz, çünkü bir modülden miras alamazsınız, bu yüzden örneklemediğiniz bir üst sınıftır (ve isterseniz bir tür modül gibi 'isim alanı' olabilir)
rmcsharry

15

Muhtemelen bunu sınıflarınızı bir modülde gruplamak için kullanmak istersiniz . Bir isim-uzay şey.

örneğin Twitter mücevherleri bunu başarmak için ad alanlarını kullanır:

Twitter::Client.new

Twitter::Search.new

Her iki yüzden Clientve Searchsınıfların altında yaşarlar Twittermodülü.

Kaynakları kontrol etmek istiyorsanız, her iki sınıfın kodunu burada ve burada bulabilirsiniz .

Bu yardımcı olur umarım!


3
Twitter mücevher güncellenmiş bağlantı: github.com/sferik/twitter/tree/master/lib/twitter
kode

6

Ruby'de yuvalanmış sınıflar ve yuvalanmış modüller arasında 2.5'ten önce başka bir fark daha var. Bu arama sürecidir.

Kısacası: 2.5'ten önce Ruby'de üst düzey sabit arama nedeniyle, iç Objectiçe sınıflar kullanırsanız , Ruby iç içe sınıfınızı yanlış yerde ( özellikle) arayabilir.

Ruby önce 2.5:
İç İçe sınıfsal yapısı: Bir sınıfı olduğunu varsayalım Xiç içe sınıf ile, Yya X::Y. Ve sonra aynı zamanda bir üst sınıf dersiniz var Y. Eğer X::Yyüklü değil, aşağıdaki Aradığınızda olur X::Y:

bulunamadı olması Yhalinde X, Ruby atalarının onu aramak için çalışacağız X. Dan beriXbir modül değil, bir sınıftır, aralarında ataları vardır [Object, Kernel, BasicObject]. Yani, aramaya çalışır Yin Objectbaşarıyla onu bulur.

Yine de üst düzeydir Y, değil X::Y. Bu uyarıyı alacaksınız:

warning: toplevel constant Y referenced by X::Y


İç içe modül yapısı: Önceki örnekte Xbir sınıf değil, bir modül olduğunu varsayalım .

Bir modülün yalnızca atası vardır: X.ancestorsüretecekti [X].

Bu durumda, Ruby a'nın Yatalarından birini arayamaz Xve atar NameError. Raylar (veya otomatik yüklemeli başka bir çerçeve) bundan X::Ysonra yüklenmeye çalışacaktır .

Daha fazla bilgi için bu makaleye bakın https://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/

yılında Ruby 2.5:
Üst seviye sabit arama kaldırıldı.
Bu hatayla karşılaşma korkusu olmadan iç içe dersler kullanabilirsiniz.


3

Önceki cevaplara ek olarak: Ruby'deki Modül bir sınıftır

$ irb
> module Some end
=> nil
> Some.class
=> Module
> Module.superclass
=> Object

11
'Ruby'deki sınıf bir modüldür' demek daha doğru olur!
Tim Diggins

2
Ruby'deki her şey bir nesne olabilir, ancak bir modülü çağırmak bir sınıf doğru görünmüyor: irb (main): 005: 0> Class.ancestors.reverse => [BasicObject, Çekirdek, Nesne, Modül, Sınıf]
Chad M

ve burada "is" tanımına geliyoruz
ahnbizcad

Modüllerin somutlaştırılamayacağını unutmayın. Yani, bir modülden nesne oluşturmak mümkün değildir. Yani modüllerin, sınıfların aksine, bir yöntemi yoktur new. Modüllerin bir sınıf (küçük harf) olduğunu söyleyebilmenize rağmen, bunlar Sınıf (büyük harf) ile aynı şey değildir :)
rmcsharry
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.