This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/invert-control
Hands down, these are the best videos on the net right now for Rubyists and possibly coders in general. Huge huge huge thanks and mad props for Ben, Joe, and the team who make these.
keep em coming, we will keep watching and implementing!
I like this kind of idea to push dependencies up into one place. In this particular example, I think we could go further by asking a question why Sender
has to know about the parser at all because what they need to know is only a collection of recipients to do their job, so I think itâs better Sender
to accept recipients
instead of parser
:
class InvitationsController < ApplicationController
def create
recipients = Parser.new(params[:csv_file].read).recipients
Sender.new(recipients, params[:message]).send
end
end
class Sender
def initialize(recipients, message)
...
end
...
end
Before Sender
has to know about parser
on how to get recipients and use each recipient object in the collection, but by doing refactoring above we reduce responsibility of Sender
to not even care how to get recipients
from the parser at all.
I absolutely agree with your assessment here. Keep the chain as short as possible without exposing the left and right link where it doesnât need to be.
Sender
probably wasnât the best name for this class. There might exist classes whose sole purpose is to wrap the interaction with the system:
class CommentOnIssue
def run(issue_id, message)
comment = Comment.new(issue_id: issue_id, message: message)
comment.save
comment.mentioned_user_names.each do |name|
user = User.find_by_name name
IssueMailer.mail_mentioned_in_comment(user, comment).deliver
end
end
end
vs
class CommentOnIssue < Struct.new(:database, :mailer)
def run(issue_id, message)
comment = Comment.new(issue_id: issue_id, message: message)
database.create comment
comment.mentioned_user_names.each do |name|
user = database.find_user_by_name name
mailer.mail_mentioned_in_comment(user, comment)
end
end
end
In the first example the abstract concept of the use case depends on the implementation details of how to persist things (using ActiveRecord) and how to mail (using ActionMailer). While the second example depends only on abstractions.
Thanks! It really means a lot to us, and youâve just made my morning; happy friday!