CSC/ECE 517 Fall 2007/wiki3 4 s2: Difference between revisions
Line 44: | Line 44: | ||
<p>Imagine that in your application you are required to write some Data Access Objects (DAO). These data objects should support a variety of data sources. Let's consider that the two main data sources are file and database. You must be careful enough to come up with an interface-based design, where the implementation of data access can be varied without affecting the client code using your DAO object. The following design is a good example of the above requirements.</p> | <p>Imagine that in your application you are required to write some Data Access Objects (DAO). These data objects should support a variety of data sources. Let's consider that the two main data sources are file and database. You must be careful enough to come up with an interface-based design, where the implementation of data access can be varied without affecting the client code using your DAO object. The following design is a good example of the above requirements.</p> | ||
[[Image:Interface_segregation_DAO_1.gif]] | |||
[[Image:]] | |||
<p>There's another aspect that needs be to considered. What happens if the data source is read-only? The methods for inserting and updating data are not needed. On the other hand, if the DAO object should implement the DAO interface, it will have to provide a null implementation for those methods defined in the interface. This is still acceptable, but the design is gradually going wrong. What if there is a need to rotate the file data source to a different file once a certain amount of data has been written to the file? That will require a separate method to add to the DAO interface. This is just to add the flexibility to the clients using this FileDAO object to enable them to choose either the normal append feature to the file data source or to make use of the improved file rotation feature.</p> | <p>There's another aspect that needs be to considered. What happens if the data source is read-only? The methods for inserting and updating data are not needed. On the other hand, if the DAO object should implement the DAO interface, it will have to provide a null implementation for those methods defined in the interface. This is still acceptable, but the design is gradually going wrong. What if there is a need to rotate the file data source to a different file once a certain amount of data has been written to the file? That will require a separate method to add to the DAO interface. This is just to add the flexibility to the clients using this FileDAO object to enable them to choose either the normal append feature to the file data source or to make use of the improved file rotation feature.</p> | ||
Line 58: | Line 56: | ||
<p>Thus, for the problem with the Data Access Objects, follow the Interface Segregation Principle, and separate the interfaces based on the behaviors. The database access classes and file access classes should subscribe to two separate interfaces. The following design is obtained by applying the Interface Segregation Principle.</p> | <p>Thus, for the problem with the Data Access Objects, follow the Interface Segregation Principle, and separate the interfaces based on the behaviors. The database access classes and file access classes should subscribe to two separate interfaces. The following design is obtained by applying the Interface Segregation Principle.</p> | ||
[[Image:Interface_segregation_DAO_2.gif]] | |||
[[Image:]] | |||
<p>With this design, the Fat interface symptom is avoided and the interfaces clearly delineate their intended purpose. If any imaginary data access object requires a combination of operations defined in both of these interfaces, they will be able to do so by implementing both the interfaces. </p> | <p>With this design, the Fat interface symptom is avoided and the interfaces clearly delineate their intended purpose. If any imaginary data access object requires a combination of operations defined in both of these interfaces, they will be able to do so by implementing both the interfaces. </p> |
Revision as of 02:42, 30 November 2007
Interface Segregation principle
The Interface Segregation principle states that
"Clients should not be forced to depend upon interfaces that they do not use."[1]
Several other definitions are possible:[2]
- "Many client specific interfaces are better than one general purpose interface"
- "The dependency of one class to another one should depend on the smallest possible interface"
- "Make fine grained interfaces that are client specific."
Each of these definitions point to the same thing. A desire to avoid "fat" interfaces. "Fat" interfaces are interfaces whose methods can be divided into more than one group.
This is very similar to another object-oriented principle, "A class should only do one thing and do it well."
Any interface, therefore, should be as small as possible for the required functionality and many small interfaces are favored over fewer larger interfaces. Clients are likely to change the requirements of a system over time. If the same interface is shared among multiple client, you will quickly run into a situation where a client's code contains functionality the he or she has no use for. In addition to making the client carry unnecessary code, this also creates the potential for conflicts within the code.
Example: Online Bookseller
An excellent example of why "fat" interfaces should be avoided is provided by DoodleProject[1]:
Recall the on-line bookseller system introduced in the Open-Closed Principle and referred to in the Dependency Inversion Principle. Now the company wants to start offering music CDs for sale through its website. To accommodate this, the search engine portion of the website will need to be modified. Trying to reuse as much code as possible, the SearchEngine and Query types are amended to accept CD searching queries. The resulting class diagram is:
To satisfy the requirements of CD searching, the Query interface was forced to handle both BookServletQuery and CDServletQuery functionality. In the future, if there is any change to the searching functionality of CDServletQuery, then that change will be forced onto the Query interface. Furthermore, any change to Query forces a change to all of its implementers, including BookServletQuery. This scenario, a change in the CD query portion of the website forces a change (and possible bugs) in the book query portion of the website, makes the software very fragile. To fix this BookServletQuery and CDServletQuery must be implemented from separate interfaces. This can easily be accomplished by adding some additional abstract types to the design that are more specific in their tasks. The new class diagram is:
With this design, the two functional concerns have been separated. Now, if CDServletQuery needs new searching functionality, the change is forced upon CDQuery but can not propagate to the book query portion of the design.
Example: Database Access Objects
This example using Database Access Objects is provided by Java Boutique[3]
Imagine that in your application you are required to write some Data Access Objects (DAO). These data objects should support a variety of data sources. Let's consider that the two main data sources are file and database. You must be careful enough to come up with an interface-based design, where the implementation of data access can be varied without affecting the client code using your DAO object. The following design is a good example of the above requirements.
There's another aspect that needs be to considered. What happens if the data source is read-only? The methods for inserting and updating data are not needed. On the other hand, if the DAO object should implement the DAO interface, it will have to provide a null implementation for those methods defined in the interface. This is still acceptable, but the design is gradually going wrong. What if there is a need to rotate the file data source to a different file once a certain amount of data has been written to the file? That will require a separate method to add to the DAO interface. This is just to add the flexibility to the clients using this FileDAO object to enable them to choose either the normal append feature to the file data source or to make use of the improved file rotation feature.
With the DatabaseDAO implementation now broken, we'll need to change it, to provide a null implementation of the new method added to the interface. This is against the Open-Closed Principle.
So, what went wrong? In the basic design, the fact that the file data access operation and database access operation can differ fundamentally must be considered. We defined the behaviors for both the data access operation, and the database access operation together in a single interface. This caused problems at a later stage in the development. It is not necessary to be a guru in Object Oriented System Design, to solve this problem nor is vast experience in designing software applications needed. What is necessary is to think of interfaces as the behaviors to be provided through particular objects. If two or more objects implementing the interface depict different sets of behaviors, then they probably cannot subscribe to a single interface.
When a single interface is designed to support different groups of behaviors, they are, by virtue, inherently poorly designed, and are called Fat interfaces. They are called Fat because they grow enormously with each additional function required by clients using that interface.
Thus, for the problem with the Data Access Objects, follow the Interface Segregation Principle, and separate the interfaces based on the behaviors. The database access classes and file access classes should subscribe to two separate interfaces. The following design is obtained by applying the Interface Segregation Principle.
With this design, the Fat interface symptom is avoided and the interfaces clearly delineate their intended purpose. If any imaginary data access object requires a combination of operations defined in both of these interfaces, they will be able to do so by implementing both the interfaces.
References
[1] http://doodleproject.sourceforge.net/articles/2001/interfaceSegregationPrinciple.html
[2] http://davidhayden.com/blog/dave/archive/2005/06/15/1482.aspx
[3] http://javaboutique.internet.com/tutorials/JavaOO/interface_segregation.html