Single Responsibility Principle

43

Transcript of Single Responsibility Principle

Single Responsibility PrincipleDo One thing! Do It Well!

SOLID

Five basic principles of object-oriented programming and design that deals with “dependency management”

● S SRP Single responsibility principle● O OCP Open/closed principle● L LSP Liskov substitution principle● I ISP Interface segregation principle● D DIP Dependency inversion principle

Principles vs. Patterns

A pattern in “is a general reusable solution to a commonly occurring problem within a given context in software design”

● Use a pattern to solve a known problem.● Use the principles to identify the problem and come up

with the solution.

Single Responsibility Principle

“A class should have one and only one reason to change, meaning that a class should have only one job.”

Not responsible for bug fixes or refactoring. Those things are the responsibility of the programmer, not of the program

Then what’s a program responsible to?

First Example

class Employee {

public Money calculatePay()

public void save()

public String reportHours()

}

Three reasons to change:● The business rules having to do with calculating pay.● The database schema.● The format of the hours report.

Step by Step Example

New request has arrived:1. Generate a weekly report of online users2. Send it to Jill in France

First glance ...

1- Monthly instead of weekly

3- Jill gets fired

2- Change report’s format to CSV

Possible Reasons to Change

1- Get a list of users upon a criteria

2- Format the message

3- Deliver the report

Break down:Responsibilities handled in each method in one class

4- Put it all togather

1- CSV Formatter

2- Query execution

Break down:Separate among many classes

3- Report generation

Inject these two, so UserWeeklyReport would rarely change

4- Mail delivery

And finally ….

5- Parse users

Sketch - Relations

ReportMailer UserWeeklyReport

ReportDataCSVCompiler UserWeeklyReportData

User

Formatting

Parsing users

Getting users

Mail delivery

Report generation

Separation of Concerns

Classes are not the end. Applicable on every level!

It goes back even to the days when Structured Programming was all the hype

Applicable on functions, classes, modules, …

Separation of Concerns

● OOP languages separate concerns into objects

● Architectural design patterns like MVC separate content from presentation and the data-processing (model) from content

● Service-oriented design use services

● Procedural languages such as C use functions.

Method-wise Example

● In a class that handles syncing local data with Dropbox

● Show up an alert view to handle some clashes

● Due to some design limitation, the following method gets called to handle the user’s choice for each alert-alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

Scary to look !

Response to “Notepad clashes” alert

Three sub cases!

Response to “older DB” alert

Handle project clashes

Logical separationThe purpose of this method is clear: “switching”

Each of the other three methods handle one case

1

23

We can apply the same here

1

2

3

Examples of “Frequent” Responsibilities

● Persistence to storage● Validation ● Notification ● Error Handling● Logging● Class Selection / Instantiation● Formatting● Parsing● Mapping

How do you know that SRP is violated?

First answer: “Length”

Popular choices:Classes shouldn’t exceed 200 lines of code (exclude comments)Methods shouldn’t exceed 30 lines of code

Is this a solid metric? … Not exactly!

Less than 200 lines of code

Does it follow SRP correctly?

Less than 200 lines of code

Does it follow SRP correctly?

Code to “prepare” for requests performed for Dashboard

Descriptors.Similar to routing

MappingJSON <-> Objects

Various requests performed on Dashboard

Reasons “Mapping” part would change● Web request refactored

● Dashboard needed to be used in another request with different parameters

Encapsulate a single request into a proxy object

Few reasons to make this class change

Biggest benefit: Dashboard is simpler and shorter

Length ??

So length isn’t a very accurate metric.

If a class is higher than 200 loc, then it’s problem!

If it’s fewer than 200 loc, that doesn’t mean you’re clear!

SRP Violation Smells

● Class has too many instance variables and methods○ Especially, when you always use a group of them together

● The test class becomes too complicated○ Too many mock objects○ Too many test cases

● Use “and” or “or” in names● Class with low cohesion

○ Many parts not related to each other

● Class / Method is Long

SRP Violation Smells

● Change In One Place Breaks Another

● Shotgun Effect

● Unable to Encapsulate a Module

● Class has too many dependencies

Why?

● Organize the code

○ So you know where to look for the next request

● Code Changes

○ If you have to refactor one class, only one class will change

● Less fragile

○ “You were only supposed to send an email, how did you end

up deleting all the data?”

Why?

● Testability○ Less test cases for a class with one job to do

● Code sharing○ All previous reasons are equally or more important than this

one!○ Don’t wait till you have to reuse the code, start breaking

down your code when it makes sense.

Complex view example

A controller that populates a table view and a charts area with data retrieved from another object

Edit and filter dataAlready separated

Summary data in a table

Charts

Data source to ChartsEngine: Provides needed content to the object that draws the charts~ 50 lines

Source to the side table view~ 40 lines

Another responsibility: Provide a widget builder so other object can use it build it.

Get data and show it in view

Separate providing data to table in a new object

And one for charts

Add a couple of lines to initialize those helpers and provide data to them

And the view controller drops to ~ 100 lines of total code

One clear responsibility: Get data from a data loader object and provide to “view source” object

We didn’t handle that yet! But you get the picture...

Correlation with Frameworks

Sometimes, the way the framework is designed makes it easy to put all your code in one place

● iOS: View controllers and application delegates

● Android: Activities

● RoR: ActiveRecord models

Monster AppDelegate - Example Responsibilities

● Receive application’s notifications● Register for a 3rd party service● Handle global view settings● Show notification for version updates and handle

user’s action● Dropbox connection● Handle push notifications

Example Patterns to Decompose Fat Classes

● Standard composition / Delegation○ Two basic design patterns○ Break a complex class into smaller ones

● View objects / View model / Presenter○ If some logic is needed to prepare the model for view

● Decorator○ Put the extra behaviour in a new class

● Specific patterns to specific problems○ e.g. Factory / Builder for object creation

References1. The Principles of OOD (Uncle Bob)2. SOLID (Wikipedia)3. Software Design Pattern (Wikipedia)4. Uncle Bob (blog.8thlight.com)5. Ruby Example (John Bohn)6. Separation of Concerns (Wikipedia)7. Examples of Responsiblities (deviq.com)8. Why SRP and SRP Smells (Java Code Geeks)9. Fat ActiveRecord Models (blog.codeclimate.com)

10. Active Record Breaks SRP by Design (sitepoint.com) 11. Active Record Breaks SRP by Design (programmers.stackexchange)12. Patterns to Destroy Big View Controllers (Soroush Khanlou)