Interface Oriented Design - Ken Pugh
three laws that all implementations should obey, regardless of what services they offer.
1. An Interface’s Implementation Shall Do What Its Methods Says It Does
If the purpose and meaning of a method are not unambiguously obvious from the method’s name and its place within an interface, then those aspects should be clearly documented.3 The documentation may refer to interface tests, such as those described later in this chapter, to demonstrate method meaning in a practical, usage context.
2. An Interface Implementation Shall Do No Harm
3. If An Implementation Is Unable to Perform Its Responsibilities, It Shall Notify Its Caller
An implementation should always report problems that are encountered and that it cannot fix itself.
Michael Hunter suggests, “They should be documented regardless. Conversely, if
they need documentation, the name should be improved.”
To successfully use an interface, both the caller and implementer need to understand the contract—what the implementation agrees to do for the caller. You can start with informal documentation of that agreement. Then, if necessary, you can create a standard contract.
An interface implementation is not required to check the preconditions. You may assume that the user has met those preconditions. If the user has not, the implementation is free to fail. Any failures should be reported as in the Third Law of Interfaces.
An implementation with weaker preconditions can meet the contract for the interface. One that has stronger preconditions cannot.
An implementation with stronger postconditions meets the contract; one with weaker postconditions does not.
You also need to know the protocol to the interface—the set of allowable sequences of method calls.
As a general rule, no interface definition is complete until you have all the contractual tests successfully running for at least one implementation.
Design to an interface, not an implementation.
“Test to an interface, not an implementation”.
Writing tests for an interface can also help you work out difficulties in the interface.
If you find that your interface is hard to test, then it probably will be hard to use. If this happens, you can redesign your interface without even having coded an implementation.
Levels of contracts.19 The levels are:
• Basic type contracts as in typed programming languages
• Semantic contracts that include the preconditions and postconditions
• Performance contracts for real-time systems
• Quality of service contracts that are hard to quantify
Testing for quality of service contracts is usually more difficult than testing for performance contracts
An implementation of an interface should obey the three laws:
• Do what its methods say it does.
• Do no harm.
• Notify its caller with meaningful errors if unable to perform its responsibilities.
Establish a contract for each interface (either formally or informally):
• Indicate the preconditions and postconditions for each method.
• Specify the protocol—the sequence of allowable method calls.
• Optionally, spell out nonfunctional aspects such as performance or quality of implementation.
• Create tests to check that an implementation performs according to its contract and that it obeys the three laws.
Interfaces move data in one of two ways: push or pull. You ask a pullstyle interface—for example, a web browser—for data. Whenever you desire information, you type in a URL, and the information is returned. On the other hand, a push-style interface transfers data to you. An email subscription is a push-style interface.
Advantage—can be simpler code, once paradigm is understood
Advantage—appears as a common control style (e.g., loop) in multiple languages
Advantage—implementer does not have to determine type of parameter
Disadvantage—implementer has to implement all the methods
Advantage—can be more resilient to change, because new methods do not have to be implemented
Disadvantage—must check parameter type to determine flow
Advantage—a small number of operators can service many requests. My mom would not be able to juggle more than a few requestors at a time.
Advantage—there is less chatter to get the same amount of work done.
Advantage—order of the method calls does not matter
Disadvantage—parameter lists are longer
Advantage—parameter lists shorter
Disadvantage—order of method calls important
If you don’t like way that an interface works, transform it into one you like.
One of the most difficult decisions in developing a program is determining what should be in an interface.
You often face another question: how many methods should you have in an interface? Many methods can make an interface more difficult to understand but can also make it more powerful.
Methods in an interface should be cohesive. They should provide services that revolve around a common concept.1 The problem is that the definition of commonness is relative.
SINGLE PRINTER INTERFACE
Advantage—can have single capability query method
Disadvantage—related capabilities may not be logically grouped together
MULTIPLE PRINTER INTERFACES
Advantage—printer need only implement interfaces it supplies
Disadvantage—lots of interfaces
Coupling measures how one module depends on the implementation of another module.
Disadvantage—callers have to be changed if implementation changes
Advantage—callers do not need to be changed if implementation changes
Advantage—easier to implement and test with fewer methods
Disadvantage—user must code their particular functionality and may wind up with duplicate code for same functionality
Advantage—user has all needed methods
Disadvantage—may be harder to understand an interface with numerous methods
Advantage—easy for the user to perform common functions
Disadvantage—variations must be coded as new methods
Advantage—users have flexibility to “do it their way”
Disadvantage—may be harder for users to understand
Advantage—delay forming hierarchy until usage known
Advantage—less delegation of common operations
Advantage—can cross hierarchies
Advantage—captures common attributes
Advantage—can capture common set of usage
Advantage—captures set of common behavior
Advantage—give more adaptability for roles that cross hierarchies
Disadvantage—may have duplicated code without helper classes to provide common functionality
Advantage—base classes can provide common implementations
Disadvantage—difficult to adapt to new situations.
When you have multiple implementations of an interface such as InputStream, you may duplicate logic in each implementation. If there are common implementation methods and you do not use a helper class, you may find yourself copying and pasting a lot. If you can create a well-defined hierarchy with many inheritable implementations, you are far better off using inheritance, rather than interfaces. But you may find that starting with interfaces and then refactoring to inheritance allows you to discover what is the best hierarchy.
Advantage—easy to inherit an implementation
Disadvantage—may be difficult to adapt to changing roles
Advantages—can be clearer what methods must be implemented.
A class in another inheritance hierarchy can provide the services of an interface.
Disadvantage—may end up with lots of helper classes.
• Interfaces show commonality of behavior.
• Inheritance shows commonality of behavior along with commonality of implementation.
• Interfaces are more fluid than inheritance, but you need to use delegation to share common code.
• Inheritance couples the derived classes to the base class, but it allows them to easily share the common code of the base class.
PROCEDURAL STYLE interface
Advantage—remote and local interfaces can appear the same
Disadvantage—can require more communication (especially if fine-grained)
DOCUMENT STYLE interface
Advantage—can require less communication
Disadvantages—style is less familiar to programmers
Advantage—practically immediate response
Disadvantage—cannot scale up as well
Advantage—can scale well, especially with document queues
Disadvantage—documents should be validated on client before transmitting
Advantages—servers can be easily scaled. If you have multiple servers processing client requests, any server can handle any client.
The service has redundancy. Any server could go down and the client could continue with any other server.
Disadvantage—amount of state information passed between client and server can grow, especially for a full shopping cart. In most cases, this amount will be less than the size of the web pages for which the state information is transferred.
Advantage—less information to communicate between client and server
Disadvantage—if using central database where the state information is stored, the amount of simultaneous connections to that database could be a limiting factor to scalability
Stability is a needed trait for external interfaces.
Both procedural and document interfaces can be flexible.
You should follow a few guidelines in being flexible in interfaces. First, never alter the semantics of existing methods or data. Second, you can add methods or data; just don’t expect that users will add calls to the new methods or provide the new data.
But although adding functionality is easy, deleting methods or data is hard.
Whenever you have an remote interface that is available to the outside world, you need to worry about security.
No guaranteed way exists to determine what responsibilities should go with which interfaces; designing interfaces takes the same effort as designing classes. Bertrand Meyer says, “Finding classes is the central decision in building an object-oriented software system; as in any creative discipline, making such decisions right takes talent and experience, not to mention luck.” He goes on to say, “No book advice can replace your know-how and ingenuity.” The same can be said for finding the right set of cohesive interfaces.
In interface-oriented design, the emphasis is on designing a solution
with interfaces. When using IOD, here are some tips:
• Use IRI cards to assign responsibilities to interfaces.
• Keep service interface definitions in code separate from the code
for classes that implement it.
• Write tests first to determine the usability of an interface.
• Write tests against the contract for the interface.
Advantage—hides implementation requirements
Disadvantage—services have dependency on a configuration interface
USING INVERSION OF CONTROL
Advantage—common feature (used in frameworks)
Disadvantage—can be harder to understand
You should review your interfaces before you implement them. Otherwise, you may implement methods that turn out to be useless.
Using interface-oriented design, just as with other techniques, does not guarantee that you’ll never have to restructure your interfaces or refactor your code. As you develop a system, you usually discover additional information that suggests a restructuring of the design.