programming-examples/ruby/_Basics/single_responsibility.rb
2019-11-15 12:59:38 +01:00

93 lines
2.6 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# The Single Responsibility Principle is the most abstract of the bunch. It
# helps keep classes and methods small and maintainable. In addition to keeping
# classes small and focused it also makes them easier to understand.
# While we all agree that focusing on a single responsibility is important, its
# difficult to determine what a classs responsibility is. Generally, it is said
# that anything that gives a class a reason to change can be viewed as a
# responsibility. By change I am talking about structural changes to the class
# itself (as in modifying the code in the classs file, not the objects
# in-memory state).
# In the below class we have a single command interface that processes
# commission payments for deals. At first glance the class seems simple enough,
# but lets look at reasons we might want to change this class. Any change in
# how we calculate commissions would require a change to this class. We could
# introduce new commission rules or strategies that would cause our
# calculate_commission method to change. For instance, we might want to vary
# the percentage based on deal amount. Any change in the steps required to mark
# a deal as processed in the mark_deal_processed method would result in a change
# in the file as well. An example of this might be adding support for sending an
# email summary of a specific persons commissions after marking a deal
# processed. The fact that we can identify multiple reasons to change signals a
# violation of the Single Responsibility Principle.
class DealProcessor
attr_reader :deals
def initialize(deals)
@deals = deals
end
def process
deals.each do |deal|
# Here we calculate commission and create instance of Commission
Commission.create(deal: deal, amount: calculate_commission(deal))
mark_deal_processed
end
end
private
def mark_deal_processed
# Implementation
end
def calculate_commission(deal)
deal.amount * 0.05
end
end
class Commission
# Implementation
end
# Solution
class DealProcessor
attr_reader :deals
def initialize(deals)
@deals = deals
end
def process
deals.each do |deal|
# Now we call calculator in one operation, all logic now in it
CommissionCalculator.create_commission(deal) if mark_deal_processed
end
end
private
def mark_deal_processed
# Implementation
end
end
class CommissionCalculator
def self.create_commission(deal)
Commission.new(deal: deal, amount: calculate(deal))
end
private
def self.calculate(deal)
deal.amount * 0.05
end
end
class Commission
# Implementation
end