You are on page 1of 28

Content

n Part 1: GRASP
13. Design principles: GRASP n Part 2: SOLID
& SOLID

1 2

1 2

GRASP GRASP
n GRASP: General Responsibility Assignment 9 patterns and principles used in GRASP:
Information expert
Software Patterns (or Principles) 1.

2. Creator
n GRASP consist of guidelines for assigning 3. Controller
Low coupling
responsibility to classes and objects 4.

5. High cohesion
in object-oriented design. 6. Indirection
n GRASP is not related to the SOLID design 7. Polymorphism
8. Pure fabrication
principle. 9. Protected variations- Don’t Talk to Strangers (Law of Demeter)

Craig Larman: Apply UML and Patterns – An


introduction to Object-Oriented Analysis and
3 Design 4

3 4

1
Information Expert Example for Expert
n Given an object o, which responsibilities can n Assume we need to get all the videos of a
be assigned to o? VideoStore.
n Expert principle says – assign those n Since VideoStore knows about all the videos,
responsibilities to o for which o has the we can assign this responsibility of giving all
information to fulfill that responsibility. the videos can be assigned to VideoStore
n They have all the information needed to class.
perform operations, or in some cases they n VideoStore is the information expert.
collaborate with others to fulfill their
responsibilities.
5 6

5 6

Example for Expert Example for Expert

7 8

7 8

2
Creator Example for Creator
n Who creates an Object? Or who should create a n Consider VideoStore and Video in that store.
new instance of some class?
n VideoStore has an aggregation association
n Container” object creates “contained” objects. with Video. I.e, VideoStore is the container
n Decide who can be creator based on the objects and the Video is the contained object.
association and their interaction: Assign class B the
responsibility to create object A if one of these is n So, we can instantiate video object in
true (more is better) VideoStore class
n B contains or compositely aggregates A
n B records A
n B closely uses A
n B has the initializing data for A
9 10

9 10

Example diagram Example for creator

11 12

11 12

3
Controller Controller
n Deals with how to delegate the request from the UI layer n We can make an object as Controller, if
objects to domain layer objects.
n Object represents the overall system (facade
n when a request comes from UI layer object, Controller
pattern helps us in determining what is that first object that controller)
receive the message from the UI layer objects. n Object represent a use case, handling a
n This object is called controller object which receives request sequence of operations (use case or session
from UI layer object and then controls/coordinates with other controller).
object of the domain layer to fulfill the request.
n It delegates the work to other class and coordinates the
n Benefits
overall activity n can reuse this controller class.
n Can use to maintain the state of the use case.
n Can control the sequence of the activities
13 14

13 14

Example for Controller Bloated Controllers


n Controller class is called bloated, if
n The class is overloaded with too many
responsibilities.
n Solution – Add more controllers
n Controller class also performing many tasks
instead of delegating to other class.
n Solution – controller class has to delegate things to
others.

15 16

15 16

4
Low Coupling Low coupling
n How strongly the objects are connected to each other? Two elements are coupled, if
Coupling – object depending on other object.
n
n One element has aggregation/composition
When depended upon element changes, it affects the
n

dependant also.
association with another element.
n Low Coupling – How can we reduce the impact of change in n One element implements/extends other element.
depended upon elements on dependant elements.
n Prefer low coupling – assign responsibilities so that coupling
remain low.
n Minimizes the dependency hence making system
maintainable, efficient and code reusable

17 18

17 18

Example for poor coupling Example for low coupling


n Here class Rent knows about both VideoStore and n VideoStore and Video class are coupled, and Rent is
Video objects. Rent is depending on both the coupled with VideoStore. Thus providing low coupling
classes.

19 20

19 20

5
High Cohesion Example for low cohesion
n How are the operations of any element are
functionally related?
n Related responsibilities in to one manageable unit.
n Prefer high cohesion
n Clearly defines the purpose of the element
n Benefits
n Easily understandable and maintainable.
n Code reuse
n Low coupling

21 22

21 22

Example for High Cohesion Indirection


n How can we avoid a direct coupling between
two or more elements.
n Indirection introduces an intermediate unit to
communicate between the other units, so that
the other units are not directly coupled.
n Benefits: low coupling
n Example: Adapter, Facade, Obserever

23 24

23 24

6
Example for Indirection Polymorphism
n Here polymorphism illustrates indirection n How to handle related but varying elements
n Class Employee provides a level of based on element type?
indirection to other units of the system n Polymorphism guides us in deciding which
object is responsible for handling those
varying elements.
n Benefits: handling new variations will become
easy.

25 26

25 26

Example for Polymorphism Pure Fabrication


n the getArea() varies by the type of shape, so we assign that n Problem: Who is responsible when you are
responsibility to the subclasses.
desperate, and do not want to violate high
cohesion and low coupling
n Solution: Assign a highly cohesive set of
responsibilities to an artificial or convenience
"behavior” class that does not represent a
problem domain concept — something made
By sending message to the Shape object, a call will be
n

made to the corresponding sub class object – Circle or


up, in order to support high cohesion, low
Triangle coupling, and reuse.
27 28

27 28

7
Example 1 Example 1
n We need to save Sale instances in a relational n Solution: create a new class (PersistentStorage) that is
database. By Information Expert, we assign this solely responsible for saving objects
responsibility to the Sale class itself, because the n Advantages:
The Sale remains well-designed, with high cohesion and low coupling.
sale has the data that needs to be saved. But n

n The PersistentStorage class is relatively cohesive, having the sole


n the Sale class becomes incohesive.
purpose of storing or inserting objects in a persistent storage medium.
n The Sale class has to be coupled to the relational database interface n The PersistentStorage class is a very generic and reusable object.
(such as JDBC in Java technologies), so its coupling goes up.
n Saving objects in a relational database is a very general task. Placing
these responsibilities in the Sale class suggests there is going to be
poor reuse or lots of duplication in other classes that do the same
thing.

29 30

29 30

Example 2 Protected Variation


interface IForeignExchange {
List<ConversionRate> getConversionRates();
}
class ConversionRate{
private String from;
n Problem: How to design objects, subsystems
private String to;
private double rate;
and systems so that the variations or
public ConversionRate(String from, String to, double rate) {
this.from = from; instability in these elements does not have an
this.to = to;
this.rate = rate; undesirable impact on other elements?
}
}
public class ForeignExchange implements IForeignExchange { n Solution: Identify points of predicted variation
public List<ConversionRate> getConversionRates() {
List<ConversionRate> rates = ForeignExchange.getConversionRatesFromExternalApi(); or instability, assign responsibilities to create
return rates;
}
private static List<ConversionRate> getConversionRatesFromExternalApi() {
a stable interface around them.
// Communication with external API. Here is only mock.
List<ConversionRate> conversionRates = new ArrayList<ConversionRate>();
conversionRates.add(new ConversionRate("USD", "EUR", 0.88));
conversionRates.add(new ConversionRate("EUR", "USD", 1.13));
return conversionRates;
}
} 31 32

31 32

8
Example for Protected Variation (1/3) Example for Protected Variation (2/3)

33 34

33 34

Example for Protected Variation (1/3)

Law of Demeter

35 36

35 36

9
Law of Demeter Law of Demeter
Karl Lieberherr i and colleagues

n Law of Demeter: An object should know as little as n A method "M" of an object "O" should invoke
possible about the internal structure of other objects only the the methods of the following kinds of
with which it interacts – a question of coupling objects:
n Or… “only talk to your immediate friends”
n itself
n Closely related to representation exposure and
(im)mutability n its parameters
n Bad example – too-tight chain of coupling between n any objects it creates/instantiates
classes n its direct component objects
general.getColonel().getMajor(m).getCaptain(cap)
.getSergeant(ser).getPrivate(name).digFoxHole();
Guidelines: not strict rules!
n Better example But thinking about them
general.superviseFoxHole(m, cap, ser, name);
will generally help you
37 produce better designs 38

37 38

Without Law of Demeter? Law of Demeter – benefits


objectA.getObjectB().getObjectC().doSomething(); n Your classes will be "loosely coupled"; your
n In the future, the class ObjectA may no longer need dependencies are reduced.
to carry a reference to ObjectB. n Reusing your classes will be easier.
n In the future, the class ObjectB may no longer need

to carry a reference to ObjectC. n Your classes are less subject to changes in


n The doSomething() method in the ObjectC class
other classes.
may go away, or change. n Your code will be easier to test.
n If the intent of your class is to be reusable, you can n Classes designed this way have been proven
never reuse your class without also requiring to have fewer errors.
ObjectA, ObjectB, and ObjectC to be shipped with
your class.
39 40

39 40

10
Example of LoD Example of LoD
public class Customer { public class Wallet {
private String firstName; private float value;
private String lastName; public float getTotalMoney() {
private Wallet myWallet; return value;
}
public String getFirstName(){ public void setTotalMoney(float newValue) {
return firstName; value = newValue;
} }
public String getLastName(){ public void addMoney(float deposit) {
return lastName; value += deposit;
} }
public Wallet getWallet(){ public void subtractMoney(float debit) {
return myWallet; value -= debit;
} }
} }

41 42

41 42

Example of LoD Example of LoD – Improvement

// code from some method inside the Paperboy class... public class Customer {
private String firstName;
payment = 2.00; // “I want my two dollars!” private String lastName;
private Wallet myWallet;
Wallet theWallet = myCustomer.getWallet(); public String getFirstName(){
if (theWallet.getTotalMoney() > payment) { return firstName;
}
theWallet.subtractMoney(payment); public String getLastName(){
return lastName;
} else { }
// come back later and get my money public float getPayment(float bill) {
if (myWallet != null) {
} if (myWallet.getTotalMoney() > bill) {
theWallet.subtractMoney(payment);
return payment;
Is this Bad? Why? }
}
}
}
43 44

43 44

11
Example of LoD – Improvement Another example of LoD
// code from some method inside the Paperboy class... public class Band {
payment = 2.00; // “I want my two dollars!”
paidAmount = myCustomer.getPayment(payment);
private Singer singer;
if (paidAmount == payment) { private Drummer drummer;
// say thank you and give customer a receipt private Guitarist guitarist;
} else {
// come back later and get my money
}
}

Why Is This Better?

45 46

45 46

Another example of LoD Another example of LoD – Improvement

class TourPromoter { public class Band {


public String makePosterText(Band band) {
String guitaristsName = band.getGuitarist().getName(); private Singer singer;
String drummersName = band.getDrummer().getName(); private Drummer drummer;
String singersName = band.getSinger().getName();
StringBuilder posterText = new StringBuilder(); private Guitarist guitarist;
posterText.append(band.getName()
posterText.append(" featuring: "); public String[] getMembers() {
posterText.append(guitaristsName);
posterText.append(", "); return {
posterText.append(singersName); singer.getName(),
posterText.append(", ")
posterText.append(drummersName); drummer.getName(),
posterText.append(", ") guitarist.getName()};
posterText.append("Tickets £50.");
}
return posterText.toString();
} }
}
47 48

47 48

12
Another example of LoD – Improvement

public class TourPromoter {


public String makePosterText(Band band) {
StringBuilder posterText = new StringBuilder();
SOLID
posterText.append(band.getName());
posterText.append(" featuring: ");
for(String member: band.getMembers()) {
posterText.append(member);
posterText.append(", ");
}
posterText.append("Tickets: £50");

return posterText.toString();
}
}

49

49 50

SOLID 1. Nguyên lý một nhiệm vụ


Single-responsibility Principle
“Principles Of OOD”, Robert C. Martin (“Uncle n Every class or module in a program should
BOB”) have responsibility for just a single piece of
n S – Single-responsiblity principle that program’s functionality, or
n O – Open-closed principle n A class should have only one reason to
change.
n L – Liskov substitution principle

n I – Interface segregation principle


n Discussion:
n Why should classes have a single responsibility?
n D – Dependency Inversion Principle
n Does this mean class should have a single
method?
51 52

51 52

13
Example 1 – UserSettingService Example 1 - Refactored code
package test; public class UserSettingService {
public class UserSettingService { public void changeEmail(User user) {
public void changeEmail(User user) { if(SecurityService.checkAccess(user)) {
//Grant option to change
if(checkAccess(user)) {
}
//Grant option to change }
} }
}
public boolean checkAccess(User user) {
public class SecurityService {
//Verify if the user is valid.
public boolean checkAccess(User user) {
}
//check the access.
} }
}

53 54

53 54

Example 2 – Employee Example 2 - Refactored code


public class Employee{ public class HRPromotions{
private String employeeId; public boolean isPromotionDueThisYear(Employee emp){
/*promotion logic implementation using the employee information passed*/
private String name; }
private string address; }
private Date dateOfJoining;
public class FinITCalculations{
public boolean isPromotionDueThisYear(){ public Double calcIncomeTaxForCurrentYear(Employee emp){
//promotion logic implementation //income tax logic implementation using the employee information passed
} }
}
public Double calcIncomeTaxForCurrentYear(){
//income tax logic implementation public class Employee{
} private String employeeId;
private String name;
//Getters & Setters for all the private attributes private string address;
} private Date dateOfJoining;
//Getters & Setters for all the private attributes
}
55

55 56

14
2. Nguyên lý đóng mở Example 1 - HealthInsuranceSurveyor
Open-closed Principle
n Software entities (classes, modules, functions, etc.) public class HealthInsuranceSurveyor{
should be open for extension, but closed for public boolean isValidClaim(){
modification System.out.println("Validating ...");
n We should strive to write code that doesn’t have to /*Logic to validate health insurance claims*/
be changed every time the requirements change
return true;
n “Open for extension”: A module (class) should
}
provide extension point to change its behaviors
}
n Closed for modification”: we should not have to
change old code in order to implement new
behavior (old code is untouched and therefore
avoiding cascading breakage)
57 58

57 58

ClaimApprovalManager ClaimApprovalManager
public class ClaimApprovalManager { public class ClaimApprovalManager {
public void processHealthClaim (HealthInsuranceSurveyor surveyor) { public void processHealthClaim (HealthInsuranceSurveyor surveyor)
{
if(surveyor.isValidClaim()){
if(surveyor.isValidClaim()){
System.out.println("Valid claim. Processing claim for approval...."); System.out.println("Valid claim. Processing ...");
} }
} }
} public void processVehicleClaim (VehicleInsuranceSurveyor surveyor)
{
if(surveyor.isValidClaim()){
System.out.println("Valid claim. Processing ...");
}
}
}

59 60

59 60

15
Refactored code ClaimApprovalManager
public abstract class InsuranceSurveyor { public class ClaimApprovalManager {
public abstract boolean isValidClaim();
} public void processClaim(InsuranceSurveyor surveyor){
public class HealthInsuranceSurveyor extends InsuranceSurveyor{ if(surveyor.isValidClaim()){
public boolean isValidClaim(){
System.out.println("HealthInsuranceSurveyor: Validating claim...");
System.out.println("Valid claim. Processing ...");
/*Logic to validate health insurance claims*/ }
return true;
} }
}
}

public class VehicleInsuranceSurveyor extends InsuranceSurveyor{


public boolean isValidClaim(){
System.out.println("VehicleInsuranceSurveyor: Validating claim...");
/*Logic to validate vehicle insurance claims*/
return true;
}
} 61 62

61 62

ClaimApprovalManagerTest Example 2
public class ClaimApprovalManagerTest { public class Rectangle{
@Test
public void testProcessClaim() throws Exception { private double length;
HealthInsuranceSurveyor healthInsuranceSurveyor = private double width;
new HealthInsuranceSurveyor(); }
ClaimApprovalManager claim = new ClaimApprovalManager();
claim1.processClaim(healthInsuranceSurveyor);
public class AreaCalculator{
public double calculateRectangleArea(Rectangle rectangle) {
VehicleInsuranceSurveyor vehicleInsuranceSurveyor = return rectangle.getLength() *rectangle.getWidth();
new VehicleInsuranceSurveyor();
ClaimApprovalManager claim2 = new ClaimApprovalManager(); }
claim2.processClaim(vehicleInsuranceSurveyor); }
}
}

63 64

63 64

16
To add a new shape (Circle) Refactored code
public class Circle{ public interface Shape{
private double radius; public double calculateArea();
}
}
public class Rectangle implements Shape{
double length;
public class AreaCalculator{ double width;
public double calculateRectangleArea(Rectangle rectangle){ public double calculateArea(){
return rectangle.getLength() *rectangle.getWidth(); return length * width;
} }
}
public double calculateCircleArea(Circle circle){
return 3.14159*circle.getRadius()*circle.getRadius(); public class Circle implements Shape{
} public double radius;
} public double calculateArea(){
return 3.14159 *radius*radius;
}
}
65 66

65 66

AreaCalculator 3. Nguyên lý thay thế Liskov


Liskov substitution principle
public class AreaCalculator{ n “Let q(x) be a property provable about
public double calculateShapeArea(Shape shape){ objects of x of type T. Then q(y) should be
return shape.calculateArea(); provable for objects y of type S where S is a
} subtype of T”
} n Functions that use pointers of references to
base classes must be able to use objects of
derived classes without knowing it
n New derived classes are extending the base
classes without changing their behavior
67 68

67 68

17
Liskov substitution principle Example – Rectangle
void clientMethod(SomeClass sc) { class Rectangle {
protected int m_width;
… protected int m_height;
sc.someMethod();
… 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;
}
69 } 70

69 70

Square Test
class Square extends Rectangle { public class RectangleFactory {
public static Rectangle generate(){
@override return new Square(); // if we return new Rectangle(), everything is fine
public void setWidth(int width){ }
}
m_width = width;
m_height = width; class LspTest {
public static void main (String args[]) {
} Rectangle r = RectangleFactory.generate();
@override
r.setWidth(5);
public void setHeight(int height){ r.setHeight(10);
m_width = height; // user knows that r it's a rectangle.
m_height = height; // 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.
}
}
71 72

71 72

18
Refactored code – Shape Rectangle
public abstract class Shape { public class Rectangle extends Shape {
protected int mHeight; @Override
protected int mWidth; public int getWidth() {
return mWidth;
public abstract int getWidth(); }

public abstract void setWidth(int inWidth); @Override


public int getHeight() {
return mHeight;
public abstract int getHeight(); }
public abstract void setHeight(int inHeight); @Override
public void setWidth(int inWidth) {
public int getArea() { mWidth = inWidth;
return mHeight * mWidth; }
}
} @Override
public void setHeight(int inHeight) {
mHeight = inHeight;
73
} 74
}

73 74

Square Test
public class Square extends Shape {
public class ShapeFactory {
@Override
public int getWidth() { public static Shape generate(){
return mWidth; return new Square();
} }
@Override }
public void setWidth(int inWidth) {
SetWidthAndHeight(inWidth); class LspTest {
} public static void main (String args[]) {
@Override
Shape s = ShapeFactory.generate();
public int getHeight() {
return mHeight;
} s.setWidth(5);
@Override s.setHeight(10);
public void setHeight(int inHeight) {
SetWidthAndHeight(inHeight); System.out.println(r.getArea());
} }
private void setWidthAndHeight(int inValue) {
mHeight = inValue; }
mWidth = inValue;
} 75 76
}

75 76

19
Optimized solution Example 2 – Project
public class Project {
public ArrayList<ProjectFile> projectFiles;

public void loadAllFiles() {


for (ProjectFile file: projectFiles) {
file.loadFileData();
}
}

public void saveAllFiles() {


for (ProjectFile file: projectFiles) {
file.saveFileData();
}
}
}

77 78

77 78

ProjectFile ReadOnlyFile
public class ProjectFile { public class ReadOnlyFile extends ProjectFile {
public string filePath;
@Override
public void saveFileData() throws new InvalidOPException {
public byte[] fileData;
throw new InvalidOPException();
public void loadFileData() { }
// Retrieve FileData from disk }
}

public void saveFileData() {


// Write FileData to disk
}
}

79 80

79 80

20
Project Project
public class Project { public class Project {
public ArrayList<ProjectFile> projectFiles; public ArrayList<ProjectFile> allFiles;
public ArrayList<WritableFile> writableFiles ;
public void loadAllFiles() { public void loadAllFiles() {
for (ProjectFile file: projectFiles) { for (ProjectFile file: allFiles) {
file.loadFileData(); file.loadFileData();
} }
}
}
public void saveAllFiles() {
public void saveAllFiles() { for (ProjectFile file: writableFiles) {
for (ProjectFile file: projectFiles) { file.saveFileData();
if (!file instanceOf ReadOnlyFile) }
}
file.saveFileData(); }
}
}
} 81 82

81 82

ProjectFile WritableFile
public class ProjectFile { public class WritableFile extends ProjectFile {
public string filePath; public void saveFileData() {
// Write FileData to disk
public byte[] fileData; }
}
public void loadFileData() {
// Retrieve FileData from disk
}
} 83 84

83 84

21
Discussion Example
n Does method overriding break the Liskov public class Report{ 3 subclasses of Report
substitution principle?
private Foo foo; 1. HTMLReport
public String toString(){ 2. XMLReport
return ""; 3. TextReport
}
}
n Contract:
n toString() returns a string, which is the representation of Foo in
some format (HTML, XML, Text) (and return the empty string if
the format is undefined).
n toString() shall not mutate the Report object
n toString() shall never throw an Exception

85 86

85 86

4. Nguyên lý phân tách giao diện Example 1 – Toy


Interface Segregation Principle
n An interface should NOT have methods that its public interface Toy {
implementing classes do not need (Client should NOT be
forced to depend on methods it does not use) void setPrice(double price);
n “Fat interface” should be split into smaller and more specific void setColor(String color);
ones so that clients will only have to know about the
methods that are of interest to them void move();
n Similar to the Single Responsibility Principle, the goal of the void fly();
Interface Segregation Principle is to reduce the side effects
and frequency of required changes by splitting the software }
into multiple, independent parts

87 88

87 88

22
public class ToyCar implements Toy, Movable {
double price;
ToyHouse
String color;
public class ToyHouse implements Toy {
@Override double price;
public void setPrice(double price) { String color;
this.price = price;
} @Override
@Override public void setPrice(double price) {
public void setColor(String color) { this.price = price;
this.color=color; }
} @Override
@Override public void setColor(String color) {
public void move(){ this.color=color;
System.out.println("ToyCar: Start moving car."); }
} @Override
@Override public void move(){}
public String toString(){ @Override
return "ToyCar: Moveable Toy car- Price: "+price+" Color: "+color; public void fly(){}
} }
} 89 90

89 90

Refactored code ToyHouse


public interface Toy { public class ToyHouse implements Toy {
void setPrice(double price); double price;
String color;
void setColor(String color);
} @Override
public void setPrice(double price) {
this.price = price;
public interface Movable { }
void move(); @Override
} public void setColor(String color) {
this.color=color;
}
@Override
public interface Flyable { public String toString(){
void fly(); return "ToyHouse: Toy house- Price: "+price+" Color: "+color;
} }
}
91 92

91 92

23
public class ToyBuilder {
public class ToyPlane implements Toy, Movable, Flyable {
double price; public static ToyHouse buildToyHouse(){
String color; ToyHouse toyHouse=new ToyHouse();
toyHouse.setPrice(15.00);
@Override toyHouse.setColor("green");
public void setPrice(double price) { return toyHouse;
this.price = price; }
} public static ToyCar buildToyCar(){
@Override ToyCar toyCar=new ToyCar();
public void setColor(String color) {
toyCar.setPrice(25.00);
this.color=color;
} toyCar.setColor("red");
@Override toyCar.move();
public void move(){ return toyCar;
System.out.println("ToyPlane: Start moving plane."); }
} public static ToyPlane buildToyPlane(){
@Override ToyPlane toyPlane=new ToyPlane();
public void fly(){ toyPlane.setPrice(125.00);
System.out.println("ToyPlane: Start flying plane."); toyPlane.setColor("white");
} toyPlane.move();
@Override
toyPlane.fly();
public String toString(){
return "ToyPlane: Moveable and flyable toy plane- Price: "+price+" Color: "+color; return toyPlane;
} }
93 } 94
}

93 94

Interface Segregation Principle and Single Example 2 – RestaurantInterface


Responsibility Principle

n Share the same goal: ensuring small, public interface RestaurantInterface {


focused, and highly cohesive software public void acceptOnlineOrder();
components. public void takeTelephoneOrder();
n Single Responsibility Principle is concerned public void payOnline();
with classes
public void walkInCustomerOrder();
n Interface Segregation Principle is concerned
public void payInPerson();
with interfaces
}

95 96

95 96

24
public class OnlineClientImpl implements RestaurantInterface {
@Override
5. Nguyên lý đảo ngược sự phụ thuộc
public void acceptOnlineOrder() {
// logic for placing online order
Dependency Inversion Principle
}
n We should avoid tightly coupled code
@Override
public void takeTelephoneOrder() { // Not Applicable for Online Order n Conventional application architecture follows a top-
throw new UnsupportedOperationException();
} down design approach where a high-level problem
@Override is broken into smaller parts à high-level modules
public void payOnline() {
// logic for paying online
that gets written directly depends on low-level
} modules
@Override n A. High-level modules should not depend on low-level
public void walkInCustomerOrder() { // Not Applicable for Online Order
throw new UnsupportedOperationException();
modules. Both should depend on abstractions.
} n B. Abstractions should not depend on details. Details
@Override should depend on abstractions”
public void payInPerson() { // Not Applicable for Online Order
throw new UnsupportedOperationException();
}
97
}

97 99

5. Dependency Inversion Principle Example – LightBulb


public class LightBulb {
public void turnOn() {
System.out.println("LightBulb: Bulb turned on...");
}
public void turnOff() {
System.out.println("LightBulb: Bulb turned off...");
}
}

100 101

100 101

25
ElectricPowerSwitch Interface ISwitchable
public class ElectricPowerSwitch {
public LightBulb lightBulb; public interface ISwitchable {
public boolean on;
public ElectricPowerSwitch(LightBulb lightBulb) {
this.lightBulb = lightBulb;
public void turnOn();
this.on = false;
} public void turnOff();
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;
}
}
}
102 103

102 103

ElectricPowerSwitch LightBulb
public class ElectricPowerSwitch { public class LightBulb implements ISwitchable {
public ISwitchable client;
public boolean on; @Override
public ElectricPowerSwitch(ISwitchable client) { public void turnOn() {
this.client = client;
this.on = false; System.out.println("LightBulb: Bulb turned on...");
}
public boolean isOn() {
}
return this.on;
} @Override
public void press(){
boolean checkOn = isOn(); public void turnOff() {
if (checkOn) {
client.turnOff();
System.out.println("LightBulb: Bulb turned off...");
this.on = false; }
} else {
client.turnOn();
}
this.on = true;
}
}
}
104 105

104 105

26
Fan ElectricPowerSwitchTest
public class Fan implements ISwitchable { public class ElectricPowerSwitchTest {

@Override @Test
public void testPress() throws Exception {
public void turnOn() { ISwitchable switchableBulb=new LightBulb();
System.out.println("Fan: Fan turned on..."); ElectricPowerSwitch bulbPowerSwitch =
new ElectricPowerSwitch(switchableBulb);
} bulbPowerSwitch.press();
bulbPowerSwitch.press();

@Override ISwitchable switchableFan=new Fan();


ElectricPowerSwitch fanPowerSwitch =
public void turnOff() { new ElectricPowerSwitch(switchableFan);
System.out.println("Fan: Fan turned off..."); fanPowerSwitch.press();
fanPowerSwitch.press();
} }
}
}

106 107

106 107

ElectricPowerSwitch ISwitch
public class ElectricPowerSwitch {
public ISwitchable client; public interface ISwitch {
public boolean on;
public ElectricPowerSwitch(ISwitchable client) {
this.client = client;
boolean isOn();
this.on = false;
} void press();
public boolean isOn() {
return this.on; Any problem? }
}
public void press(){
boolean checkOn = isOn();
if (checkOn) {
client.turnOff();
this.on = false;
} else {
client.turnOn();
this.on = true;
}
}
}
108 109

108 109

27
ElectricPowerSwitch
public class ElectricPowerSwitch implements ISwitch {
public ISwitchable client;
public boolean on;
public ElectricPowerSwitch(ISwitchable 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;
}
}
}
110

110

28

You might also like