To make design to be more
- Understandable
- Easy to extend
- Easy to Maintain, Refactor
- Single Responsibility Principle - SRP
- Open/Close Principle - OCP
- Liksov Substitue Principle - LSP
- Interface Segregation Principle - ISP
- Dependency Inversion Principle - DIP
A Class should have Only One reason to change. One Class for One Responsibility
- Class is responsible for only one behavior/functionality
- Below class 'Post' has functionality to post the message
- Should do only posting the mesage
- Shouldn't do logging the message to Database and this needs to be taken care of another class
class Post {
public void postMessage(String message){
// INFO: Only Message Post, No other responsibility
dao.post(message);
// INFO: Writng the logging to database for future references
// dao.logMessage(message); // <== Shouldn't do this, violates the (S) principle
dbLogger.logMessage(message); // <== Here, DBLogger would take care of logging to DB
}
}
Open for extension & Close for modification
- functionality can be extended through Inheritence and can be used by Polymorphism
- if we need to provide or alter the functionality, create a new implementation instead of modifying existing
class Post {
public void postMessage(String message) {
/*
>> This shouldn't be here as voilates (O) Principle <<
// INFO: If message starts with '#', create a tag instead of message
if (message.startsWith('#')) {
dao.postAsTag(message);
} else {
dao.post(message);
}
*/
// Post Message
dao.post(message);
}
}
class TagPost {
public void postMessage(String message) {
dao.postAsTag(message);
}
}
Object should be substiuted with Object of their subtypes
- S is sub type of T, then T object can be substituted with S object
- Objects in the program should be replaced by instances of their subtypes without alerting the correctness of program
abstract class Post {
public void postMessage(String message) {
// default implementation if any
}
}
class MessagePost extends Post {
public void postMessage(String message) {
dao.post(message);
}
}
class TagPost: Post {
public void postMessage(String message) {
dao.postAsTag(message);
}
}
class PostHandler {
public void postMessage(String message) {
Post post = null;
if (message.startsWith('#')) {
post = new TagPost();
} else {
post = new MessagePost();
}
post.postMessage(message); // <== We are not alerting the method call or logic
}
}
No client should be forced to depend on the methods it doesn't use
- To keep system decoupled and easier to refacter
- Don't add additional functionality to existing interface, instead add a new interface
interface IPost {
public void postMessage(String message);
}
interface IPostRead {
public List<String> readMessage();
}
class PostImpl implements IPost, IPostRead { // <== Multiple Interface Implementations
public void postMessage(String message) {
// Implementation
}
public List<String> readMessages() {
// Implementation
return list;
}
}
On should depend on Abstractions, not on Concretes
- Typically, this is Dependency Injection
- Abstractions shouldn't depend on details, But details should depend on Abstractions
class Post {
IPostDAO dao;
IDBLogger dbLogger;
public Post(IPostDAO dao, IDBLogger dbLogger) { // <== Dependency Injection
this.dao = dao;
this.dbLogger = dbLogger;
}
public void post(String message){
try {
dao.post(message);
} catch(Exception excp) {
dbLogger.logException(excp);
}
}
}