Enterprise Application FrameworX
       
(5) Extensibility
Extensibility is defined as the feature of an Application or Module to accommodate increased functionality with minimal impact to the existing codebase or functionality, and with increased re-usability.

Extensibility depends directly on Modularity. Code should be designed in the form of individual blocks, each of which performs its distinct atomic function. This simply means that we should refrain from writing long procedural blocks of code that execute a complex sequence with a lot of branching, looping and especially control transfer. Instead, it should be broken into a set of individual method invocations. That way, the code is readable and broken down into segments and if there is a need to accommodate another step, or different functionality, it is possible to do so by injecting another method call and adding a new method as opposed to injecting a block of code to a pre-existing block of code.

In addition to modularity, Inheritance is critical to sharing code across a set of classes without having to incorporate the shared code in each module. One should implement functionality that is shared by numerous classes in a base class. It may not be possible for every class to have multiple base classes, in which case using of strongly typed interfaces is recommended.

For example, if we want each screen in our application to support hibernation – the ability to persist the application to disk and take off at any point, it becomes critical that the Screen classes (Forms) in the application implement an interface that contains a Serialize method (like ISerializable) which forces every screen to implement the Serialize method, thus enforcing the ability to persist to disk. It may be impossible to ensure that the implementation of serialization in our example is going to work. The only way to ensure that is to govern the Unit test cases and code itself using the enforced SDLC Steps as we outlined earlier in this document.

We shall illustrate this with a real life example. Let us say that we have a Customer class in our application which implements a Search() method that returns an array of CustomerDetail class instances based on valid search criteria. Now imagine that you have extended your application to also enable search in a business partner's database. Example being the automotive industry. Your web site currently services GM customers. Now, all of a sudden BMW and Mercedes want you to handle their customers also. You would create a generic Customer base class and create three specialized classes - GMCustomer, BMWCustomer and MercedesCustomer, all three implementing the Search() method against the specific database. Again BMW may not want to share the data with you, but might want you to access their web service instead, while GM offers a data feed into your table. One Behavior - Multiple implementations. With inheritance, you can make this happen. Your application can continue using a reference to the Customer class and not have to worry about the actual implementation (extension).

The above example also illustrates Polymorphism - The ability to implement multiple implementations of code and enable as single invocation or instantiation to utilize different implementations based on context. Polymorphism is illustrated in two manners in the above class example.

Instantiation-driven: For one, depending on the system context during construction, the application can use a reference to the Customer class to either instantiate an instance of the BMWCustomer, or an instance of the GMCustomer class. This means that results can be obtained from a specific system with a different search implementation based on system.

Note that you can also model classes to use these different implementations of the Customer class to aggregate the results of the customer search. What if your search screen wanted to search across systems? This aggregation could not be achieved without the use of a Factory Class or a specific CustomerSearch class, which in turn invoked the search on each of the individual implementations and returned the aggregated result in the form of an array of references of type CustomerDetails.

Invocation-Driven: Understand that the Search method itself can contain several specific implementations depending on the order and type of arguments used - Method overloading. It is obvious that the valid return type for all these search implementations should be an array of references to the type CustomerDetails, but depending on the arguments, different method implementations will be invoked. This is the second type of polymorphism that the above example demonstrates.

While there are multiple implementations of Customer, and there are multiple ways to implement this search, based on parameters, some criteria remain common to all the implementations by class and by method. This is where inheritance promotes reusability. You can implement common functionality like connecting to the database (ODBC), retrieving the connection string, decrypting data, etc into the base class Customer, thus enabling every implementation of the class to not worry about implementing the base features. This can greatly increase productivity.
©Eafx, LLC.
1999-2010, All Rights Reserved