Product interface method and system which allow class evolution6058396Abstract The present invention provides for an object-oriented software product interface method which gives a tighter coupling between the software product interface and internals than traditional methods. The product interface scheme includes a Factory Class (44) and an Interface Class (46) which control object life cycles; bend compile-time time and runtime type checking; bind virtual abstract interface classes and hidden internal class hierarchies through multiple inheritance; and provide interface parameter abstraction. The present invention thus provides users of the software product with a stable product interface while preserving the designer's freedom to modify the software product's internal architecture. Claims What is claimed is: Description TECHNICAL FIELD OF THE INVENTION
______________________________________
class WorkerIF {
public:
WorkerIF ( );
virtual .about. WorkerIF( );
virtual Employee * RequestBoss( )=0;
virtual int GetWages (float time)=0;
};
class Worker: public WorkerIF {
public:
Worker (char* Name);
virtual .about. Worker( )
Employee *RequestBoss( );
virtual int GetWages (float time);
Bool InternalWork (float duration, int payScale);
protected:
char *name;
float time:
}:
______________________________________
The Application Program 40 sees only the WorkerIF interface when using a Worker object. As shown hereinabove, the WorkerIF class insulates the Application Program 40 from the internals of the Worker class while advertising Worker's product services. The Worker class can have methods which are not exposed external to the Library 48. The Application Program 40 can not see or use internal methods of the Worker class. The Application Program 40 sees only the methods exposed in the WorkerIF (Interface) class. The WorkerIF class defines the Library 48 services as pure virtual functions, which force any class derived from this WorkerIF to supply implementations of the pure virtual functions. Since the Worker class inherits the WorkerIF interface class, inheritance binds the Worker class to provide implementations of the methods, or services, advertised by WorkerIF. For example, in the example shown hereinabove, the RequestBoss and GetWages interface functions are defined by WorkerIF. The Worker class implements the RequestBoss and the GetWages interface function defined by WorkerIF. Worker also provides an InternalWork method used by the Library's 48 internal objects which is not defined in WorkerIF, and therefore is not visible to the Application Program 40. The WorkerIF class is abstract and, therefore, can not be created by the Application Program 40. Therefore, a Factory Class 42 pattern is introduced to perform object creation as illustrated in the block diagram in FIG. 5. The Factory Class 42 is also introduced in process step 27 in FIG. 3, creating the WorkerFactory that embodied all parts, tools, and the rules of construction to build a Worker object. The WorkerFactory contains a build method which is invoked by the Application Program 40 to create a Worker object which is returned as a WorkerIF handle.
______________________________________
class WorkerFactory {
public:
WorkerFactory (char *name);
.about.WorkerFactory( );
virtual WorkerIF *build( );
char *name;
};
______________________________________
The method of the present invention insulates the user from the object internals with an abstract interface class and a factory class. However, a class with many configurable parameters can quickly require bloated constructor signatures. The method of the present invention resolves this issues by modifying the Worker constructor to receive the WorkerFactory object as shown in FIG. 3 at block 29 and in the Worker class definition below.
______________________________________
class Worker: public WorkerIF {
public:
Worker (WorkerFactory *fact);
virtual .about. Worker( )
Employee *RequestBoss( );
virtual int GetWages (float time);
Bool InternalWork (float duration, int payScale);
protected:
char *name;
float time:
}:
______________________________________
Since the WorkerFactory contains all the parts, tools, and assertions needed to build a Worker, the WorkerFactory object is all the Worker constructor needs for a successful object instantiation. The Worker class can also be used as a base class to derive different types of Workers. In this example, a second Internal Class 48, Carpenter 61, is derived from the first Internal Class 48a, Worker 60, which also provides external and internal Library 48 services. See FIG. 6. In this example the Worker class provides an implementation of the RequestBoss interface method. However, the virtual GetWages interface function defined by WorkerIF is not implemented by the Worker class but is deferred to the derived Worker type, Carpenter 61.
______________________________________
WorkerIF( );
virtual .about. WorkerIF( );
virtual Employee * RequestBoss( )=0;
virtual int GetWages (float time)=0;
};
class Worker: public WorkerIF {
public:
Worker (WorkerFactory *fact);
virtual .about. Worker( )
Employee *RequestBoss( );
virtual int GetWages (float time)=0;
Bool InternalWork (float duration, int payScale);
protected:
char *name;
}:
class Carpenter: public Worker {
Carpenter (tool *screwdriver, part *screw, char
*name);
virtual .about. Carpenter( );
int GetWages(float time);
protected:
float time:
tool *screwdriver;
part *screw;
};
______________________________________
With polymorphism, WorkerIF 60 can represent as many forms of Worker as there are derivations, or types of Workers, in the Library 48, for example, Plumber, Electrician and Carpenter Workers. Each of these derivations may need different initialization parameters. Using the present invention, a user is able to create and use the desired Worker type derivations without knowledge of its internal structure or how many derivations exist. However, the user cannot escape the responsibility of knowing the Worker type they are creating and the inputs needed by that Worker type upon construction. A CarpenterFactory 71 is created to understand the parts, tools, and rules of construction used to build a Carpenter object. The Carpenter Factory 71 is derived from the Worker Factory 70, and therefore, holds the rules of construction for both the Carpenter class and the Worker class.
______________________________________
class WorkerFactory {
public:
WorkerFactory (char *name);
.about.WorkerFactory( );
virtual WorkerIF *build( );
char *name;
};
class CarpenterFactory: public WorkerFactory {
public:
CarpenterFactory (tool *screwdriver, part screw, char
*name);
.about.CarpenterFactory( );
WorkerIF *build( );
Error *ChangeTools (tool *screwdriver, part screw);
protected:
tool *screwdriver;
part screw;
};
______________________________________
WorkerFactory defines the virtual function build(). Classes that derive from WorkerFactory can provide their own implementation of build() to create their associated objects. An Application Program 40 invokes a Factory Class 44 build() method to create objects from an Internal Class 48. Thus, the rule that the Application Program 40 must create an instance of a Factory Class 44 to create an instance of an Internal Class 48 is introduced. However, a Factory object is needed only for object construction since it returns a real and whole Internal Class 48a object. The Internal Class 48a object created by the Factory Class 44 object can be used without the aid of any additional objects. The parts and tools needed for the Factory object to build an Internal object cannot be abstracted away from the Application Program 40. If the Carpenter needs a screwdriver, the Application Program 40 must know it and provide it. Therefore, the instantiation of each Factory Class 44 requires the same parts and tools needed by its product. The Worker requires, but has no constraints on, a name. Therefore, the WorkerFactory constructor requires a name parameter, but the name instance variable is public and can be changed at will by the Application Program 40. The CarpenterFactory 71 has the constraint that the screwdriver type must match the screw type, for example, Phillips. Therefore, CarpenterFactory 71 enforces this matching rule in its constructor and in the ChangeTools() method. Protecting the screwdriver and screw instance variables guarantees the Application Program 40 cannot update them without CarpenterFactory 71 performing assertion checks. Consistent assertion checking ensures screwdriver and screw are a valid pair before the Carpenter constructor receives them. Normally a class cannot perform parameter checking or assertions until the operating system allocates its memory and enters its constructor. Factory Class 44 objects are lightweight and can be created on the stack while Internal Class 48a objects exposed as Interface Class 46 objects are potentially heavy and are created on the heap. Therefore efficiency is gained by performing the Internal Class 48a object's assertion and parameter type checking in its Factory 46. Since a Factory 46 retains sole rights to creation of its Internal Class 48a object, it can also enforce quota or singleton constraints by restricting the number of objects created. Furthermore, once created, a single Factory 46 object can produce multiple internal objects. The configuration of the Internal Class 48a object created depends on the state of the Factory 46 when the Application Program 40 invokes the build() method. The Application Program 40 can set the Factory's 46 state variable values then perform many invocations of build() or change the Factory's 46 state variable values between each invocation of build(). So the Factory 46 assists in creation management of objects with many configurable parameters. The Factory Class's 46 job is to handle the parts, tools, and assertions needed to build their Internal Class 48a object. Since the CarpenterFactory inherits the WorkerFactory, the CarpenterFactory must be sensitive to changes in the creation part requirements for a Worker class. However, the Carpenter derived internal class is also vulnerable to changes in the creation requirements of the base Worker internal class. To eliminate this constraint, the Carpenter constructor is modified to receive the CarpenterFactory object.
______________________________________
class Worker:
public WorkerIF,
public Employee{
public:
Worker (WorkerFactory *fact);
virtual .about.Worker( )
. . . };
class Carpenter: public Worker {
Carpenter (CarpenterFactory *fact);
virtual .about.Carpenter( );
. . . };
______________________________________
Each factory encapsulates the configurable parameters needed to build its interface object. Passing the CarpenterFactory to the Carpenter constructor maintains a skinny signature on the constructor regardless of future additions or changes. Additionally, since the CarpenterFactory derives from the WorkerFactory, the Carpenter constructor can extract the WorkerFactory object from the CarpenterFactory object. The WorkerFactory object can then be passed to the Worker constructor with no knowledge of its contents other than that imposed by the compiler. This insulates all classes derived from Worker from changes in Worker's instantiation requirements. Finally, since Worker has delegated assertions necessary for its creation to the WorkerFactory object, the Carpenter's constructor can be certain the data passed to the Worker constructor is legal. Products can extend the interface in several ways as needed. Some product architectures, for example, need to control deletion of interface objects created by its users. In this case, the method of the present invention can be extended to protect the Interface Class's 46 destructor and add a static destroy() function to the Interface Class 46. static void destroy (WorkerIF *wrk); The Application Program 40 invokes destroy() on Interface Class 46 objects that are no longer need. However, destroy() can simply set the object to a deleted state. This implementation delays actually deletion of the object until all internal cleanup in completed. The product interface scheme also supports evolution of an Internal Class 48a object's construction. Suppose, for example, the Worker class is extended to allow an additional overtime parameter. This necessitates the addition a new constructor to Worker and WorkerFactory to allow users to exploit the new feature. However, Application Programs currently using the Library 48 and not needing the overtime capabilities should not have to recompile to accommodate the new version of WorkerFactory. Thus, to protect the current Application Programs, a NewWorkerFactory is derived from the existing WorkerFactory. FIG. 7 shows Factory evolution with a second derived NewWorkerFactory 71 which provides the new constructor parameter. New Application Programs 40 would include the NewWorkerFactory with the new features while existing Application Programs 40 continue to use the old WorkerFactory. The new Worker constructor accepts a NewWorkerFactory object while the existing Worker constructor continues to accept an old WorkerFactory object. Thus, the compiler aids in managing class evolution. The same pattern can be used in evolving the WorkerIF interface 78 to accommodate the addition of new services. FIG. 8 shows interface evolution with the inclusion of a second WorkerIF Interface 90. The second interface 90, for example, exposes new services provided by the base Worker objects. Thus, the present invention provides for a powerful interface scheme. Internal Classes 48a can evolve at will provided they continue to support the operations advertised by the Interface Classes 46. The compiler (for example a C++ compiler) oversees the Internal Classes' 48a responsibility to Interface Classes 46 by virtue of inheritance. Internal Classes 48a can continue to leverage the power of Internal Class hierarchies by using multiple inheritance of the Interface Class 46 and other Internal Classes. Application Programs 40 wield Factory Class 44 objects to create multiple instances of an Internal Class 48a. Factories Class 44 objects manage the configuration parameters and enforce the rules of constriction for their associated Internal Class 48a. And derived Internal Classes 48a are insulated from their base Internal Classes' 48a construction parameters and creation assertions by the base class's Factory 44. The compile-time benefits of the interface scheme of the present invention combined with the Factory 44 runtime assertion checks provides a robust interface. Tight coupling between Interface Class 46 and the Internal Class 48a is achieved (virtual functions ensure compliance at compile time). Build assertions are applied before storage allocation. "Skinny" build signatures are provided for (parameters reduced to state of factory). Finally, evolution management techniques are applied which preserve backward compatibility. Although the present invention and its advantages have been described in detail, it should be understood that various changes, substitutions and alterations can be made herein without departing from the spirit and scope of the invention as defined by the appended claims.
|
Same subclass Same class Consider this |
||||||||||
