Raylarda OO Tasarımı: Nereye yerleştirilir


244

Rails'ten hoşlanıyorum (genellikle RESTless olmama rağmen) ve Ruby'nin çok OO olmasından hoşlanıyorum. Yine de, devasa ActiveRecord alt sınıfları ve büyük denetleyiciler yapma eğilimi oldukça doğaldır (kaynak başına bir denetleyici kullansanız bile). Daha derin nesne dünyaları yaratacak olsaydınız, sınıfları (ve modülleri nereye koyardınız?) (Helpers'ın kendisinde?), Denetleyiciler ve modeller hakkında sorular soruyorum.

Lib iyidir ve geliştirici bir ortamda yeniden yüklenmesini sağlamak için bazı çözümler buldum , ancak bunu yapmanın daha iyi bir yolu olup olmadığını bilmek istiyorum. Gerçekten çok büyük büyüyen sınıflardan endişeliyim. Ayrıca, Peki ya Motorlar ve nasıl oturuyorlar?

Yanıtlar:


384

Raylar MVC açısından yapısını sağladığından, kullanarak sonuna doğaldır sadece sizin için verilmiştir modeli, görünüm ve denetleyici kapları. Yeni başlayanlar (ve hatta bazı ara programcılar) için tipik deyim, uygulamadaki tüm mantığı modele (veritabanı sınıfı), denetleyiciye veya görünüme sıkıştırmaktır.

Bir noktada, birisi "yağ modeli, sıska denetleyici" paradigmasına dikkat çekiyor ve ara geliştiriciler aceleyle her şeyi kontrolörlerinden çıkarıyor ve uygulama mantığı için yeni bir çöp kutusu olmaya başlayan modele atıyor.

Sıska kontrolörler aslında iyi bir fikir, ancak sonuç - her şeyi modele koymak, gerçekten en iyi plan değil.

Ruby'de, işleri daha modüler hale getirmek için birkaç iyi seçeneğiniz var. Oldukça popüler bir cevap, sadece libyöntem gruplarını tutan ve daha sonra modülleri uygun sınıflara dahil eden modülleri (genellikle saklanır ) kullanmaktır . Bu, birden fazla sınıfta yeniden kullanmak istediğiniz işlevsellik kategorilerine sahip olduğunuz, ancak işlevselliğin yine de sınıflara bağlı olduğu durumlarda yardımcı olur.

Bir sınıfa bir modül eklediğinizde, yöntemler sınıfın örnek yöntemleri haline gelir, bu yüzden yine de tonlarca yöntem içeren bir sınıfla sonuçlanırsınız, bunlar çok sayıda dosyada güzelce düzenlenmiştir.

Bu çözüm bazı durumlarda iyi çalışabilir - diğer durumlarda, kodunuzda model, görünüm veya denetleyici olmayan sınıfları kullanmayı düşünmek isteyeceksiniz .

Bunu düşünmenin iyi bir yolu, bir sınıfın tek (veya az sayıda) şeyden sorumlu olması gerektiğini söyleyen "tek sorumluluk ilkesi" dir. Modelleriniz, uygulamanızdan veritabanına kadar olan verilerin kalıcı olmasından sorumludur. Denetleyicileriniz bir istek almaktan ve uygun bir yanıt döndürmekten sorumludur.

Bu kutulara tam olarak uymayan kavramlarınız varsa (kalıcılık, istek / yanıt yönetimi), muhtemelen söz konusu fikri nasıl modelleyeceğinizi düşünmek istersiniz . Model dışı sınıfları uygulama / sınıflarda veya başka herhangi bir yerde saklayabilir ve şunları yaparak bu dizini yükleme yolunuza ekleyebilirsiniz:

config.load_paths << File.join(Rails.root, "app", "classes")

Yolcu veya JRuby kullanıyorsanız, muhtemelen istekli yükleme yollarına da yol eklemek istersiniz:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

Sonuç olarak, Rails'te bu soruyu sorduğunuz bir noktaya geldiğinizde, Ruby pirzolalarınızı güçlendirmenin ve sadece Rails'in varsayılan olarak verdiği MVC sınıfları olmayan sınıfları modellemeye başlamanın zamanı geldi.

Güncelleme: Bu cevap Rails 2.x ve üstü için geçerlidir.


D'oh. Model olmayanlar için ayrı bir dizin eklemek bana hiç gelmemişti. Kendimi düzenli hissediyorum ...
Mike Woodhouse

Yehuda, bunun için teşekkürler. Mükemmel cevap. Devraldığım (ve yaptığım) uygulamalarda gördüğüm şey tam olarak bu: denetleyiciler, modeller, görünümler ve denetleyiciler ve görünümler için otomatik olarak sağlanan yardımcılar. O zaman lib'den mixins gel, ama asla gerçek OO modelleme yapmaya teşebbüs edilmez. Yine de haklısınız: "uygulamalar / sınıflar veya başka herhangi bir yerde". Sadece eksik olduğum bazı standart cevap olup olmadığını kontrol etmek istedim ...
Dan Rosenstark

33
Daha yeni sürümlerde, config.autoload_paths varsayılan olarak uygulama altındaki tüm dizinleri kullanır. Dolayısıyla config.load_paths dosyasını yukarıda açıklandığı gibi değiştirmeniz gerekmez. Yine de eager_load_paths (henüz) hakkında emin değilim ve içine bakmak gerekir. Zaten kimse biliyor mu?
Shyam Habarakada

Ara ürünlere karşı pasif agresif: P
Sebastian Patten

8
Rails, "tek sorumluluk ilkesini" teşvik etmek ve geliştiricilerin veritabanı destekli olmayan nesneler oluşturmalarını sağlamak için bu "sınıflar" klasörü ile birlikte gönderilirse iyi olur. Rails 4'teki "Endişeler" uygulaması (Simone'un cevabına bakınız) mantığı modeller arasında paylaşmak için modüllerin uygulanmasına özen göstermiş görünmektedir. Ancak, veritabanı destekli olmayan düz Ruby sınıfları için böyle bir araç oluşturulmamıştır. Rails çok görüşlü olduğu göz önüne alındığında, böyle bir klasör dahil DEĞİL arkasındaki düşünce sürecini merak ediyorum?
Ryan Francis

62

Güncelleme : Rails 4'te Endişelerin kullanımı yeni varsayılan olarak onaylandı .

Gerçekten modülün doğasına bağlıdır. Genellikle denetleyici / model uzantılarını uygulama içindeki bir / endişeler klasörüne yerleştiririm.

# concerns/authentication.rb
module Authentication
  ...
end    

# controllers/application_controller.rb
class ApplicationController
  include Authentication
end



# concerns/configurable.rb
module Configurable
  ...
end    

class Model 
  include Indexable
end 

# controllers/foo_controller.rb
class FooController < ApplicationController
  include Indexable
end

# controllers/bar_controller.rb
class BarController < ApplicationController
  include Indexable
end

/ lib genel amaçlı kütüphaneler için tercih ettiğim seçenektir. Her zaman lib'de bir proje ad alanım vardır, burada uygulamaya özel tüm kütüphaneleri koyarım.

/lib/myapp.rb
module MyApp
  VERSION = ...
end

/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb

Ruby / Rails çekirdek uzantıları genellikle yapılandırma başlatıcılarında yer alır, böylece kütüphaneler Rails boostrap'a yalnızca bir kez yüklenir.

/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb

Yeniden kullanılabilir kod parçaları için, genellikle (mikro) eklentiler oluştururum, böylece bunları diğer projelerde tekrar kullanabilirim.

Yardımcı dosyalar genellikle yardımcı yöntemler içerir ve bazen nesnenin yardımcılar tarafından kullanılması amaçlandığında sınıflar (örneğin, Form Oluşturucular).

Bu gerçekten genel bir bakış. Daha özelleştirilmiş öneriler almak istiyorsanız lütfen belirli örnekler hakkında daha fazla ayrıntı sağlayın. :)


Tuhaf bir şey. Bu dizini dışında bir şey ile çalışmak için bu requir_dependency RAILS_ROOT + "/ lib / my_module" alınamıyor. Dosya bulunmazsa kesinlikle yürütür ve şikayet eder, ancak dosyayı yeniden yüklemez.
Dan Rosenstark

Ruby'nin sadece bir kez bir şeyler yüklemesi gerekir. Koşulsuz olarak bir şey yüklemek istiyorsanız load kullanın.
Chuck

Ayrıca, bir uygulama örneğinin ömrü boyunca iki kez bir dosya yüklemek isteyebileceğiniz oldukça sıra dışı görünüyor. Gittikçe kod mu üretiyorsunuz?
Chuck

Neden requir_ yerine requir_dependency kullanıyorsunuz? Ayrıca, adlandırma kurallarını izlerseniz kullanmanız gerekmediğini unutmayın. Lib / my_module içinde MyModule oluşturursanız, MyModule'u önceden bir gereksinim olmadan çağırabilirsiniz (gereksinim kullanmak daha hızlı ve bazen daha okunabilir olsa bile). Ayrıca, / lib içindeki dosyanın bootstrap'e yalnızca bir kez yüklendiğini unutmayın.
Simone Carletti

1
Endişelerin kullanımı ile ilgili
bbozo 15:15

10

... devasa ActiveRecord alt sınıfları ve devasa denetleyiciler yapma eğilimi oldukça doğal ...

"kocaman" endişe verici bir kelimedir ... ;-)

Denetleyicileriniz nasıl büyük hale geliyor? Bu bakmanız gereken bir şey: ideal olarak kontrolörler ince olmalı. İnce havadan kuralsız bir kural seçerek, denetleyici yöntemi (eylem) başına düzenli olarak 5 veya 6 satırdan fazla kodunuz varsa, denetleyicilerinizin muhtemelen çok şişman olduğunu öneririm. Yardımcı bir işleve veya filtreye taşınabilecek çoğaltma var mı? Modellerin içine itilebilecek bir iş mantığı var mı?

Modelleriniz nasıl büyük olur? Her bir sınıftaki sorumluluk sayısını azaltmanın yollarını mı arıyorsunuz? Mixins'e çıkarabileceğiniz yaygın davranışlar var mı? Veya yardımcı sınıflara devredebileceğiniz işlev alanları?

EDIT: Biraz genişletmeye çalışıyorum, umarım çok kötü bir şey bozmaz ...

Yardımcıları: Yaşamak app/helpersve çoğunlukla görüntülemeyi kolaylaştırmak için kullanılır. Denetleyiciye özgü (bu denetleyicinin tüm görünümleri için de kullanılabilir) veya genel olarak ( module ApplicationHelperapplication_helper.rb) kullanılabilir.

Filtreler: Birkaç eylemde aynı kod satırına sahip olduğunuzu varsayalım (oldukça sık, bir nesneyi params[:id]veya benzeri kullanarak geri çağırma ). Bu çoğaltma önce ayrı bir yönteme, sonra da tamamen sınıf tanımında bir filtre bildirerek eylemlerin dışına çıkarılabilir before_filter :get_object. ActionController Rails Kılavuzundaki Bölüm 6'ya bakın . Bildirimli programlamanın arkadaşınız olmasına izin verin.

Modelleri yeniden düzenlemek biraz daha dini bir şey. Müritleri Amca Bob sen Beş Emir izlemeniz örneğin önereceğim KATI . Joel & Jeff , daha sonra uzlaşma gibi görünseler de daha "pragmatik" bir yaklaşım önerebilir . Bir sınıf içinde, özniteliklerinin açıkça tanımlanmış bir alt kümesinde çalışan bir veya daha fazla yöntem bulmak, ActiveRecord türetilmiş modelinizden yeniden düzenlenebilecek sınıfları tanımlamayı denemenin bir yoludur.

Rails modellerinin bu arada ActiveRecord :: Base alt sınıfları olması gerekmez. Ya da başka bir deyişle, bir modelin bir tablonun analogu olması, hatta depolanan herhangi bir şeyle ilgili olması gerekmez. Daha da iyisi, dosyanızı app/modelsRails kurallarına göre adlandırdığınız sürece (Rails'in neyi arayacağını bulmak için sınıf adına #underscore çağrısı yapın), Rails herhangi bir requiregereksinim duymadan dosyayı bulur .


Her şeyden ötürü, Mike ve endişeniz için teşekkürler ... Denetleyicilerin üzerinde çok büyük yöntemlerin olduğu bir projeyi miras aldım. Bunları daha küçük yöntemlere ayırdım ama kontrolörün kendisi hala "şişman". Yani aradığım şeyleri boşaltmak için tüm seçeneklerim. Cevaplarınız "yardımcı fonksiyonlar", "filtreler", "modeller," "mixins" ve "yardımcı sınıflar" dır. Öyleyse, bunları nereye koyabilirim? Bir dev env'de otomatik olarak yüklenen bir sınıf hiyerarşisini düzenleyebilir miyim?
Dan Rosenstark

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.