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.