CSC/ECE 517 Fall 2012/ch2b 2w70 sm

From Expertiza_Wiki
Jump to navigation Jump to search

Introduction

The Interface Segregation Principle(also referred as ISP) states that "Clients should not be forced to implement interfaces they don't use". Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one submodule.ISP focuses on the cohesiveness of interfaces with respect to the clients that use them.This principle alleviates the disadvantages of "fat" or "polluted" interfaces.The interface-segregation principle (ISP) is one of the five SOLID principles of Object-Oriented Design. ISP is very similar to high cohesion principle of GRASP.

Two main ISP guidelines are: Classes should not be forced to depend on methods that they do not use, and the dependency of one class to another one should depend on the smallest possible interface.

It is a software development principle used for clean development and intends to make software easy-to-change.ISP helps developers to change, refactor and redeploy their code easily.ISP splits interfaces which are very large into smaller and more specific ones so that clients will only have to know about the methods that are of interest to them. In a nutshell, no client should be forced to depend on methods it does not use.Such shrunken interfaces are also called role interfaces.

Motivation

When we design an application we should take care how we are going to abstract a module which contains several submodules. Considering the module implemented by a class, we can have an abstraction of the system done in an interface. But if we want to extend our application adding another module that contains only some of the submodules of the original system, we are forced to implement the full interface and to write some dummy methods. Such an interface is named fat interface or polluted interface. Having an interface pollution is not a good solution and might induce inappropriate behavior in the system.

Origin

The ISP was first used and formulated by Robert C. Martin when doing some consulting for Xerox. Xerox had created a new printer system that could perform a variety of tasks like stapling a set of printed papers and faxing. The software for this system was created from the ground up and performed its tasks successfully. As the software grew, making modification became more and more difficult so that even the smallest change would take a redeployment cycle to an hour. This was making it near impossible to continue development.

The main problem was that one main Job class was used by almost all of the tasks. Anytime a print job or a stapling job had to be done, a call was made to some method in the Job class.This means that the Job class was getting huge or 'fat', full of tons of different methods which were specific to a variety of different clients.Because of this design, a staple job would know about all the methods of the print job, even though there was no use for them.So whenever a developer had to change a small detail about a print job, every one of the classes that used the Job class would have to be recompiled.

The solution suggested by Robert is called the Interface Segregation Principle today. His suggestion was that they add a layer of interfaces between the Job class and all of its clients. All of the dependencies could be reversed using the Dependency Inversion Principle.Instead of having one 'fat' Job class, a Staple Job interface or a Print Job interface was created that would be used by the Staple or Print classes, respectively, calling methods of the Job class.Therefore one interface is created for each job, and the Job class would inherit from all of these interfaces. This segregation of interfaces vastly decoupled the software allowing the clients like the Staple class to only depend on the methods it cared about.So when a developer made a change to the Print Job, the Staple Job would be unaffected and have no need to recompile.

Violation

The Xerox example is a clear violation of the Interface Segregation Principle, but not all violations are so clear cut. A more commonly known example is the ATM Transaction example given in Agile Software Development: Principles, Patterns, and Practices [1] and in an article also written by Robert C. Martin specifically about the ISP.[5] This example is about an interface for the User Interface for an ATM, that handles all requests such as a deposit request, or a withdrawal request, and how this interface needs to be segregated into individual and more specific interfaces.

Examples

Example 1

Some interfaces are created with huge amount of functionalities. But when those interfaces are implemented by client implementor classes, not required functionalities are forced to be implemented, and so the code will have many dummy/empty implementations. This situation can be prevented by seperating big interfaces into smaller ones. Only strictly related method definitions must be in the same interface. For a better design and usage,different types of functionalities must be placed in different interfaces. Below is a wrong design that's not using Interface Segregation Principle:

public interface Animal {
   void fly();
   void run();
   void bark();
}
public class Bird implements Animal {
   public void bark() { /* do nothing */ }
   public void run() {
       // write code about running of the bird
   }
   public void fly() {
       // write code about flying of the bird
   }
}
public class Cat implements Animal {
   public void fly() { throw new Exception("Undefined cat property"); }
   public void bark() { throw new Exception("Undefined cat property"); }
   public void run() {
       // write code about running of the cat
   }
}
public class Dog implements Animal {
   public void fly() { }
   public void bark() {  
       // write code about barking of the dog
   }
   public void run() {
       // write code about running of the dog
   }
}

This above example uses interfaces where three different functions are defined but few are not being used.Below is the implementation which uses ISP:

public interface Flyable {
   void fly();
}
public interface Runnable {
   void run();
}
public interface Barkable {
   void bark();
}
public class Bird implements Flyable, Runnable {
   public void run() {
       // write code about running of the bird
   }
   public void fly() {
       // write code about flying of the bird
   } 
}
public class Cat implements Runnable{
   public void run() {
       // write code about running of the cat
   }
}
public class Dog implements Runnable, Barkable {
   public void bark() {  
       // write code about barking of the dog
   }
   public void run() {
       // write code about running of the dog
   }
}

Example2

The following code violates ISP shows the outline of three classes:

public class Contact
{
   public string Name { get; set; }
   public string Address { get; set; }
   public string EmailAddress { get; set; }
   public string Telephone { get; set; }
}

    
public class Emailer
{
   public void SendMessage(Contact contact, string subject, string body)
   {
       // Code to send email, using contact's email address and name
   }
}

    
public class Dialler
{
   public void MakeCall(Contact contact)
   {
       // Code to dial telephone number of contact
   }
}

The Contact class represents a person or business that can be contacted. The class holds the person's name, address, email address and telephone number. The Emailer class sends email messages to contacts. The contact and the subject and body of the email are passed to the parameters. The Dialler class extracts the telephone number from the Contact and calls it using an automatic dialling system. The Emailer class is a client of the Contact class. Although it only requires access to the Name and EmailAddress properties, it is aware of other members too. Similarly, the Dialler class uses a single property, "Telephone". However, it has access to the entire Contact interface.

To demonstrate the application of the ISP, we can review some code that violates it and explain how to refactor to comply with the principle. The following code shows the outline of three classes:

public class Contact
{
   public string Name { get; set; }
   public string Address { get; set; }
   public string EmailAddress { get; set; }
   public string Telephone { get; set; }
}

    
public class Emailer
{
   public void SendMessage(Contact contact, string subject, string body)
   {
       // Code to send email, using contact's email address and name
   }
}

    
public class Dialler
{
   public void MakeCall(Contact contact)
   {
       // Code to dial telephone number of contact
   }
}

The Contact class represents a person or business that can be contacted. The class holds the person's name, address, email address and telephone number. The Emailer class sends email messages to contacts. The contact and the subject and body of the email are passed to the parameters. The Dialler class extracts the telephone number from the Contact and calls it using an automatic dialling system.

The example code violates the ISP. The Emailer class is a client of the Contact class. Although it only requires access to the Name and EmailAddress properties, it is aware of other members too. Similarly, the Dialler class uses a single property, "Telephone". However, it has access to the entire Contact interface.

To refactor the code to comply with the ISP we need to hide unused members from the client classes. We can achieve this by introducing two new interfaces, both implemented by Contact. The IEmailable interface defines properties that hold the name and email address of an object that can receive email. The IDiallable interface includes only a Telephone property, which is enough to allow client classes to call the telephone number of a target object.

The Email class is updated, replacing the Contact dependency with an IEmailable object. Similarly, the Dialler's dependency becomes an IDiallable instance. Both classes now interact with contacts using the smallest possible interface.

With smaller interfaces it is easier to introduce new classes that implement them. To demonstrate, the refactored code includes a new class named "MobileEngineer". This represents engineers that visit customer sites. Engineer has properties for a name, telephone number and vehicle registration. The class implements IDiallable so that the Dialler object can call engineers.


public interface IEmailable
{
   string Name { get; set; }
   string EmailAddress { get; set; }
}


public interface IDiallable
{
   string Telephone { get; set; }
}


public class Contact : IEmailable, IDiallable
{
   public string Name { get; set; }
   public string Address { get; set; }
   public string EmailAddress { get; set; }
   public string Telephone { get; set; }
}


public class MobileEngineer : IDiallable
{
   public string Name { get; set; }
   public string Vehicle { get; set; }
   public string Telephone { get; set; }
}


public class Emailer
{
   public void SendMessage(IEmailable target, string subject, string body)
   {
       // Code to send email, using target's email address and name
   }
}


public class Dialler
{
   public void MakeCall(IDiallable target)
   {
       // Code to dial telephone number of target
   }
}

Related Concepts

The following concepts are related to this one:

  • Low coupling--Coupling refers to the relationship of a module with another module. A module is said to be highly coupled with another module if changes to it will result to changes to the other module. And a module is said to be loosely coupled if a module is independent of any other modules. This can be achieved by having a stable interface that effectively hides the implementation of another module.

<t>Benefits of low coupling are:<\t>

1.maintainability – changes are confined in a single module

2.testability – modules involved in unit testing can be limited to a minimum

3.readability – classes that need to be analyzed are kept at a minimum

  • High cohesion--Cohesion refers to the measure of how strongly-related the functions of a module are. Low cohesion refers to modules that have different unrelated responsibilities. High cohesion refers to modules that have functions that are similar in many aspects.

Benefits of high cohesion are:

1.Readability – (closely) related functions are contained in a single module

2.Maintainability – debugging tends to be contained in a single module

3.Reusability – classes that have concentrated functionalities are not polluted with useless functions

  • Liskov Substitution Principle (LSP)--Liskov Substitution principle (LSP) states that "Methods that use references to the base classes must be able to use the objects of the derived classes without knowing it".This principle was written by Barbara Liskov in 1988.The idea here is that the subtypes must be replaceable for the super type references without affecting the program execution.

References

http://codebalance.blogspot.com/2010/09/oop-solid-rules-interface-segregation.html

http://en.wikipedia.org/wiki/Interface_segregation_principle

http://500internalservererror.wordpress.com/2009/02/23/what-do-low-coupling-and-high-cohesion-mean-what-does-the-principle-of-encapsulation-mean/

http://blog.sanaulla.info/2011/11/28/solid-liskov-substitution-principle/

http://www.blackwasp.co.uk/ISP.aspx

http://en.wikipedia.org/wiki/User:LupusDei108/Interface_Segregation_Principle