Design Principles
Pranalee Rokde
WHY?
• Difficult to understand code
• Difficult to make safe changes, existing code gets change while
adding new feature as product expands
• Difficult to write jUnits
• Find 1 bug and have to search entire code base to fix for every
implementation
• Code review is difficult
SYMPTOMS OF BAD DESIGN
• Rigidity
• Fragility
• Immobility
• Viscosity
RIGIDITY
• It is hard to change because every change affects too many other
parts of the system.
FRAGILITY
• When you make a change, unexpected parts of the system break.
IMMOBILITY
• Inability to reuse software from parts of the same project or from
other projects.
• It often happens that one engineer will discover that he needs a
module that is similar to one that another engineer wrote. However, it
also often happens that the module in question has too much
baggage that it depends upon.
• It is hard to reuse in another application because it cannot be
separated from the current application.
VISCOSITY
• When the hacks are easy to employ than the design preserving
methods
SOLID
• Single Responsibility Principle
• Open Closed Principle
• Liskov's Substitution Principle
• Interface Segregation Principle
• Dependency Inversion Principle
SRP – SINGLE RESPONSIBILITY PRINCIPLE
• A class should have only one responsibility.
• A class should have only one reason to change.
• If we have 2 reasons to change for a class, we have to split the
functionality in two classes.
SINGLE RESPONSIBILITY PRINCIPLE
public class Employee {
public Money calculatePay() ...
public String reportHours() ...
public void save() ...
}
SINGLE RESPONSIBILITY PRINCIPLE
public class Employee {
public Money calculatePay() ...
}
public class EmployeeReporter {
public String reportHours(Employee e) ...
}
public class EmployeeRepository {
public void save(Employee e) ...
}
Single Responsibility Principle
public class UserSettingService
{
public void changeEmail(User user)
{
if(checkAccess(user))
{
//Grant option to change
}
}
public boolean checkAccess(User user)
{
//Verify if the user is valid.
}
}
Single Responsibility Principle
public class SecurityService
{
public static boolean checkAccess(User user)
{
//check the access.
}
}
public class UserSettingService
{
public void changeEmail(User user)
{
if(SecurityService.checkAccess(user))
{
//Grant option to change
}
}
}
Single Responsibility Principle
• The SRP is one of the simplest of the principles, and one of the
hardest to get right
• 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
• i.e. Don’t fall into trap of needless complexity
OCP - OPEN CLOSED PRINCIPLE
• Software entities should be open for extension but closed for
modification
OCP - Example Version 1
public class AreaCalculator
{
public double Area(Rectangle[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
area += shape.Width*shape.Height;
}
return area;
}
}
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
}
OCP - Example Version 2
public double Area(object[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
if (shape is Rectangle)
{
Rectangle rectangle = (Rectangle) shape;
area += rectangle.Width*rectangle.Height;
}
else
{
Circle circle = (Circle)shape;
area += circle.Radius * circle.Radius * Math.PI;
}
}
return area;
}
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
}
public class Circle
{
public double Radius { get; set; }
}
OCP - Example Version 3
public double Area(Shape[] shapes)
{
double area = 0;
foreach (var shape in shapes)
{
area += shape.Area();
}
return area;
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double Area()
{
return Width*Height;
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double Area()
{
return Radius*Radius*Math.PI;
}
}
OCP
• OCP is heart of object oriented design
• OCP leads to design patterns like Factory, Observer
SRP/OCP Benefits
• Less merge conflicts
• Less changes to existing code means easy to trace bug in regression
testing
• Saved efforts on rewriting jUnits as existing code has less chances to
change
LSP - LIKOV'S SUBSTITUTION PRINCIPLE
• Child classes should never break the parent class' type definitions
LSP - LIKOV'S SUBSTITUTION PRINCIPLE
class Rectangle
{
protected int m_width;
protected int m_height;
public void setWidth(int width){
m_width = width;
}
public void setHeight(int height){
m_height = height;
}
public int getWidth(){
return m_width;
}
public int getHeight(){
return m_height;
}
public int getArea(){
return m_width * m_height;
}
}
class Square extends Rectangle
{
public void setWidth(int width){
m_width = width;
m_height = width;
}
public void setHeight(int height){
m_width = height;
m_height = height;
}
}
LSP - LIKOV'S SUBSTITUTION PRINCIPLE
class LspTest
{
private static Rectangle getNewRectangle()
{
// it can be an object returned by some factory ...
return new Square();
}
public static void main (String args[])
{
Rectangle r = LspTest.getNewRectangle();
r.setWidth(5);
r.setHeight(10);
// user knows that r it's a rectangle.
// It assumes that he's able to set the width and height as for the base class
System.out.println(r.getArea());
// now he's surprised to see that the area is 100 instead of 50.
}
}
LSP - LIKOV'S SUBSTITUTION PRINCIPLE
• LSP must be followed correctly in order for OCP to function correctly
ISP - INTERFACE SEGREGATION PRINCIPLE
• Fat interfaces
• No clients should be forced to implement methods it doesn't use.
• Abstract class - sub classes with doNothing override
ISP - Violation Example
interface IMessage {
List<String> getToAddresses();
String getMessageBody();
String getSubject();
boolean Send();
}
ISP - Violation Example
class EmailMessage implements IMessage {
public List<String> getToAddresses() {
// return addresses
return null;
}
public String getMessageBody() {
// return message body
return null;
}
public String getSubject() {
// return subject
return null;
}
public boolean Send() {
// send email
return false;
}
}
class SMSMessage implements IMessage {
public List<String> getToAddresses() {
// return addresses
return null;
}
public String getMessageBody() {
// return message body
return null;
}
public String getSubject() {
throw new RuntimeException("SMS don't have subject");
}
public boolean Send() {
// send sms
return false;
}
}
ISP Example
interface IMessage {
List<String> getToAddresses();
String getMessageBody();
boolean Send();
}
interface IEmailMessage extends IMessage{
List<String> getBccAddresses();
String getSubject();
}
ISP Example
class EmailMessage implements IEmailMessage {
public List<String> getToAddresses() {
// return addresses
return null;
}
public String getMessageBody() {
// return message body
return null;
}
public String getSubject() {
// return subject
return null;
}
public boolean Send() {
// send email
return false;
}
public List<String> getBccAddresses() {
// return bcc addresses
return null;
}
}
class SMSMessage implements IMessage {
public List<String> getToAddresses() {
// return addresses
return null;
}
public String getMessageBody() {
// return message body
return null;
}
public boolean Send() {
// send sms
return false;
}
}
ISP
• Fat interfaces lead to inadvertent couplings
DIP - DEPENDENCY INVERSION PRINCIPLE
public class ElectricPowerSwitch {
public LightBulb lightBulb;
public boolean on;
public ElectricPowerSwitch(LightBulb lightBulb) {
this.lightBulb = lightBulb;
this.on = false;
}
public boolean isOn() {
return this.on;
}
public void press(){
boolean checkOn = isOn();
if (checkOn) {
lightBulb.turnOff();
this.on = false;
} else {
lightBulb.turnOn();
this.on = true;
}
}
}
public class LightBulb {
public void turnOn() {
System.out.println("LightBulb: Bulb turned on...");
}
public void turnOff() {
System.out.println("LightBulb: Bulb turned off...");
}
}
DIP - DEPENDENCY INVERSION PRINCIPLE
public interface Switch {
boolean isOn();
void press();
}
public class ElectricPowerSwitch implements Switch {
public Switchable client;
public boolean on;
public ElectricPowerSwitch(Switchable client) {
this.client = client;
this.on = false;
}
public boolean isOn() {
return this.on;
}
public void press(){
boolean checkOn = isOn();
if (checkOn) {
client.turnOff();
this.on = false;
} else {
client.turnOn();
this.on = true;
}
}
}
public interface Switchable {
void turnOn();
void turnOff();
}
public class LightBulb implements Switchable {
@Override
public void turnOn() {
System.out.println("LightBulb: Bulb turned on...");
}
@Override
public void turnOff() {
System.out.println("LightBulb: Bulb turned off...");
}
}
DIP
Few more…
• DRY/WET
• Abstract class
• Package principles
Thank You
References
• Articles by Robert Martin

Design principles - SOLID

  • 1.
  • 2.
    WHY? • Difficult tounderstand code • Difficult to make safe changes, existing code gets change while adding new feature as product expands • Difficult to write jUnits • Find 1 bug and have to search entire code base to fix for every implementation • Code review is difficult
  • 3.
    SYMPTOMS OF BADDESIGN • Rigidity • Fragility • Immobility • Viscosity
  • 4.
    RIGIDITY • It ishard to change because every change affects too many other parts of the system.
  • 5.
    FRAGILITY • When youmake a change, unexpected parts of the system break.
  • 6.
    IMMOBILITY • Inability toreuse software from parts of the same project or from other projects. • It often happens that one engineer will discover that he needs a module that is similar to one that another engineer wrote. However, it also often happens that the module in question has too much baggage that it depends upon. • It is hard to reuse in another application because it cannot be separated from the current application.
  • 7.
    VISCOSITY • When thehacks are easy to employ than the design preserving methods
  • 8.
    SOLID • Single ResponsibilityPrinciple • Open Closed Principle • Liskov's Substitution Principle • Interface Segregation Principle • Dependency Inversion Principle
  • 10.
    SRP – SINGLERESPONSIBILITY PRINCIPLE • A class should have only one responsibility. • A class should have only one reason to change. • If we have 2 reasons to change for a class, we have to split the functionality in two classes.
  • 11.
    SINGLE RESPONSIBILITY PRINCIPLE publicclass Employee { public Money calculatePay() ... public String reportHours() ... public void save() ... }
  • 12.
    SINGLE RESPONSIBILITY PRINCIPLE publicclass Employee { public Money calculatePay() ... } public class EmployeeReporter { public String reportHours(Employee e) ... } public class EmployeeRepository { public void save(Employee e) ... }
  • 13.
    Single Responsibility Principle publicclass UserSettingService { public void changeEmail(User user) { if(checkAccess(user)) { //Grant option to change } } public boolean checkAccess(User user) { //Verify if the user is valid. } }
  • 14.
    Single Responsibility Principle publicclass SecurityService { public static boolean checkAccess(User user) { //check the access. } } public class UserSettingService { public void changeEmail(User user) { if(SecurityService.checkAccess(user)) { //Grant option to change } } }
  • 15.
    Single Responsibility Principle •The SRP is one of the simplest of the principles, and one of the hardest to get right • 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 • i.e. Don’t fall into trap of needless complexity
  • 16.
    OCP - OPENCLOSED PRINCIPLE • Software entities should be open for extension but closed for modification
  • 17.
    OCP - ExampleVersion 1 public class AreaCalculator { public double Area(Rectangle[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Width*shape.Height; } return area; } } public class Rectangle { public double Width { get; set; } public double Height { get; set; } }
  • 18.
    OCP - ExampleVersion 2 public double Area(object[] shapes) { double area = 0; foreach (var shape in shapes) { if (shape is Rectangle) { Rectangle rectangle = (Rectangle) shape; area += rectangle.Width*rectangle.Height; } else { Circle circle = (Circle)shape; area += circle.Radius * circle.Radius * Math.PI; } } return area; } public class Rectangle { public double Width { get; set; } public double Height { get; set; } } public class Circle { public double Radius { get; set; } }
  • 19.
    OCP - ExampleVersion 3 public double Area(Shape[] shapes) { double area = 0; foreach (var shape in shapes) { area += shape.Area(); } return area; } public class Rectangle : Shape { public double Width { get; set; } public double Height { get; set; } public override double Area() { return Width*Height; } } public class Circle : Shape { public double Radius { get; set; } public override double Area() { return Radius*Radius*Math.PI; } }
  • 20.
    OCP • OCP isheart of object oriented design • OCP leads to design patterns like Factory, Observer
  • 22.
    SRP/OCP Benefits • Lessmerge conflicts • Less changes to existing code means easy to trace bug in regression testing • Saved efforts on rewriting jUnits as existing code has less chances to change
  • 23.
    LSP - LIKOV'SSUBSTITUTION PRINCIPLE • Child classes should never break the parent class' type definitions
  • 24.
    LSP - LIKOV'SSUBSTITUTION PRINCIPLE class Rectangle { protected int m_width; protected int m_height; public void setWidth(int width){ m_width = width; } public void setHeight(int height){ m_height = height; } public int getWidth(){ return m_width; } public int getHeight(){ return m_height; } public int getArea(){ return m_width * m_height; } } class Square extends Rectangle { public void setWidth(int width){ m_width = width; m_height = width; } public void setHeight(int height){ m_width = height; m_height = height; } }
  • 25.
    LSP - LIKOV'SSUBSTITUTION PRINCIPLE class LspTest { private static Rectangle getNewRectangle() { // it can be an object returned by some factory ... return new Square(); } public static void main (String args[]) { Rectangle r = LspTest.getNewRectangle(); r.setWidth(5); r.setHeight(10); // user knows that r it's a rectangle. // It assumes that he's able to set the width and height as for the base class System.out.println(r.getArea()); // now he's surprised to see that the area is 100 instead of 50. } }
  • 26.
    LSP - LIKOV'SSUBSTITUTION PRINCIPLE • LSP must be followed correctly in order for OCP to function correctly
  • 28.
    ISP - INTERFACESEGREGATION PRINCIPLE • Fat interfaces • No clients should be forced to implement methods it doesn't use. • Abstract class - sub classes with doNothing override
  • 29.
    ISP - ViolationExample interface IMessage { List<String> getToAddresses(); String getMessageBody(); String getSubject(); boolean Send(); }
  • 30.
    ISP - ViolationExample class EmailMessage implements IMessage { public List<String> getToAddresses() { // return addresses return null; } public String getMessageBody() { // return message body return null; } public String getSubject() { // return subject return null; } public boolean Send() { // send email return false; } } class SMSMessage implements IMessage { public List<String> getToAddresses() { // return addresses return null; } public String getMessageBody() { // return message body return null; } public String getSubject() { throw new RuntimeException("SMS don't have subject"); } public boolean Send() { // send sms return false; } }
  • 31.
    ISP Example interface IMessage{ List<String> getToAddresses(); String getMessageBody(); boolean Send(); } interface IEmailMessage extends IMessage{ List<String> getBccAddresses(); String getSubject(); }
  • 32.
    ISP Example class EmailMessageimplements IEmailMessage { public List<String> getToAddresses() { // return addresses return null; } public String getMessageBody() { // return message body return null; } public String getSubject() { // return subject return null; } public boolean Send() { // send email return false; } public List<String> getBccAddresses() { // return bcc addresses return null; } } class SMSMessage implements IMessage { public List<String> getToAddresses() { // return addresses return null; } public String getMessageBody() { // return message body return null; } public boolean Send() { // send sms return false; } }
  • 33.
    ISP • Fat interfaceslead to inadvertent couplings
  • 35.
    DIP - DEPENDENCYINVERSION PRINCIPLE public class ElectricPowerSwitch { public LightBulb lightBulb; public boolean on; public ElectricPowerSwitch(LightBulb lightBulb) { this.lightBulb = lightBulb; this.on = false; } public boolean isOn() { return this.on; } public void press(){ boolean checkOn = isOn(); if (checkOn) { lightBulb.turnOff(); this.on = false; } else { lightBulb.turnOn(); this.on = true; } } } public class LightBulb { public void turnOn() { System.out.println("LightBulb: Bulb turned on..."); } public void turnOff() { System.out.println("LightBulb: Bulb turned off..."); } }
  • 36.
    DIP - DEPENDENCYINVERSION PRINCIPLE public interface Switch { boolean isOn(); void press(); } public class ElectricPowerSwitch implements Switch { public Switchable client; public boolean on; public ElectricPowerSwitch(Switchable client) { this.client = client; this.on = false; } public boolean isOn() { return this.on; } public void press(){ boolean checkOn = isOn(); if (checkOn) { client.turnOff(); this.on = false; } else { client.turnOn(); this.on = true; } } } public interface Switchable { void turnOn(); void turnOff(); } public class LightBulb implements Switchable { @Override public void turnOn() { System.out.println("LightBulb: Bulb turned on..."); } @Override public void turnOff() { System.out.println("LightBulb: Bulb turned off..."); } }
  • 37.
  • 38.
    Few more… • DRY/WET •Abstract class • Package principles
  • 39.