# 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, it’s # difficult to determine what a class’s 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 class’s file, not the object’s # 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 let’s 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 person’s 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