Principles of Object Oriented Design
Download
Report
Transcript Principles of Object Oriented Design
High Cohesion
Low Coupling
Old Standards
for Object Oriented Programming
Problems Solved
Maintainability
Easier to Test
Less Resistance to Change
Fear to Change Code Leads to Old Stinky Code
Reduces Repetition (DRY)
Don’t Cut and Paste!!!!
Allows For Continuous Improvement
Essential for Agile Development
Topics To Discuss
Single Responsibility Principle
Dependency Inversion Principle
Open Closed Principle
Interface Segregation Principle
Law of Demeter
Liskov Substitution Principle
Open Closed Principle
Classes should be Open to Extensibility and Closed to
Modifications
To Change behavior, you want to add new code instead
of modifying existing Code.
May not have access to code
Single Responsibility Principle
What is Responsibility?
- Reason for change
- Find how many reason you can think of for
you class/method to change
- If more than one, then your design violates
SRP
Single Responsibility Principle
Demo
Single Responsibility Principle
Problem: we have more than one responsibility
reading transaction record
Formatting
Sending Email
Different user may want different email format
IT Dept. may decide to change how data is stored
Bank might want to start using third party email
sending utility.
Single Responsibility Principle
Refactor
public class TransactionRepository {…. }
public class HTMLMailFormater {….}
public class EmailService {…….. }
Demo
Dependency Inversion
Depend upon abstraction
High level module should not depend upon
low level module implementation, rather
depend upon abstraction
Template pattern
Dependency Inversion
Naïve Example Demo
Dependency Inversion
Problem: All the client must use text file to store
transaction
Sends email in HTML format only
Code in BankAccount class, doesn’t depend
upon storage but still can’t be tested in
isolation.
Why Keep Code Closed To
Modifications
Changing Existing Code is Risky.
Can be Time Consuming in a Non-Refactored code
base.
Code Not Designed on OCP
private string SetDefaultEditableText()
{
StringBuilder editableText = new StringBuilder();
switch ( SurveyManager.CurrentSurvey.TypeID )
{
case 1:
editableText.Append("<p>Text for Survey Type 2 Goes Here</p>");
case 2:
editableText.Append("<p>Text for Survey Type 2 Goes Here</p>");
case 3:
default:
editableText.Append("<p>Text for Survey Type 3 Goes Here</p>");
}
return editableText.ToString();
}
Desigined For Extensibility
Strategy Pattern (Composition focused)
interface ISurveyEmailFormatter{
string GetDefaultEditableText();
}
public SurveyType1EmailFormatter : ISurveyEmailFormatter{
public string GetDefaultEditableText(){
return "<p>Text for Survey Type 1 Goes Here</p>";
}
}
public SurveyType2EmailFormatter : ISurveyEmailFormatter{
public string GetDefaultEditableText(){
return "<p>Text for Survey Type 2 Goes Here</p>";
}
}
public SurveyType3EmailFormatter : ISurveyEmailFormatter{
public string GetDefaultEditableText(){
return "<p>Text for Survey Type 3 Goes Here</p>";
}
}
Strategy Pattern Cont’d
public class Survey{
IEmailFormatter _emailFormatter;
public Survey(IEmailFormmater emailFormater){
_emailFormatter = emailFormatter;
}
public string GetEmailDefaultText(){
return _emailFormatter.GetDefaultEditableText();
}
}
Designed for Extensibility
Template Pattern (Inheritance)
public abstract Survey{
protected abstract string GetDefaultEditableText();
public string GetEmailText()
{
…
string defaultText = GetDefaultEditableText();
…
return somthingHere;
}
}
public SurveyType1{
protected string GetDefaultEditableText()
{
return "<p>Text for Survey Type 1 Goes Here</p>";
}
}
Extensibility Only Goes So far
Change is going to occur and you can’t keep everything
from changing
Must use judgment on which parts of your application
are more likely to change and focus extensibility there.
Law of Demeter
Don’t Talk To Strangers
Methods Should Only Talk To:
Methods and fields on the Object
Parameters passed into the method
Objects Created within the method
Only one “dot”
foo.bar.baz (bad)
Demeter
Breaking this principle breaks encapsulation, and
tightens coupling.
Refactoring much more difficult
Test Setup becomes much more difficult
Interface Segregation Princple
Clients shouldn't be forced to implement
interfaces they don't use
Interface Segregation Princple
Example of FAT or Polluted Interface
Try implementing custom Membership
Provider in ASP.NET 2.0.
You just need to implement 27
methods/properties
Interface Segregation Princple
Interface Idoor
{
void Lock();
void UnLock();
bool IsOpen();
}
Public Class Door : Idoor
{
public void Lock() {}
public void UnLock(){}
bool IsOpen(){}
}
<Demo>
Interface Segregation Princple
Consider one implementation as security door which need to ring
alarm if door is kept open for longer duration.
Interface Idoor
{
void Lock();
void UnLock();
bool IsOpen();
void Timeout();
}
public class SecurityDoor : Idoor
{
void Lock() {}
void UnLock() {}
bool IsOpen() {}
void Timeout() {}
}
<Demo>
Interface Segregation Princple
Problem
All type of door are not timed
By adding Timeout into IDoor we are
polluting our interface
Interface Segregation Princple
public interface ITimerClient
{
void TimeOut();
}
public class SecurityDoor : IDoor, ITimerClient
{
public void TimeOut()
{…….}
}
Demeter
class Wallet
attr_accessor :cash
end
class Customer
has_one :wallet
end
class
Paperboy
def collect_money(customer, due_amount)
if customer.wallet.cash < due_ammount
raise InsufficientFundsError
else customer.wallet.cash
-= due_amount
@collected_amount += due_amount
end
end
end
Demeter
The fix:
class Wallet
attr_accessor :cash
def withdraw(amount)
raise InsufficientFundsError
if amount > cash cash -= amount
amount
end
end
class Customer
has_one :wallet
# behavior delegation def pay(amount)
@wallet.withdraw(amount)
end
end
class Paperboy
def collect_money(customer, due_amount)
@collected_amount += customer.pay(due_amount)
end
end
Liskov Substitution Principle
All sub-classes must be substitutable for their base
class.
Liskov violation
Structural
You should not have to explicitly cast a base object to it’s
subclass to do something.
Behavior
Subtypes should behave in a consistent manner in terms
of their clients
Liskov Example
public class Customer{
FirstName{get;set}
LastName{get;set}
Order GetOrder()
}
public class PreferredCustomer : Customer{
double GetDiscount()
}
//voilation
foreach(Customer c in CustomerList){
if(c is PreferredCustomer){
GetDiscount();
}
}
Liskov
Subtypes should restrict the subclass, not expand it.
Use Composition to solve expansion violations
Bottom line …
SRP
Only one reason to change
DIP
“Don’t call me, I’ll call you”
OCP
Open for extension, Closed for modification
LSP
Sub types should be substitutable for their bases types
LoD
“Don’t talk to strangers”
ISP
Clients should not be forced to depend on methods they do
not use
Remember…
Always code as if the guy
maintaining your code would be a
violent psychopath and he knows
where you live.