Evan Linden

Helping to create great programmers one at a time
posts - 8, comments - 3, trackbacks - 0

My Links

News

Twitter












Tag Cloud

Article Categories

Archives

Post Categories

Image Galleries

About Me

Tuesday, June 03, 2008

Using Interfaces - Design by Contract

An argument for Interface based design and programming
For software to survive in the ever-changing jungle of the production environment, it must have three distinct characteristics: reusability, maintainability, and extensibility.
Interface-based programming exists outside the world of COM. It is a programming discipline that is based on the separation of the public interface from implementation. It was pioneered in languages such as C++ and Smalltalk by software engineers who discovered that using distinct interfaces could make their software, especially large applications, easier to maintain and extend. The creators of Java saw the elegance of interface-based programming and consequently built support for it directly into their language.
Interfaces solve many problems associated with code reuse in object-oriented programming. This document will discuss what some of these problems are. In particular, when you program in a style consistent with classic OOP, a client can build inflexible dependencies on a class definition. These dependencies can make it difficult to maintain or extend the class without breaking the client. It becomes tedious or impossible to improve the code for an object over time. Certain problems are also associated with a popular OOP language feature known as implementation inheritance. This powerful but often misused feature is vulnerable to similar dependency problems, which compromise an application's maintainability and extensibility. Since most 3GL languages support implementation inheritance, this document will discuss its strengths and limitations in order to address some of the problems that interface-base programming was created to solve.

Separating the Interface from the Implementation
Object composition offers another way to achieve reuse without the tendency toward tight coupling. Object composition is based on black-box reuse, in which implementation details of a class are never revealed to the client. Clients know only about an available set of requests (the what). Objects never expose internal details of the response (the how).
Black-box reuse is based on formal separation of interface and implementation. This means the interface becomes a first-class citizen. An interface is an independent data type that is defined on its own. This is an evolution of classic OOP, in which a public interface is defined within the context of a class definition.
At this point, you are probably thinking that this is all pretty vague. You're asking yourself, "What exactly is an interface?" Unfortunately, it's hard to provide a concise definition that conveys the key concepts of an entirely new way to write software. An interface can be described in many ways. You can get up to speed pretty quickly on the syntax for defining, implementing, and using interfaces. However, the implications that interfaces have on software design are much harder for the average programmer to embrace. Learning how to design with interfaces usually takes months or years.
At its most basic level, an interface is a set of public method signatures. It defines the calling syntax for a set of logically related client requests. However, while an interface defines method signatures, it cannot include any implementation or data properties. By providing a layer of indirection, an interface decouples a class from the clients that use it. This means an interface must be implemented by one or more classes in order to be useful. Once an interface has been implemented by a class, a client can create an object from the class and communicate with it through an interface reference.
You can use an interface to create an object reference but not the object itself. This makes sense because an object requires data properties and method implementations that cannot be supplied by an interface. Because it is not a creatable entity, an interface is an abstract data type. Objects can be instantiated only from creatable classes known as a concrete data types.
From a design standpoint, an interface is a contract. A class that implements an interface guarantees the objects it serves up will support a certain type of behavior. More specifically, a class must supply an implementation for each method defined by the interface. When communicating with an object through an interface reference, a client can be sure the object will supply a reasonable response to each method defined in the interface.
More than one class can implement the same interface. An interface defines the exact calling syntax and the loose semantics for each method. These loose semantics give each class author some freedom in determining the appropriate object behavior for each method. For instance, if the IDatabase interface defines a method named ExecuteSql(string commandText), different class authors can supply different responses to the same request, as long as each somehow reinforces the concept of running a sql statement. The OracleDatabaseConnector class can implement ExecuteSql() in a different way than either MSSqlDatabaseConnector or SybaseDatabaseConnector. This means that interfaces provide the opportunity for polymorphism. Interfaces are like implementation inheritance in the sense that they let you build applications composed of plug-compatible objects. However, interfaces provide plug-compatibility without the risk of the tight coupling that can occur with implementation inheritance and white-box reuse.

Note that this is an example only I am not sure I could ever justify doing this or argue that this is a good thing unless your are forced into it. I felt compelled to implement this due to usage restrictions in a corporate environment. In an effort to foster reuse (topic for another time… How and when to write for reuse) and comply with some misinterpreted security policies a wrapper was written around .Net native Data Access to “Protect” developers from themselves and to hide database connection strings from developers at runtime in the IDE. DON’T ASK HOW that’s a story for another day.

The Two Faces of Inheritance
Inheritance is an objected-oriented concept that models an "IS A" relationship between two entities. So far, this article has used the term implementation inheritance instead of the more generic term inheritance because extending a super class with a sub class is only one way to leverage an "IS A" relationship. When a class implements an interface, it also takes advantage of an "IS A" relationship. For instance, if a class OracleDatabaseConnector implements the interface IDatabase, it is correct to say that an OracleDatabaseConnector "IS A" Database. You can use an OracleDatabaseConnector object in any situation in which an IDatabase-compatible object is required.
Interface-based programming is founded on a second form of inheritance known as interface inheritance. This means that inheritance does not require the reuse of method implementations. Instead, the only true requirement for inheritance is that a sub class instance be compatible with the base type that is being inherited. The base type that is inherited can be either a class or a user-defined interface. In either situation, you can use the base-type references to communicate with objects of many different types. This allows both forms of inheritance to achieve polymorphism.
Both forms of inheritance offer polymorphism, yet they differ greatly when it comes to their use of encapsulation. Implementation inheritance is based on white-box reuse. It allows a sub class to know intimate details of the classes it extends. This allows a sub class to experience implicit reuse of a super class's method implementation and data properties. Implementation inheritance is far more powerful than interface inheritance in terms of reusing state and behavior. However, this reuse comes with a cost. The loss of encapsulation in white-box reuse limits its scalability in large designs.
As the term black-box reuse suggests, interface inheritance enforces the concepts of encapsulation. Strict adherence to the encapsulation of implementation details within classes allows for more scalable application designs. Interface-based programming solves many of the problems associated with white-box reuse. However, to appreciate this style of programming, you must accept the fact that the benefits are greater than the costs. This is a struggle for many programmers.
When a class implements an interface, it takes on the obligation to provide set methods. Sub class authors must write additional code whenever they decide to implement an interface. When you compare this to implementation inheritance, it seems like much more work. When you inherit from a class most of your work is already done, but when you inherit from an interface your work has just begun. At first glance, implementation inheritance looks and smells like a cheeseburger, while interface inheritance looks like a bowl of steamed broccoli. You have to get beyond the desire to have the cheeseburger to reach a higher level of interface awareness. The key advantage of interface inheritance over implementation inheritance is that it is not vulnerable to the tight coupling that compromises the extensibility of an application.

Why Use Interfaces?
When developers learn how to use interfaces in an application, they often wonder, "Why would I ever want to do that?" or "Why should I care?" Programming with class-based references seems far more natural compared to the additional complexity required with user-defined interfaces. The previous example would have been far easier if the client code programmed against public methods and properties of the OracleDatabaseConnector class instead of the IDatabase interface. User-defined interfaces seem like extra work without any tangible benefits.
There are several significant reasons why a programmer should care about interfaces. The first reason is that interfaces are the foundation of Services they define the contract. In a services environment clients can use class-based references but imagine the power contract based programming. Instead of class objects you access and program to their published interface the contract is immutable the objects are not. This provides a greater degree of isolation and protection from changes in the service and a high degree of flexibility through interface references. I know this sounds like the fundamentals of COM. Isn’t COM dead? Yes is died with a whimper and a cheer but the rules we lived with port directly to the WebServices and SOA enabled world. If you embrace interface-based programming, you will become a much stronger programmer.
Another reason why you should care about interfaces is that they can offer power and flexibility in software designs. Using interfaces in your programming becomes valuable when you don't have a one-to-one mapping between a class and a public interface. There are two common scenarios. In one scenario, you create an interface and implement it in multiple classes. In the other scenario, you implement multiple interfaces in a single class. Both techniques offer advantages over application designs in which clients are restricted to using references based on concrete classes. While interface-based designs often require more complexity, the sky is the limit when it comes to what you can do with them.
Consider a case in which many classes implement the same interface. For example, assume the classes CSecurityProvider, CSettingsProvider, and CNotificationsProvider all implement the interface IProvider.

An application can maintain a collection of IProvider-compatible objects using the following code:


Well thats neat but how is that of use? I can accomplish the same thing with a generic collection or an array of object type. Yes your right but where is the value add? How do you know what is in your generic collection or array of objects?
First lets assume that the interface defines a method/contract of GetData() and that an array of providers is provided in the constructor of the containing class. Lets also assume for this example that each Provider needs to call the GetData method in the constructor of the containing class. A simple for loop can be used to accomplish this for all the IProviders.
 

Another scenario could be that you only want to call the GetData method on the SecurityProvider and verify the user is authorized before continuing. Now our simple loop has become a bit more complex.There are two ways to accomplish this I would recommend the second for simplicity,elegance and clarity.
Example 1

Example 2

This has by no means been an exhaustive discussion of interfaces but I think you can begin to see the value and power that design by interface can bring to you applications.

posted @ Tuesday, June 03, 2008 10:17 PM | Feedback (0) |

Why Unit Test

Unit Testing
In computer programming, unit testing is a procedure used to validate that individual modules or units of source code are working properly.
More technically one should consider that a unit is the smallest testable part of an application. In a Procedural Design a unit may be an individual program, function, procedure, web page, menu etc. But in Object Oriented Design, the smallest unit is always a Class; which may be a base/super class, abstract class or derived/child class.
A unit test is a test for a specific unit. Ideally, each test case is independent from the others; mock objects can be used to assist testing a module in isolation. Unit testing is typically done by the developers and not by end-users.


Systems Best Practice for Unit Testing
Writing good unit tests is simply a best practice for application developers. When we do code reviews we do attempt to run the unit tests if they in fact exist and we note if the tests pass or fail. The tests are there for the benefit of the development team to ensure that the code functions according to the specifications delivered and to support long term maintainability.
I would recommends that unit tests exist for each and every public function that is accessible within the code base. This will also assist in identifying problem areas during maintenance work and ensure that once the code it developed and stable that it remains stable. It is highly recommended that unit tests be developed that exercise both the positive path and negative path to ensure that improper inputs as will as expected behavior is properly controlled.


Benefit
The goal of unit testing is to isolate each part of the program and show that the individual parts are correct. Unit testing provides a strict, written contract that the piece of code must satisfy. As a result, it affords several benefits.


Facilitates change
Unit testing allows the programmer to refactor code at a later date, and make sure the module still works correctly (i.e. regression testing). The procedure is to write test cases for all functions and methods so that whenever a change causes a regression, it can be quickly identified and fixed. This provides the benefit of encouraging programmers to make changes to the code since it is easy for the programmer to check if the piece is still working properly. Good unit test design produces test cases that cover all paths through the unit with attention paid to loop conditions.
In continuous unit testing environments, through the inherent practice of sustained maintenance, unit tests will continue to accurately reflect the intended use of the executable and code in the face of any change. Depending upon established development practices and unit test coverage, up-to-the-second accuracy can be maintained.


Simplifies integration
Unit testing helps to eliminate uncertainty in the units themselves and can be used in a bottom-up testing style approach. By testing the parts of a program first and then testing the sum of its parts, integration testing becomes much easier.
A heavily debated matter exists in assessing the need to perform manual integration testing. While an elaborate hierarchy of unit tests may seem to have achieved integration testing, this presents a false sense of confidence since integration testing evaluates many other objectives that can only be proven through the human factor. Some argue that given a sufficient variety of test automation systems, integration testing by a human test group is unnecessary. Realistically, the actual need will ultimately depend upon the characteristics of the product being developed and its intended uses. Additionally, the human or manual testing will greatly depend on the availability of resources in the organization.


Documentation
Unit testing provides a sort of "living document". Clients and other developers looking to learn how to use the module can look at the unit tests to determine how to use the module to fit their needs and gain a basic understanding of the API.
Unit test cases embody characteristics that are critical to the success of the unit. These characteristics can indicate appropriate/inappropriate use of a unit as well as negative behaviors that are to be trapped by the unit. A unit test case, in and of itself, documents these critical characteristics, although many software development environments do not rely solely upon code to document the product in development.
Ordinary documentation, on the other hand, is more susceptible to drifting from the implementation of the program and will thus become outdated (e.g. design changes, feature creep, relaxed practices to keep documents up to date).

Separation of interface from implementation
Because some classes may have references to other classes, testing a class can frequently spill over into testing another class. A common example of this is classes that depend on a database: in order to test the class, the tester often writes code that interacts with the database. This is a mistake, because a unit test should never go outside of its own class boundary. As a result, the software developer abstracts an interface around the database connection, and then implements that interface with their own mock object. This results in loosely coupled code, minimizing dependencies in the system.

Limitations of unit testing
Unit testing will not catch every error in the program. By definition, it only tests the functionality of the units themselves. Therefore, it will not catch integration errors, performance problems or any other system-wide issues. In addition, it may not be easy to anticipate all special cases of input the program unit under study may receive in reality. Unit testing is only effective if it is used in conjunction with other software testing activities.
It is unrealistic to test all possible input combinations for any non-trivial piece of software. A unit test can only show the presence of errors; it cannot show the absence of errors. Though these two limitations apply to any form of software test.

Techniques
Unit testing is commonly automated, but may still be performed manually. As with many reputable standards bodies, the IEEE[1] prescribes neither an automated nor a manual approach. A manual approach to unit testing may employ a step-by-step instructional document. Nevertheless, the objective in unit testing is to isolate a unit and validate its correctness. Automation is efficient for achieving this, and enables the many benefits listed in this article. Conversely, if not planned carefully, a careless manual unit test case may execute as an integration test case that involves many software components, and thus preclude the achievement of most if not all of the goals established for unit testing.
Under the automated approach, to fully realize the effect of isolation, the unit or code body subjected to the unit test is executed within a framework outside of its natural environment, that is, outside of the product or calling context for which it was originally created. Testing in an isolated manner has the benefit of revealing unnecessary dependencies between the code being tested and other units or data spaces in the product. These dependencies can then be eliminated through refactoring, or if necessary, re-design.
Using a unit testing framework, the developer codifies criteria into the unit test to verify the correctness of the unit under test. During execution of the unit test(s), the framework logs test cases that fail any criterion. Many frameworks will also automatically flag and report in a summary these failed test cases. Depending upon the severity of a failure, the framework may halt subsequent testing.
As a consequence, unit testing is traditionally a motivator for programmers to create decoupled and cohesive code bodies. This practice promotes healthy habits in software development. Design patterns, unit testing, and refactoring often work together so that the most ideal solution may emerge.

posted @ Tuesday, June 03, 2008 10:16 PM | Feedback (0) |

Software + Services

So lately I've been thinking a lot about the next generation of IT. Software + services... While the concepts for Web 2.0 are intriguing many questions come to mind. What is a good candidate for Web 2.0 especially if it’s not maintained inside the corporate infrastructure?
The approach is very attractive but introduces many new obstacles, how do we ensure the integrity of transactions, data ownership issues, transparency. As services grow and mature they tend to change significantly over time with inevitable impact on subscribers. How do we continue to reduce TCO in this type of environment? Can subscribers create prescriptive contracts that prevent this from occurring? On the provider side of the house are there design patterns that allow for providing services flexibly to a multitude of subscribers without incurring the high costs of maintenance?
What is required here will likely turn out to cause providers to migrate in one of several directions.
The first option will be to provide only services that are either marginal or no value add to the majority of corporate businesses. Services that I see falling in grouping will likely fall within the IT space. Messaging services- IM, SMS, etc...
Level two services will likely provide services that are designed to replace existing data interchanges such as EDI. AS2 and Internet EDI really fill this space currently but I can foresee a day when the XML standards based spaces solidify and begin to define Contract based data exchange that will allow businesses to move away from internal EDI and or VAN based exchange. The main hurtle to overcome here will be timing as the standards based bodies do not move nearly as quickly as the remainder of the industry creating contention between technology and standards.
Finally, in the software +services arena there will be those who approach their services as core infrastructure that are extended through interfaces. These will be provided as the contract for subscribers that can be easily added to or extended and managed for each customers need. A great deal of effort will have to be invested in the design and architecture by those who choose this road. Companies that expend the effort up front to design for flexibility will be the winners in this space. Once established as a framework mission critical services as well as the more mundane service can be managed via a single infrastructure achieving economies of scale on the support model. Decisions to implement this strategy (enterprise wide vs. mission critical) will then become a simple question of economics.

posted @ Tuesday, June 03, 2008 9:50 PM | Feedback (0) |

SOA WCF and You Tech Ed Session

Today I attended an amazing session presented by Juval Lowy . I have to say in the last 5-8 years it was the most interesting and enjoyable session I have been to 4 Microsoft TechEd's, 3 SAP TechEd's, 2 Microsoft Mix's and 2 VS Live conferences. I enjoyed the historical walk down memory lane of software development. Having been in the industry for 20+  years it was interesting to see the patterns that Juval drew out.

A number of interesting thoughts were brought out during the day long conversation.

  • Object are inherently non reusable. Its the interface that is reusable.  Its about time in my office it seems I am the only one that has ever sung this song.
  • Refactoring - Its Evil Don't Do it EVER!!!!!
  • Plumbing code is even more EVIL. AHMEN but somebody has to write it... it does not just happen. I agree pick a vendor/framework and invest in it. If you don't like the way it works and you don't speak up don't complain either.

Juval spoke at length about the benefits and direction that he sees WCF headed towards in the future. He is convinced that WCF is finally the answer to universal reuse. While I tend to lean in the direction of supporting this thought I don't think that WCF is at that matured state that we should jump on the bandwagon. Maybe consider architecturally adopting the core principals of WCF but all classes should be WCF services. I am not ready to book the cruise quite yet. I am not convinced the Juval is either. While he is a staunch supported of WCF he is not short sighted or blind to the fallacies of the current implementation of WCF either.

Much like the references to WIN32, COM, and COM+ legacy of DLL hell and version hell the current incarnation of WCF has solved all of these problem by introducing a true interface based programming model. Additionally, all the bells and whistles that you get out of the box "For Free" -- well almost are an attractive draw. he pointed out that now we have the language we just do not have the platform to run it on. While I agree, there is little doubt that Microsoft will have to provide an MFC/ATL like set of helpers to bring everyone to the table. I also agree with Juval on another valuable point that he brought out more subtly and may have been missed by some. Each evolutionary step we have taken in Software development over the last 30 years has built on and in many cases solved the faults of the previous language construct. The new model introduce its own set of defects. As I look at WCF today it seems obvious to me that the faults WCF brings fall within two main areas.

  1. WCF out of the box if you need concurrence / reliability/ FIFO processing in a web farm it falls flat on its face. No doubt Microsoft is working to resolve that right now.
  2. The greater flaw I see in the WCF implementation in 2008 is the massive volume of configuration that needs to be maintained for all of the aspects that support the services. It would be at best daunting to maintain this in a "all classes should be services world" (I support the end game here by the way), but just as we should concentrate on the features because no one cares about the plumbing and Refactoring is evil because there is no Value Add only Value Minus. All this configuration on a corporate web farm has simply replaced DLL hell with Configuration hell for operations groups that have to maintain and synchronize it not to mention all the copies you need to keep for Dev Test QA and Prod. Lets hope Microsoft addresses this sooner than later.

See you all in the new world soon. Even with its faults the benefits in productivity will drive adoption and adoption will drive improvement and change.


posted @ Tuesday, June 03, 2008 9:44 PM | Feedback (0) |

Powered by: