OO Design in Rails : 물건을 넣을 곳


244

나는 Rails를 정말로 즐기고 있으며 (일반적으로 RESTless 임에도 불구하고) Ruby가 OO 인 것을 좋아합니다. 그럼에도 불구하고 거대한 ActiveRecord 서브 클래스와 거대한 컨트롤러를 만드는 경향은 아주 당연합니다 (리소스별로 컨트롤러를 사용하더라도). 더 깊은 객체 세계를 만들려면 클래스 (및 모듈)를 어디에 두겠습니까? 뷰 (헬퍼 자체?), 컨트롤러 및 모델에 대해 묻고 있습니다.

Lib은 괜찮 습니다. 개발 환경에서 다시로드 할 수있는 솔루션을 찾았 지만이 작업을 수행하는 더 좋은 방법이 있는지 알고 싶습니다. 나는 수업이 너무 커지는 것에 대해 정말로 걱정하고 있습니다. 또한 엔진은 어떻습니까?

답변:


384

Rails는 MVC 측면에서 구조를 제공하기 때문에 사용자에게 제공 되는 모델, 뷰 및 컨트롤러 컨테이너 사용하는 것이 당연 합니다. 초보자 (및 일부 중간 프로그래머)의 일반적인 관용구는 앱의 모든 논리를 모델 (데이터베이스 클래스), 컨트롤러 또는 뷰에 넣는 것입니다.

어느 시점에서 누군가 "지방 모델, 스키니 컨트롤러"패러다임을 지적하고, 중간 개발자는 서둘러 컨트롤러에서 모든 것을 소비하고이를 모델로 던져 애플리케이션 로직의 새로운 쓰레기통이되기 시작합니다.

실제로 스키니 컨트롤러는 좋은 생각이지만 모델에 모든 것을 넣는 추론은 실제로 최고의 계획은 아닙니다.

Ruby에는 모듈화를위한 몇 가지 좋은 옵션이 있습니다. 상당히 인기있는 대답은 lib메소드 그룹을 보유한 모듈 (보통 스태킹 )을 사용한 다음 해당 클래스에 모듈을 포함시키는 것입니다. 여러 클래스에서 재사용하려는 기능 범주가 있지만 기능이 클래스에 개념적으로 첨부되어있는 경우에 유용합니다.

모듈을 클래스에 포함 시키면 메소드는 클래스의 인스턴스 메소드가되므로 톤을 포함하는 클래스로 끝납니다. 방법을, 그들은 단지 여러 파일에 잘 정리하고 있습니다.

이 솔루션은 어떤 경우에는 잘 작동 할 수 있습니다. 다른 경우 에는 모델, 뷰 또는 컨트롤러 가 아닌 코드에서 클래스를 사용하는 것이 좋습니다.

그것에 대해 생각하는 좋은 방법은 "단일 책임 원칙"인데, 이는 클래스가 단일 (또는 소수) 사물을 책임 져야한다고 말합니다. 모델은 애플리케이션에서 데이터베이스로 데이터를 유지해야합니다. 컨트롤러는 요청을 받고 실행 가능한 응답을 반환 할 책임이 있습니다.

해당 상자에 잘 맞지 않는 개념 (지속성, 요청 / 응답 관리)이있는 경우 해당 아이디어를 어떻게 모델링 할 것인지 생각할 수 있습니다 . 비 모델링 클래스를 앱 / 클래스 또는 다른 곳에 저장하고 다음을 수행하여 해당 디렉토리를로드 경로에 추가 할 수 있습니다.

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

승객 또는 JRuby를 사용하는 경우 열망하는로드 경로에 경로를 추가 할 수도 있습니다.

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

결론은 일단 Rails에서이 질문을하는 지점에 도달하면 Ruby chops을 강화하고 Rails가 기본적으로 제공하는 MVC 클래스가 아닌 모델링 클래스를 시작할 차례입니다.

업데이트 : 이 답변은 Rails 2.x 이상에 적용됩니다.


도 비 모델에 별도의 디렉토리를 추가하지 않았습니다. 난 깔끔한 느낌을 느낄 수 있습니다 ...
Mike Woodhouse

예후 다, 고마워 좋은 대답입니다. 그것은 내가 상속받는 앱 (그리고 내가 만든 것들)에서 정확히 보는 것입니다 : 컨트롤러, 모델, 뷰 및 컨트롤러 및 뷰에 자동으로 제공되는 도우미의 모든 것. 그런 다음 lib에서 믹스 인을 가져 오지만 실제 OO 모델링을 시도하지는 않습니다. 하지만 "앱 / 클래스 또는 다른 곳"에서 옳습니다. 그냥 ... 내가 부족 일부 표준 대답이 있는지 확인하고 싶어서
댄 Rosenstark는

33
최신 버전에서 config.autoload_paths는 기본적으로 app 아래의 모든 디렉토리로 설정됩니다. 따라서 위에서 설명한대로 config.load_paths를 변경할 필요가 없습니다. 그래도 eager_load_paths (아직)에 대해 확실하지 않으므로 조사해야합니다. 아무도 이미 알고 있습니까?
Shyam Habarakada

수동 공격적 중간체를 향한 : P
세바스찬 패튼

8
Rails가이 "클래스"폴더와 함께 제공되어 "단일 책임 원칙"을 장려하고 개발자가 데이터베이스 백업되지 않은 객체를 만들 수있게하는 것이 좋을 것입니다. Rails 4의 "Concerns"구현 (Simone의 답변 참조)은 모델간에 로직을 공유하기 위해 모듈을 구현하는 것으로 보였습니다. 그러나 데이터베이스 지원되지 않는 일반 Ruby 클래스에 대해서는 그러한 도구가 작성되지 않았습니다. Rails는 매우 의견이 많으므로 이와 같은 폴더를 포함하지 않는 배후의 사고 과정이 궁금하십니까?
Ryan Francis

62

업데이트 : Rails 4에서 관심사 사용이 새로운 기본값 으로 확인 되었습니다 .

실제로 모듈 자체의 특성에 따라 다릅니다. 나는 보통 컨트롤러 / 모델 확장자를 앱 내의 / concerns 폴더에 넣습니다.

# 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는 범용 라이브러리에서 선호하는 선택입니다. 나는 항상 모든 응용 프로그램 특정 라이브러리를 넣는 lib에 프로젝트 네임 스페이스가 있습니다.

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

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

Ruby / Rails 코어 확장은 일반적으로 구성 초기화 프로그램에서 발생하므로 라이브러리는 Rails 부 스트랩에 한 번만로드됩니다.

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

재사용 가능한 코드 조각의 경우 다른 프로젝트에서 재사용 할 수 있도록 종종 (마이크로) 플러그인을 만듭니다.

헬퍼 파일은 일반적으로 헬퍼 메소드 (예 : 폼 빌더)와 같이 헬퍼가 오브젝트를 사용할 때 클래스를 보유합니다.

이것은 실제로 일반적인 개요입니다. 보다 맞춤화 된 제안을 받으려면 특정 예에 대한 자세한 정보를 제공하십시오. :)


기괴한 것. lib 디렉토리에서 무언가를 처리하기 위해 require_dependency RAILS_ROOT + "/ lib / my_module"을 얻을 수 없습니다. 파일을 찾지 못하면 확실히 실행하고 불평하지만 다시로드하지는 않습니다.
Dan Rosenstark

루비의 요구 사항은 한 번만로드됩니다. 무조건 무언가를로드하려면 load를 사용하십시오.
Chuck

또한 앱 인스턴스 수명 동안 파일을 두 번로드하려는 경우는 매우 드 seems니다. 코드를 생성하고 있습니까?
Chuck

require 대신에 require_dependency를 사용하는 이유는 무엇입니까? 또한 명명 규칙을 따르는 경우 require를 전혀 사용할 필요가 없습니다. lib / my_module에서 MyModule을 작성하면 이전 요구없이 MyModule을 호출 할 수 있습니다 (요청을 사용하는 것이 더 빠르며 때로는 더 읽기 쉬운 경우에도). 또한 / lib의 파일은 부트 스트랩에서 한 번만로드됩니다.
Simone Carletti

1
우려의 사용에 관한
bbozo

10

... 거대한 ActiveRecord 서브 클래스와 거대한 컨트롤러를 만드는 경향은 매우 자연 스럽습니다 ...

"거대한"은 걱정스러운 단어입니다 ... ;-)

컨트롤러가 어떻게 커지고 있습니까? 이상적으로 컨트롤러는 얇아 야합니다. 얇은 공기에서 규칙을 선택하면 컨트롤러 방법 (작업) 당 5 ~ 6 줄 이상의 코드를 정기적으로 가지고 있다면 컨트롤러가 너무 뚱뚱 할 것입니다. 헬퍼 기능 또는 필터로 이동할 수있는 중복이 있습니까? 모델에 적용 할 수있는 비즈니스 로직이 있습니까?

모델이 어떻게 커지나요? 각 반의 책임 수를 줄이는 방법을 찾고 있습니까? 믹스 인으로 추출 할 수있는 일반적인 동작이 있습니까? 아니면 도우미 클래스에 위임 할 수있는 기능 영역이 있습니까?

편집 : 비트를 확장하려고하면 너무 심하게 왜곡되지 않는 것이 좋습니다 ...

조력자 : 생활하며 app/helpers주로보기를 더 단순하게 만드는 데 사용됩니다. 컨트롤러별로 다르거 나 (해당 컨트롤러의 모든보기에도 사용 가능) 일반적으로 사용 가능합니다 (module ApplicationHelper application_helper.rb에서).

필터 : 여러 작업에서 동일한 코드 줄이 있다고 가정합니다 (물론 사용 params[:id]하거나 이와 유사한 객체 검색 ). 이러한 중복은 먼저 별도의 메소드로 추상화 한 다음 클래스 정의에서 필터를 선언하여 조치에서 완전히 제거 할 수 있습니다 (예 :) before_filter :get_object. ActionController 레일 가이드의 6 절을 참조하십시오 . 선언적 프로그래밍을 친구로 십시오.

리팩토링 모델은 좀 더 종교적인 것입니다. 예를 들어 Bob 아저씨의 제자들은 SOLID 의 다섯 계명을 따를 것을 제안 합니다. Joel & Jeff 나중에 좀 더 화해 된 것처럼 보이지만 보다 실용적인 접근 방식을 추천 할 수 있습니다 . 클래스에서 속성의 명확하게 정의 된 하위 집합에서 작동하는 하나 이상의 메서드를 찾는 것이 ActiveRecord 파생 모델에서 리팩토링 될 수있는 클래스를 식별하는 방법 중 하나입니다.

그런데 Rails 모델은 ActiveRecord :: Base의 서브 클래스 일 필요는 없습니다. 또는 다른 방식으로 표현하기 위해 모델은 테이블과 유사하거나 전혀 저장된 것과 관련이 없어도됩니다. app/modelsRails의 규칙 에 따라 파일 이름을 지정 하는 한 (레일 이름을 찾기 위해 클래스 이름에서 #underscore를 호출하면) Rails는 require필요 없이 파일을 찾습니다 .


모든면에서, Mike, 걱정 해주셔서 감사합니다 ... 나는 컨트롤러에 거대한 방법이 몇 가지있는 프로젝트를 물려 받았습니다. 나는 이것을 더 작은 방법으로 분류했지만 컨트롤러 자체는 여전히 "뚱뚱하다". 그래서 내가 찾고있는 것은 물건을 오프로드하는 모든 옵션입니다. 귀하의 답변은 "도우미 기능", "필터", "모델", "믹싱"및 "도우미 클래스"입니다. 그럼, 이것들을 어디에 넣을 수 있습니까? 개발 환경에서 자동로드되는 클래스 계층을 구성 할 수 있습니까?
Dan Rosenstark

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.