a method m of an object O may only invoke the methods of the following kinds of objects: • O itself • m's parameters • Any objects created/instantiated within m • O's direct component objects • A global variable, accessible by O, in the scope of m
my two dollars!” Wallet theWallet = myCustomer.getWallet(); if (theWallet.getTotalMoney() > payment) { theWallet.subtractMoney(payment); } else { // come back later and get my money } }
my two dollars!” paidAmount = myCustomer.getPayment(payment); if (paidAmount == payment) { // say thank you and give customer a receipt } else { // come back later and get my money } }
my two dollars!” paidAmount = myCustomer.getPayment(payment); if (paidAmount == payment) { // say thank you and give customer a receipt } else { // come back later and get my money } }
my two dollars!” paidAmount = myCustomer.getWalletSubtractAmount(payment); if (paidAmount == payment) { // say thank you and give customer a receipt } else { // come back later and get my money } }
my two dollars!” paidAmount = myCustomer.subtractAmountFromWallet(payment); if (paidAmount == payment) { // say thank you and give customer a receipt } else { // come back later and get my money } }
# … 10 more email-related attributes … end def send_a_bunch_of_emails(user) if user.send_weekly_email_summary? SendWeeklySummary.perform_later(user) elsif user.send_daily_email_summary? SendDailySummary.perform_later(user) end end
send_a_bunch_of_emails(user) if user.email_settings.weekly_summary? SendWeeklySummary.perform_later(user) elsif user.email_settings.daily_summary? SendDailySummary.perform_later(user) end end
/ … increases the number of methods. [ / … the abstraction may be less comprehensible, and implementation and maintenance are more difficult. There might also be an increase in the number of arguments passed to some methods. 6 The Trade-off
Comment.transaction do @comment.save! update_article_counter update_some_other_thing end end private def update_article_counter @comment.article.update_comment_count end def update_some_other_thing # ... end end
Comment.transaction do @comment.destroy update_article_counter update_some_other_thing end end private def update_article_counter @comment.article.update_comment_count end def update_some_other_thing # ... end end
Comment.transaction do @comment.save! update_article_counter update_some_other_thing end end def destroy Comment.transaction do @comment.destroy update_article_counter update_some_other_thing end end private # ... end
Comment.transaction do @comment.save! update_article_counter update_some_other_thing end end def destroy Comment.transaction do @comment.destroy update_article_counter update_some_other_thing end end private # ... end
Comment.transaction do @comment.save! update_article_counter update_some_other_thing end end def not_very_srp! Comment.transaction do @comment.destroy update_article_counter update_some_other_thing end end private # ... end
late 1990s I tried to consolidate these notions into a principle, which I called: The Single Responsibility Principle.” “[It] states that each software module should have one and only one reason to change.”
scenario "registration form sends an email" do register_user(user_params) expect_an_email_to_have_been_sent end scenario "invalid registration form" do register_user(invalid_user_params) expect_errors_in_the_form end
expect_user_to_be_registered end scenario "registration form" do visit '/register' fill_in_login_details(invalid_params) fill_in_address(invalid_params) fill_in_banking_info(invalid_params) submit_form expect_errors_in_form end
how the application is changing.” “[…] If, on the other hand, the application is not changing in ways that cause the the two responsibilities to change at different times, then there is no need to separate them.” “An axis of change is only an axis of change if the changes actually occur. It is not wise to apply the SRP, or any other principle for that matter, if there is no symptom.” – Robert C. Martin