OO Diseño en Rieles: Dónde poner cosas


Realmente estoy disfrutando de Rails (aunque generalmente estoy INQUieto), y disfruto de Ruby siendo muy OO. Aún así, la tendencia a crear subclases de ActiveRecord enormes y controladores enormes es bastante natural (incluso si usa un controlador por recurso). Si tuvieras que crear mundos de objetos más profundos, ¿dónde pondrías las clases (y módulos, supongo)? Estoy preguntando sobre puntos de vista (en los propios Ayudantes?), controladores y modelos.

Lib está bien, y he encontrado algunas soluciones para conseguirlo recargar en un entorno de desarrollo, pero me gustaría saber si hay una mejor manera de hacer estas cosas. Me preocupa que las clases sean demasiado grandes. Además, ¿qué pasa con los motores y cómo encajan?

Author: Dan Rosenstark, 2009-07-01

4 answers

Debido a que Rails proporciona estructura en términos de MVC, es natural terminar usando solo los contenedores modelo, vista y controlador que se proporcionan para usted. El modismo típico para principiantes (e incluso algunos programadores intermedios) es meter toda la lógica en la aplicación en el modelo (clase de base de datos), controlador o vista.

En algún momento, alguien señala el paradigma del" modelo gordo, controlador delgado", y los desarrolladores intermedios eliminan apresuradamente todo de sus controladores y lanzarlo en el modelo, que comienza a convertirse en una nueva papelera para la lógica de la aplicación.

Los controladores flacos son, de hecho, una buena idea, pero el corolario putting poner todo en el modelo, no es realmente el mejor plan.

En Ruby, tienes un par de buenas opciones para hacer las cosas más modulares. Una respuesta bastante popular es simplemente usar módulos (generalmente guardados en lib) que contienen grupos de métodos, y luego incluir los módulos en las clases apropiadas. Esto ayuda en los casos donde tiene categorías de funcionalidad que desea reutilizar en varias clases, pero donde la funcionalidad todavía está asociada teóricamente a las clases.

Recuerde, cuando se incluye un módulo en una clase, los métodos se convierten en métodos de instancia de la clase, por lo que todavía termina con una clase que contiene un ton de métodos, solo están organizados muy bien en varios archivos.

Esta solución puede funcionar bien en algunos casos in en otros casos, vas a querer pensar acerca del uso de clases en su código que son no modelos, vistas o controladores.

Una buena manera de pensarlo es el "principio de responsabilidad única", que dice que una clase debe ser responsable de una sola (o un pequeño número) de cosas. Sus modelos son responsables de conservar los datos de su aplicación en la base de datos. Sus controladores son responsables de recibir una solicitud y devolver una respuesta viable.

Si tienes conceptos que no encajan perfectamente en esos (persistencia, gestión de solicitudes / respuestas), probablemente quiera pensar cómo modelaría la idea en cuestión. Puede almacenar clases que no son de modelo en app / classes, o en cualquier otro lugar, y agregar ese directorio a su ruta de carga haciendo:

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

Si está utilizando passenger o JRuby, probablemente también desee agregar su ruta a las rutas de carga ansiosas:

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

La conclusión es que una vez que llegas a un punto en Rails donde te encuentras haciendo esta pregunta, es es hora de reforzar tus habilidades Ruby y empezar a modelar clases que no son solo las clases MVC que Rails te da por defecto.

Update: Esta respuesta se aplica a Rails 2.x y superior.

 375
Author: Yehuda Katz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-12 21:16:48

Update : El uso de Preocupaciones se ha confirmado como el nuevo valor predeterminado en Rails 4.

Realmente depende de la naturaleza del módulo en sí. Suelo colocar extensiones de controlador / modelo en una carpeta / concerns dentro de la aplicación.

# 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 es mi opción preferida para bibliotecas de propósito general. Siempre tengo un espacio de nombres de proyecto en lib donde pongo todas las bibliotecas específicas de la aplicación.

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

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

Las extensiones de núcleo de Ruby/Rails generalmente tienen lugar en configuración inicializadores para que las bibliotecas solo se carguen una vez en Rails boostrap.

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

Para fragmentos de código reutilizables, a menudo creo (micro)plugins para que pueda reutilizarlos en otros proyectos.

Los archivos Helper generalmente contienen métodos helper y a veces clases cuando el objeto está destinado a ser utilizado por helpers (por ejemplo, Constructores de formularios).

Esta es una visión general. Proporcione más detalles sobre ejemplos específicos si desea obtener sugerencias más personalizadas. :)

 61
Author: Simone Carletti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-27 11:02:43

... la tendencia a hacer enormes ActiveRecord subclases y enorme controladores es bastante natural ...

"enorme" es una palabra preocupante... ;-)

¿Cómo se están haciendo enormes sus controladores? Eso es algo que deberías mirar: idealmente, los controladores deberían ser delgados. Escogiendo una regla de oro de la nada, sugeriría que si regularmente tiene más de, digamos, 5 o 6 líneas de código por método (acción) del controlador, entonces sus controladores probablemente sean demasiado gordos. Existir duplicación que podría pasar a una función auxiliar o un filtro? ¿Hay una lógica de negocios que pueda ser empujada hacia abajo en los modelos?

¿Cómo tus modelos llegan a ser enormes? ¿Debería buscar maneras de reducir el número de responsabilidades en cada clase? ¿Hay algún comportamiento común que pueda extraer en mixins? ¿O áreas de funcionalidad que puede delegar a clases auxiliares?

EDITAR: Tratando de expandir un poco, con suerte no distorsionar nada demasiado mal...

Ayudantes: en vivo en app/helpers y se utilizan principalmente para simplificar las vistas. Son específicos del controlador (también disponibles para todas las vistas de ese controlador) o generalmente disponibles (module ApplicationHelper en application_helper.po).

Filtros: Digamos que tienes la misma línea de código en varias acciones (muy a menudo, recuperación de un objeto usando params[:id] o similar). Esa duplicación se puede abstraer primero en un método separado y luego fuera de las acciones completamente declarando un filtro en la definición de la clase, como before_filter :get_object. Ver Sección 6 en la Guía de carriles de ActionController Deje que la programación declarativa sea su amiga.

Refactorizar modelos es un poco más de una cosa religiosa. Los discípulos de Tío Bob sugerirán, por ejemplo, que sigas los Cinco Mandamientos de SÓLIDO . Joel y Jeff pueden recomendar un enfoque más, er, "pragmático", aunque parecían ser un poco más reconciliados posteriormente. Encontrar uno o más métodos dentro de una clase que operar en un subconjunto claramente definido de sus atributos es una forma de intentar identificar clases que podrían ser refactorizadas de su modelo derivado de ActiveRecord.

Los modelos Rails no tienen que ser subclases de ActiveRecord::Base, por cierto. O para decirlo de otra manera, un modelo no tiene que ser un análogo de una tabla, o incluso relacionado con nada almacenado en absoluto. Aún mejor, siempre y cuando nombre su archivo en app/models de acuerdo con las convenciones de Rails (llame # underscore en el nombre de la clase para averiguar lo que Rails buscará), Rails lo encontrará sin necesidad de require.

 10
Author: Mike Woodhouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-05 19:11:51

Aquí hay una excelente entrada de blog sobre la refactorización de los modelos de grasa que parecen surgir de la filosofía del "controlador delgado":

Http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

El mensaje básico es "No extraiga Mixins de Modelos Fat", use clases de servicio en su lugar, el autor proporciona 7 patrones para hacerlo

 1
Author: bbozo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-15 07:58:45