# S.O.L.I.D PRINCIPLES
###### tags: `Concepts`
[TOC]
## What is SOLID?
**S**ingle Responsability Principle SRP(單一責任)
**O**pen Closed Principle OCP (開放封閉原則)
**L**iskov Substitution Principle LSP (里氏替換原則)
**I**nterface Segregation Principle ISP (介面隔離原則)
**D** Inversion Principle DIP (相依反轉原則)
Note: https://proandroiddev.com/exploring-s-o-l-i-d-principle-in-android-a90947f57cf0
## S - Single Responsability Principle (SRP)
A class/method should only have one responsability.
**Before**
```java===
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Movie movie = movies.get(position);
holder.title.setText(movie.getTitle());
holder.rating.setText(movie.getRating());
//SRP violation, onBindViewHolder has only the responsibility to display data
// & not make data formatting operations
String[] genres = song.getGenres();
StringBuilder builder = new StringBuilder();
foreach (String genre : genres){
builder.append(genre).append(",");
}
holder.genres.setText(builder.toString());
}
```
**After**
```java===
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Movie movie = movies.get(position);
holder.title.setText(movie.getTitle());
holder.rating.setText(movie.getRating())
//all the logic is moved into util class...now is clean!
holder.authors.setText(AppUtils.getGenres(movie))
}
```
## O - Open Closed Principle (OCP)
If we are required to add a new features to the project, it is good practice to **NOT** modify the existing code but rather write new code that will be used by the existing code.
**Before**
```java===
public class TimeOfDayGreeting {
private String timeOfDay;
/*
* Every time this method is called it will
* called an if else logic, which is in violation of the
* OCP principle.
*/
public String getGreetingFromTimeOfDay() {
if (this.timeOfDay == "Morning") {
return "Good Morning, sir.";
}
else if (this.formality == "Afternoon") {
return "Good Afternoon, sir.";
}
else if (this.formality == "Evening") {
return "Good Evening, sir.";
}
else {
return "Good Night, sir.";
}
}
public void setTimeOfDay(String timeOfDay) {
this.timeOfDay = timeOfDay;
}
}
```
**After**
```java===
public class TimeOfDayGreeting {
private TimeOfDay timeOfDay;
public TimeOfDayGreeting(TimeOfDay timeOfDay) {
this.timeOfDay = timeOfDay;
}
public String getGreetingFromTimeOfDay() {
return this.timeOfDay.greet();
}
}
public interface TimeOfDay {
public String greet();
}
/* Morning class */
public class Morning implements TimeOfDay {
public String greet() {
return "Good morning, sir.";
}
}
/* Afternoon class */
public class Afternoon implements TimeOfDay {
public String greet() {
return "Good afternoon, sir.";
}
}
/* Evening class */
public class Evening implements TimeOfDay {
public String greet() {
return "Good evening, sir.";
}
}
/* Night class */
public class Night implements TimeOfDay {
public String greet() {
return "Good night, sir.";
}
}
```
## L —The Liskov Substitution Principle (LSP)
Sub classes should oveverride classes from parent and do not break parent class' type definitions/methods.
**Before**
Bad Practice: Why Override onClick and make methods separately? You still will have to check which instance are you from, therefore, it is not recommended.
```java===
public interface ClickListener {
public void onClick();
}
public class Fragment1 implements ClickListener {
@Override
public void onClick() {
//handle logic
}
public void decrementClickCount() {
}
}
public class Fragment2 implements ClickListener {
@Override
public void onClick() {
//handle logic
}
public void incrementClickCount() {
}
}
public void onButtonClick(ClickListener clickListener) {
// IF we have a requirement where we need to increment the click count in
// framgent2 but decrement the count in fragment 1
// we would have to follow something like this, which is bad practice.
if(clickListener instanceOf Fragment2) {
clickListener.incrementClickCount();
} else if(clickListener instanceOf Fragment1) {
clickListener.decrementClickCount();
}
clickListener.onClick();
}
```
**After**
```java===
public interface ClickListener {
public void onClick();
}
public class Fragment1 implements ClickListener {
@Override
public void onClick() {
decrementClickCount();
//handle logic
}
public void decrementClickCount() {
}
}
public class Fragment2 implements ClickListener {
@Override
public void onClick() {
incrementClickCount();
//handle logic
}
public void incrementClickCount() {
}
}
/*
* We handle the individual logic inside the overridden methods
* in the framgents. In the main implementation we should
* never handle logic
*/
public void onButtonClick(ClickListener clickListener) {
clickListener.onClick();
}
```
## I — The Interface Segregation Principle (ISP)
The interface-segregation principle (ISP) states that no client should be forced to depend on methods it does not use.
In simple words, it means that sometimes the interface is too fat, and we do not actually need all methods in some cases. Therefore, we should split it into small interfaces for the client to use.
**Before**
We are aware that TextWatcher interface always have 3 methods, but we do not actually always use all 3 of them.
```java===
/*
* There are 3 methods to the TextWatcher interface.
* But in most scenarios we need to use only one method.
* This means ideally the remaining two methods have no use and should
* not be part of the interface. This is in violation of the ISP principle
*/
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
/* In most of the scenarios this is the only method we use. The other methods are pointless in these cases. */
}
@Override
public void afterTextChanged(Editable editable) {
}
});
```
**After**
```java===
/* We create an interface with one method */
public interface TextWatcherWithInstance {
void onTextChanged(EditText editText, CharSequence s, int start, int before, int count);
}
/* We create a custom class called MultiTextWatcher.
* And pass the interface here
*/
public class MultiTextWatcher {
private TextWatcherWithInstance callback;
public MultiTextWatcher setCallback(TextWatcherWithInstance callback) {
this.callback = callback;
return this;
}
public MultiTextWatcher registerEditText(final EditText editText) {
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
callback.onTextChanged(editText, s, start, before, count);
}
@Override
public void afterTextChanged(Editable editable) {}
});
return this;
}
/*
* We can call this class from our Activity/Fragment like this:
* This only has one method, which we are using in the app
*/
new MultiTextWatcher()
.registerEditText(editText)
.setCallback(new MultiTextWatcher.TextWatcherWithInstance() {
@Override
public void onTextChanged(EditText editText, CharSequence s, int start, int before, int count) {
}
});
```
## D — The Dependency Inversion Principle (DIP)
If you use a class insider another class, this class will be dependent of the class injected.
>High-level modules **SHOULD NOT** depend on low-level modules. Both should depend on abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.[color=Orange]