DEV Community

Daniel
Daniel

Posted on • Edited on

Repartiendo Responsabilidades En Rails Con composed_of

El día de hoy me encuentro estudiando más acerca de técnicas para organizar código de una manera más adecuada con las bondades que brinda Ruby On Rails.
Por lo tanto, para dar inicio solo basta un simple, problema con su solución donde tenemos un modelo Student el cual estará compuesto de las siguientes columnas:

  • name
  • qualifications

Empezaremos generando nuestra migración con el siguiente comando:

rails g migration CreateStudent
Enter fullscreen mode Exit fullscreen mode

Esta debera de lucir como se muestra enseguida:

class CreateStudent < ActiveRecord::Migration[7.0]
  def change
    create_table :students do |t|
      t.string  :name
      t.json :qualifications

      t.timestamps
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Con lo cual podremos generar objetos del tipo Student de la siguiente manera:

Student.create!(name: 'Daniel', qualifications: { subject_one: 10, subject_two: 9, subject_three: 8, subject_four: 10 })
Enter fullscreen mode Exit fullscreen mode

Enseguida se nos pide que calculemos el promedio de las calificaciones y lo primero que hariamos es generar los modelos en nuestro modelo Student como se muestra enseguida:

class Student < ApplicationRecord
  validates :name, presence: true
  validates :qualifications, presence: true

  def average_calculation
    sum_qualificiations / qualifications.size
  end

  private

  def sum_qualificiations
    qualifications.values.inject(0, :+)
  end
end
Enter fullscreen mode Exit fullscreen mode

Con esto podremos obtener el promedio de un estudiante únicamente llamando al método average_calculation es decir:

student = Student.first
student.average_calculation #=> 9
Enter fullscreen mode Exit fullscreen mode

En este caso tenemos la clase Student y está debe ser capaz de únicamente tratar con temas relacionados con el estudiante, su nombre, nombre de tutor, teléfono de emergencias, etcétera.Si bien hasta aquí podemos indicar que hemos hecho lo solicitado, estamos olvidando una regla muy importante en la POO que es la responsabilidad única en clases.

El problema

Este radica en que él hacer el calculo de calificaciones no es algo que una clase como Student debería de hacer y está logica la podemos pasar a un objeto compuesto que representara las calificaciones.

Todo esto lo podemos hacer gracias al método  composed_of que brinda Ruby On Rails el cual lo que hace es delegar la responsabilidad a una clase externa.

Lo anterior es llamado composition el cual podremos definirla como una relación entre dos o más clases donde podemos adquirir un comportamiento y utilizar el código que está en otra clase en nuestro objeto actual.

La solucion

Lo que tendremos que hacer son dos cosas sencillas:

  • Actualizar nuestro modelo Student
  • Generar una nueva clase.

Nuestro modelo Student se actualizara de la siguiente manera:

class Student < ApplicationRecord
  validates :name, presence: true
  validates :qualifications, presence: true

  composed_of :calculations,
              class_name: 'QualificationCalculation',
              mapping: [%w(qualifications qualifications)]
Enter fullscreen mode Exit fullscreen mode

La sintaxis de composed_of cuenta con tres opciones, primero el nombre del método al cual haremos referencia (calculations), el nombre de su clase (la clase que crearemos) y el mapeo de la columna qualifications al atributo qualifications en la nueva clase.

Dicho esto procederemos a crear nuestra nueva clase (qualification_calculation.rb) justo en el directorio de modelos.

class QualificationCalculation
  attr_accessor :qualifications

  def initialize(qualifications)
    @qualifications = qualifications
  end

  def average_calculation
    sum_qualifications / qualifications.size
  end

  private

  def sum_qualifications
    qualifications.values.inject(0, :+)
  end
end
Enter fullscreen mode Exit fullscreen mode

Hecho esto podemos calcular el promedio de calificación de nuestro estudiante de la siguiente manera:

student = Student.first
student.calculations.average_calculation #=> 9
Enter fullscreen mode Exit fullscreen mode

De esta manera nuestro modelo original no cuenta con la responsabilidad de calcular el promedio y además si ocupamos otro calculo lo podemos agregar directamente en nuestra nueva clase QualificationCalculation.

Top comments (0)