CSC/ECE 517 Fall 2009/wiki3 8 agummad
Interface Segregation Principle vs. Principle of Small Interfaces
Introduction
Software changes. To enable software to chage gracefulllyspend a lot of time in designing. Use of oo best practices. We present two specific design rules that deal in different levels abstractions in the design phase. We present an explanation of both and provide a comparision of both strategies and their application.
Background
OO programming and design patterns
The oo paradigm of programming and their associated design patterns have long been studied heavily by a computer programmer. Ever since OO programming replaced the functional type programming style of the early 70s, it has been used extensively to develop commonly used software. However, as the scope and the size of these projects grew, researchers studied ways to maintain the quality of the software written. They introduced patterns of programming within OO in part to address some very common problems occurring while programming and maintaining a large project. This study of OO design programming patterns and related principles is a vast field, and its understanding is considered vital for developers.
Modularity
A modular system is one that is made of autonomous elements which are connected and inter-operate in a simple yet coherent structure. However to formally define modularity, one needs to look at the concept from different view points. We may define modularity along the following concepts, as defined in [1].
- Decompose-ability: A software development method is Decomposable if it helps in the task of decomposing functionality into a small number of less complex subproblems. These smaller units of functionality are then connected by a simple structure, allowing them to be independent from each other in that each of them is extensible without affecting the others.
- Composability:A software development method is Modular composable if it allows the production of software elements which may then be freely used as building blocks for newer systems. Each of these elements may be combined with each other to produce new systems,quite often in an environment very different from the ones in which they were initially developed.
- Understandability: A software development method should produce software, which is non trivial enough in that a human can understand the functionality and working of each module without having to learn about others or, at worst, by having to examine only a few of the others.
- Continuity: A software development method satisfies Modular Continuity if, in the software architectures that it yields, a small change in a problem specification will trigger a change of just one module, or a small number of modules.
- Protection: A method satisfies Modular Protection if it yields architectures in which the effect of an abnormal condition occurring at run time in a module will remain confined to that module, or at worst will only propagate to a few neighboring modules.
Rules for Modularity
From the preceding criteria, five rules follow which we must observe to ensure modularity in a given system. These rules were collectively postulated by Bertrand Meyer:
- Rule of Direct Mapping:
- Rule of Few Interfaces:
- Rule of Small interfaces (weak coupling)
- Rule of Explicit Interfaces
- Rule of Information Hiding
SOLID Design Principles
The SOLID principles of Object Oriented Design are five basic class design principles. These were first stated by Bob Martin and are outlined extensively on his websites. Collectively, they provide a set of guidelines that help design better objdect oriented systems. The SOLID principles are most concerned with dependency management, or the ability to keep code loosely coupled and flexible. The 5 principles are given below :
- SRP - The Single Responsibility Principle< - A class should have one, and only one, reason to change. The SRP says a class should focus on doing one thing. This doesn't mean it should only have one method, but instead all the methods should relate to a single purpose. For example, an Invoice class might have the responsibility of calculating various amounts based on it's data. In that case it probably shouldn't know about how to retrieve this data from a database, or how to format an invoice for print or display.
- OCP - The Open Closed Principle - You should be able to extend a classes behavior, without modifying it. Change a class' behaviour using inheritance and composition. Bob Martin's initial paper on the OCP linked from The Principles of OOD attributes the idea to Bertrand Meyer, who wrote that classes should be "open for extension, but closed for modification"[2]. The idea is that we can use OO techniques like inheritance and composition to change (or extend) the behavior of a class, without modifying the class itself.
- LSP - The Liskov Substitution Principle - Derived classes must be substitutable for their base classes.
- ISP - The Interface Segregation Principle - Make fine grained interfaces that are client specific.
- DIP - The Dependency Inversion Principle - Depend on abstractions, not on concretions.
The Interface Segregation Principle
Statement
The Interface Segregation Principle (ISP) is a SOLID Principle that states the following:
"Make fine grained interfaces that are client specific."
Motivation
This principle deals with the disadvantages of polluted interfaces -"fat" interfaces and unnecessary recompilation.
Explanation
In simpler terms, consider a client that depends upon a class that contains interfaces that our client does not use, but that other clients of that class do use. The problems that exist are as follows:
- The class now has to provide a null or a valid implementation for all those extra interfaces that our client does not use. Any change to any part in this hierarchy needs a complete recompilation.
- Our client now will be affected by the changes that those other clients force upon the class.
We would like to avoid such couplings where possible, and so we want to separate the interfaces where possible.
Example of a Violation:
Consider a secirity system with Door objects that can be locked and unlocked.
class Door { public: virtual void Lock() = 0; virtual void Unlock() = 0; virtual bool IsDoorOpen() = 0; };
We make this an abstract class and each type of door that we wish to create use this class and provides its own implementation of Door.Consider a TimedDoor, which sounds an alarm when a when a door is left open for too long. For this, it communicates with a Timer object.
class Timer { public: void Regsiter(int timeout, TimerClient* client); }; class TimerClient { public: virtual void TimeOut() = 0; };
Thus, when a TimedDoor object wishes to be informed about a timeout, it calls the Register function of the Timer. We force Door, and therefore TimedDoor, to inherit from TimerClient. This ensures that TimerClient can register itself with the Timer and receive the TimeOut message.
The problem with this implementation is that Door class now depends on TimerClient. For Door types that dont need timing, we still need to provide nil implementations for Timeout method. Also all these derivatives need a #include for TimerClient, even though, they don't use it. Thus, our TimedDoor has forced a change in all the tyes of Doors that we have.
Resolution
Possible ways to ensure that ISP is enforced is to use the Adapter Pattern. this makes sure that the interfaces of the class can be broken up into groups of member functions. Each group serves a different set of clients. Thus some clients use one group of member functions, and other clients use the other groups. The other solution is to use Multiple Inheritance,
The problem is resolved using the ADAPTER pattern as follows: We create an adapter object that derives from TimerClient and delegates to the TimedDoor. That is, when the TimedDoor wants to register a timeout request with the Timer, it creates a DoorTimerAdapter and registers it with the Timer. When the Timer sends the TimeOut message to the DoorTimerAdapter, the DoorTimerAdapter delegates the message back to the TimedDoor. Thus DoorTimerAdapter TimedDoor does not have to have the exact same interface as TimerClient. The DoorTimerAdapter can translate the TimerClient interface into the TimedDoor interface.
Object Form of Adapter Pattern class TimedDoor : public Door { public: virtual void DoorTimeOut(int timeOutId); }; class DoorTimerAdapter : public TimerClient { public: DoorTimerAdapter(TimedDoor& theDoor) : itsTimedDoor(theDoor) {} virtual void TimeOut(int timeOutId) {itsTimedDoor.DoorTimeOut(timeOutId);} private: TimedDoor& itsTimedDoor; };
PSI
Statement
This rule states that
If two modules communicate, they should exchange as little information as possible
Motivation
The Small Interfaces or “Weak Coupling” rule relates to the size of intermodule connections rather than to their number or their size. One would want to reduce the size of the intermodule connections to the very minimum, thus enabling tem to communidcate effectively exchanging the very leasy amount of information.
Explanation
The rule for smaller interfaces is desirable in most engineering fields. For example, in the field of electrical sciences, one would paraphrase to say that the communication channels between any two modules. This requirement follows from the criteria of continuity and protection (see discussion of modular systems). Once the modules are tightly coupled with each other, they are harlder to change and harder to protect. This is because by having larger fields of communication, every module might increase the risk of misusing the common data. Therefore in tightly coupled systems, the problems of propagation of changes and propagation of errors are really nasty.
Example of a Violation
An example of violation is an established Fortran practice of using COMMON.
A common block in Fortran shows up the following directive form:
COMMON /common_name/ variable1... variablen Example: >> COMMON block1 A[75], B[25] >> COMMON block1 C[50], D[50]
This indicates that the variables listed are accessible not just to the enclosing module but also to any other module which includes a COMMON directive with the same common_name. It is not infrequent to see Fortran systems whose every module includes an identical gigantic COMMON directive, listing all significant variables and arrays so that every module may directly use every piece of data. This runs the risk of data misuse as discussed above. Developers using nested structures also face similar problems.
Resolution
Developers using languages with nested structures can suffer from similar troubles.
With block structure as introduced by Algol and retained in a more restricted form by Pascal, it is possible to include blocks, delimited by begin ¼ end pairs, within other blocks. In addition every block may introduce its own variables, which are only meaningful within the syntactic scope of the block. For example:
local-- Beginning of block B1 x, y: INTEGER do Instructions of B1 local -- Beginning of block B2 z: BOOLEAN do Instructions of B2 end --- of block B2 local -- Beginning of block B3 y, z: INTEGER do Instructions of B3 end -- of block B3 Instructions of B1 (continued) end -- of block B1
Variable x is accessible throughout the block, whereas the two variables called z (one BOOLEAN, the other INTEGER) have scopes limited to B2 and B3 respectively. Like x, variable y is declared at the level of B1, but its scope does not include B3, where another variable of the same name (and also of type INTEGER) locally takes precedence over the outermost y. Block structure. However introduces may still violate the Small Interfaces rule. The architecture of object-oriented software will involve three levels: a system is a set of clusters; a cluster is a set of classes; a class is a set of features (attributes and routines). Clusters, an organizational tool rather than a linguistic construct, can be nested to allow a project leader to structure a large system in as many levels as necessary; but classes as well as features have a flat structure, since nesting at either of those levels would cause unnecessary complication.
CONCLUSION
The principle of small interfaces and principle of interface separation differ in that one deals with the size of the interface and the other deals with the size of the communication channels between the interfaces. Therefore these are used in different abstraction levels. One would benefit by using the SOLID principles to design better structured OO software. Using the rules of modularity one would develop software that lends itself to better modular solutions