Applied Software Engineering Using Apache Jakarta Commons (Charles River Media Computer Engineering)

In some frameworks, it is acceptable to create one factory class for every implementation that is to be exposed. However, doing so can very quickly become tedious . In addition, a hard reference to the implementation is created indirectly through the use of the helper class. For example, imagine an implementation class called MyActionImplementation; the factory class helper might be called MyActionFactory . If a new class such as MyActionNewImplementation is defined, will the factory class be called MyActionNewFactory ? If the naming of the factory follows the naming of the implementation, the client locks into specific implementations, which we hoped to avoid in the first place. In code terms, the problem of new implementations is shown in Listing 3.6.

Listing 3.6

interface InterfaceToBeShared { void availability(); } class SomeFunctionality implements InterfaceToBeShared { public void availability() { System.out.println( "Yes we have bananas today"); } } class SomeFunctionality2 implements IntefaceToBeShared { public void availability() { System.out.println( "Yes we still have bananas today"); } } class SharedFactory { public static InterfaceToBeShared createObject() { //return new SomeFunctionality();correct ??? //return new SomeFunctionality2(); correct ??? } }

 

In Listing 3.6, the interface InterfaceToBeShared is still the public interface. However, this time, we have the original implementation, SomeFunctionality , and the new one, SomeFunctionality2 . The latter class is a new implementation that is supposed to be used instead of the original implementation. The problem is in the class SharedFactory because the createObject method needs to be updated. Specifically, which object the method createObject should return is a mystery.

The quick and simple answer on which implementation to instantiate is the newest version, but that is incorrect and can cause problems. The problem is that there are bug fixes and system updates. A bug fix typically will not require you to define a new class. A system update will require more complicated changes, like the ones proposed in Listing 3.6. However, the complicated update is not supposed to impact the client. Remember, this goes back to the Commons Bridge best practice of separating interface and implementation. This means that returning an instance of the class SomeFunctionality2 could cause some consumers to react incorrectly because some bugs are fixed. Of course, one can argue that the consumer took into account how the interface reacted to certain conditions, which broke the Commons Bridge best practice. This is absolutely correct, but the problem is that we can argue like this for days and still not be closer to solving the problem of which class instance to return.

The correct approach generally is not to create a specific class factory for an instance. Instead, consider the factory as a task-specific undertaking. If you take this approach, it becomes simpler to insert new versions and at times use specific versions, as shown in Listing 3.7.

Listing 3.7

final class MultiObjectSharedFactory { static final int INITIAL_VERSION = 1; static final int UPDATED_VERSION = 2; public static InterfaceToBeShared anImplementation( int version) { switch( version) { case MultiObjectSharedFactory.INITIAL_VERSION: return new SomeFunctionality(); case MultiObjectSharedFactory.UPDATED_VERSION: return new SomeFunctionality2(); } return null; } public static InterfaceToBeShared anotherImplementation() { return new AnotherFunctionality(); } }

 

In Listing 3.7, the class SharedFactory has been changed and it called the class MultiObjectSharedFactory to indicate the varying capabilities of the factory. The class MultiObjectSharedFactory has its declaration changed to final , indicating that nobody can subclass the factory. Within the class are two methods to instantiate different implementations that support the interface InterfaceToBeShared . The method anotherImplementation is similar in purpose to that of the createObject method of the class SharedFactory . The method anImplementation is entirely different than previous object creation methods because it requires a parameter, which is an integer value. The parameter represents a version number of which object to instantiate. For the old implementations, the initial version will be used. However, the newer version will be used for the new implementations.

When you use this approach to solve the factory problem, the objective is not to look at the details of the implementation. Instead, the objective is to notice that the factory is a task-based class used to instantiate specific objects. This means a factory for one subsystem will not be identical to a factory for another subsystem. This problem of figuring out the correct implementation of a factory is an issue regardless of the way that the object is instantiated . In later sections of this chapter, we will show you different factory approaches, but they do not solve the problem of cross-referencing the instance with the client. The different factory approaches make it simpler to develop a generic framework.

Категории