Quantcast
Channel: Active questions tagged ruby - Stack Overflow
Viewing all articles
Browse latest Browse all 4633

Should I use Class Methods or Instance Methods?

$
0
0

I am trying to understand what is the best approach for this calculator service. It will be called when there is a request coming in via API and return some data arranged in a table.

Below you will find the instance methods approach and the class methods approach.

From what I found online, it seems that using instance classes is wrong because this is not related to a specific instance, therefore this should be handled with class methods.

The instance methods way:

class DamageCalculatorService  include TimeFilterable  TOTAL_DAMAGE_DEALT_COLUMN = 'total_damage_dealt'.freeze  TOTAL_DAMAGE_TAKEN_COLUMN = 'total_damage_taken'.freeze  def initialize(time_filter:, timezone:, limit:, formatted_table:)    @time_filter = time_filter    @timezone = timezone    @limit = limit    @formatted_table = formatted_table  end  def damage_dealt    @sort_by = TOTAL_DAMAGE_DEALT_COLUMN    calculate_damage  end  def damage_taken    @sort_by = TOTAL_DAMAGE_TAKEN_COLUMN    calculate_damage  end  private  def calculate_damage    start_time = TimeFilterable.start_time_for(time_filter: @time_filter, timezone: @timezone)    return [] unless start_time.present?    begin      query = Stat.where('stats.created_at >= ?', start_time)      results = query.joins(:player)        .select('players.name as player_name','players.steam_id as steam_id','SUM(stats.damage_dealt) as total_damage_dealt','SUM(stats.damage_taken) as total_damage_taken'        )        .group('players.id', 'players.name', 'players.steam_id')        .order("#{@sort_by} DESC")        .limit(@limit)      final_results = results.map do |result|        {          steam_id: result.steam_id,          player_name: result.player_name,          total_damage_dealt: result.total_damage_dealt.to_i,          total_damage_taken: result.total_damage_taken.to_i        }      end      return @formatted_table ? to_table(data: final_results) : results    rescue ActiveRecord::StatementInvalid => e      Rails.logger.error("Error in DamageCalculatorService: #{e.message}")      []    end  end  def to_table(data:)    title = "#{@sort_by.titleize.split('').join('')} for the #{@time_filter}"    headers = %w[player_name total_damage_dealt total_damage_taken]    TabletizeService.new(title: title, data: data, headers: headers).table  endend

The class methods way:

class DamageCalculatorService  include TimeFilterable  TOTAL_DAMAGE_DEALT_COLUMN = 'total_damage_dealt'.freeze  TOTAL_DAMAGE_TAKEN_COLUMN = 'total_damage_taken'.freeze  def self.damage_dealt(time_filter:, timezone:, limit:, formatted_table:, sort_by: TOTAL_DAMAGE_DEALT_COLUMN)    calculate_damage(time_filter: time_filter, timezone: timezone, limit: limit, formatted_table: formatted_table, sort_by: sort_by)  end  def self.damage_taken(time_filter:, timezone:, limit:, formatted_table:, sort_by: TOTAL_DAMAGE_TAKEN_COLUMN)    calculate_damage(time_filter: time_filter, timezone: timezone, limit: limit, formatted_table: formatted_table, sort_by: sort_by)  end  private  def self.calculate_damage(time_filter:, timezone:, limit:, formatted_table:, sort_by:)    start_time = TimeFilterable.start_time_for(time_filter: time_filter, timezone: timezone)    return [] unless start_time.present?    begin      query = Stat.where('stats.created_at >= ?', start_time)      results = query.joins(:player)        .select('players.name as player_name','players.steam_id as steam_id','SUM(stats.damage_dealt) as total_damage_dealt','SUM(stats.damage_taken) as total_damage_taken'        )        .group('players.id', 'players.name', 'players.steam_id')        .order("#{sort_by} DESC")        .limit(limit)      final_results = results.map do |result|        {          steam_id: result.steam_id,          player_name: result.player_name,          total_damage_dealt: result.total_damage_dealt.to_i,          total_damage_taken: result.total_damage_taken.to_i        }      end      return formatted_table ? formatted_table(data: final_results, time_filter: time_filter, sort_by: sort_by) : results    rescue ActiveRecord::StatementInvalid => e      Rails.logger.error("Error in DamageCalculatorService: #{e.message}")      []    end  end  def self.formatted_table(data:, time_filter:, sort_by:)    title = "#{sort_by.titleize.split('').join('')} for the #{time_filter}"    headers = %w[player_name total_damage_dealt total_damage_taken]    TabletizeService.new(title: title, data: data, headers: headers).table  endend

I have no issues using class methods, however it seems quite verbose to have all the parameters passed over and over, while in the instance it seems cleaner.

Using class instance variables is not good though since I can have multiple concurrent requests.

I then came up with this:

class DamageCalculatorService  include TimeFilterable  TOTAL_DAMAGE_DEALT_COLUMN = 'total_damage_dealt'.freeze  TOTAL_DAMAGE_TAKEN_COLUMN = 'total_damage_taken'.freeze  def self.damage_dealt(params)    calculate_damage(**params.merge({ sort_by: TOTAL_DAMAGE_DEALT_COLUMN }))  end  def self.damage_taken(params)    calculate_damage(**params.merge({ sort_by: TOTAL_DAMAGE_TAKEN_COLUMN }))  end  private  def self.calculate_damage(time_filter:, timezone:, limit:, formatted_table:, sort_by:)    start_time = TimeFilterable.start_time_for(time_filter: time_filter, timezone: timezone)    return [] unless start_time.present?    begin      query = Stat.where('stats.created_at >= ?', start_time)      results = query.joins(:player)        .select('players.name as player_name','players.steam_id as steam_id','SUM(stats.damage_dealt) as total_damage_dealt','SUM(stats.damage_taken) as total_damage_taken'        )        .group('players.id', 'players.name', 'players.steam_id')        .order("#{sort_by} DESC")        .limit(limit)      final_results = results.map do |result|        {          steam_id: result.steam_id,          player_name: result.player_name,          total_damage_dealt: result.total_damage_dealt.to_i,          total_damage_taken: result.total_damage_taken.to_i        }      end      return formatted_table ? formatted_table(data: final_results, time_filter: time_filter, sort_by: sort_by) : results    rescue ActiveRecord::StatementInvalid => e      Rails.logger.error("Error in DamageCalculatorService: #{e.message}")      []    end  end  def self.formatted_table(data:, time_filter:, sort_by:)    title = "#{sort_by.titleize.split('').join('')} for the #{time_filter}"    headers = %w[player_name total_damage_dealt total_damage_taken]    TabletizeService.new(title: title, data: data, headers: headers).table  endend

But this allows to pass any parameter I want, which doesn't seem great.

What is the clean and right way to define this?


Viewing all articles
Browse latest Browse all 4633

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>