CSC/ECE 517 Fall 2010/ch6 6f AZ: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 52: Line 52:


In the below example you see one class CarConfigurator interface implementing interface ICarConfigurator that is being used by three different classes; DrawExterior, DrawInterior and DrawEngine. All these different classes are using different functionality of the ICarConfigurator interface.This means that all of these classes are connected to each other and if there is a change for one class in the interface then this will affect all of them.
In the below example you see one class CarConfigurator interface implementing interface ICarConfigurator that is being used by three different classes; DrawExterior, DrawInterior and DrawEngine. All these different classes are using different functionality of the ICarConfigurator interface.This means that all of these classes are connected to each other and if there is a change for one class in the interface then this will affect all of them.
<pre>
Interface: ICarConfigurator
namespace ISP
{
  public interface ICarConfigurator
  {
      string ExteriorColor { get; set; }
      string InteriorColor { get; set; }
      string EngineType { get; set; }
  }
}
</pre>


==Example3==
==Example3==

Revision as of 14:17, 15 November 2010

Introduction

Interface Segregation Principle(ISP) states that "Clients should not be forced to depend upon interfaces that they do not use". Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one submodule.

If followed, the ISP will help a system stay decoupled and thus easier to refactor, change, and redeploy. It is one of the 5 principles of Object-Oriented Programming called SOLID. This helps in low coupling and high cohesion.

Origin

The ISP was first used and formulated by Robert C. Martin when he was doing some consulting for Xerox. Xerox had created a new printer system which could perform a variety of tasks such as stapling a set of printed papers, faxing, and so forth. The software for this system was created from the ground up and performed its tasks successfully, but as the software grew it became harder and harder to change. Each time a change, even the smallest of changes, was made it could take nearly an hour to recompile and redeploy. This was making it near impossible to continue development, so they hired Robert to help them out.

The issue with their code was that there was one main Job class that was used by almost all of the tasks. Anytime a print job or a stapling had to be done, a call was made to some method in the Job class. This meant that the Job class was getting huge or 'fat', with of tons of different methods which were specific to a variety of different clients. This meant that a staple job would know about all the methods of the print job, even though the staple class had no use for them. The clients were being forced to depend on methods they did not use. When 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 has now become the Interface Segregation Principle. He suggested that they add a layer of interfaces to sit between the Job class and all of its clients. Instead of having just one 'fat' Job class that all the tasks used, there would be a Staple Job interface or a Print Job interface that would be used by the Staple class or Print class, respectively, and would call methods of the Job class. Ergo, there was an interface 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. Thus if a developer made a change to the Print Job, the Staple Job would be unaffected and have no need to recompile.

Motivation

When we design an application we should take care how we are going to make 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 by 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 as a ‘fat’ interface or ‘polluted’ interface. Having a polluted interface is not a good solution and might induce inappropriate behavior in the system.

Need for ISP

Without ISP With ISP
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 or empty implementations. This situation can be prevented by segregating (i.e seperating) big interfaces into smaller ones. Only strictly related method definitions must be in the same interface. Different types of functionalities must be placed in different interfaces. This is ISP.
The larger a component gets, the larger will be its interface. This can not only make it harder to understand the purpose of a component, but it can also cause increased coupling, where by components that make use of such a component are exposed to more of that component’s capabilities that are appropriate. ISP aims to tackle this problem by breaking a component’s interface into functionally separate sub-interfaces. Although a component may still end up with the same set of public members, those members will be separated into separate interfaces such that a calling component can operate on the component by referring only to the interface that concerns the calling component.

That way calling components will not be overwhelmed by irrelevant operations that have been separated out into other interfaces.

Examples

Example1

Without ISP

For example, a User Interface class that has a number of operations for each different screen that it can handle.

In this example each screen may have a number of display operations, but in such a class the sheer number of operations that the class offers could become unmanageable when it is not easy to see which operations belong to which screens.

As a result of this, problems may arise, should a developer working on the UI class unwittingly attempt to call a UI operation that belongs to a different screen to the one currently being displayed by the UI object.

This would be a breach of the Liskov Substitution Principle (LSP) and could lead to unexpected and unpredictable behaviour.

With ISP

In the example of a UI class, each operation will belong to a particular screen. Each screen that the UI class is to handle could have an individual UI screen interface defined for it, containing only those operations that are required for that screen.

When this is done it becomes clear to the developer using a UI object which operations are related to the screen currently being displayed, because the UI interface used in the variable declaration will only expose those operations that relate to that screen.

Example2

Without ISP

In the below example you see one class CarConfigurator interface implementing interface ICarConfigurator that is being used by three different classes; DrawExterior, DrawInterior and DrawEngine. All these different classes are using different functionality of the ICarConfigurator interface.This means that all of these classes are connected to each other and if there is a change for one class in the interface then this will affect all of them.

Interface: ICarConfigurator

namespace ISP
{
  public interface ICarConfigurator
  {
      string ExteriorColor { get; set; }
      string InteriorColor { get; set; }
      string EngineType { get; set; }
  }
}

Example3