AHEAD, as discussed in the previous article defines the following tasks for generating complete software from features. .
A. define numbers of features
B. semantics to create an algebraic equation from features (feature refinement)
C. define a composer to compose the final feature out of the equation and produce the final software (assuming composer performs the final compilation)
This approach requires the selection of different features available to be specified in compile time. Every new refinement would require step B and C to be repeated.
The purpose of this short article is to show a different FOP approach where feature selection happens in runtime. In this approach – all the features are loaded at compile time and the selection of features is deferred till we run the application. The user is provided with a configuration that is read by the program to activate only those features specified in the configuration. By this we can complete the requirement of different feature selection by providing different configuration with the same executable. This ensures that we reduce the time spent in compilation for different combinations of features.
Sample Implementation
The implementation I am currently using is inspired by the “registry of singleton” mentioned in Design Patterns – Elements of Reusable Object-Oriented Software (GOF).
All features are derived from a base class – Feature. Every feature (Feature1, Feature2), in this example, assigns a different value to printStr. The features are registered with a FeatureManager that acts as a store house for features. The user specifies the features he wants to use as a command line input to the test program.
You may download the source and makefile from this link.
Base class – Feature.h
#ifndef _F_H_
#define _F_H_
#include<iostream>
class Feature
{
public:
virtual void print()
{
std::cout << printStr << std::endl;
}
protected:
std::string printStr;
};
#endif
File: Feature1.h
#ifndef _F1_H_
#define _F1_H_
#include<iostream>
#include “FeatureManager.h”
#include “Feature.h”
class Feature1 : public Feature
{
public:
Feature1()
{
printStr = “1″;
FeatureManager::Register(printStr ,this);
}
};
#endif
File:Feature1.cpp
#include “Feature1.h”
static Feature1 f1;
File: Feature2.h
#ifndef _F2_H_
#define _F2_H_
#include<iostream>
#include “FeatureManager.h”
#include “Feature.h”
class Feature2 : public Feature
{
public:
Feature2()
{
printStr = “2″;
FeatureManager::Register(printStr,this);
}
};
#endif
File: Feature2.cpp
#include “Feature2.h”
static Feature2 f2;
File: FeatureManager.h
#ifndef _FM_H_
#define _FM_H_
#include<iostream>
#include<map>
#include “Feature.h”
class FeatureManager
{
public:
static void Register(const std::string& name, Feature* f);
static FeatureManager* GetInstance();
static Feature* Lookup(const std::string& name);
private:
static FeatureManager* _instance;
static std::map<std::string, Feature*> _registry;
};
#endif
File: FeatureManager.cpp
#include “FeatureManager.h”
FeatureManager* FeatureManager::_instance = NULL;
std::map<std::string,Feature*> FeatureManager::_registry;
FeatureManager* FeatureManager::GetInstance()
{
if(_instance == NULL)
{
return (_instance = new FeatureManager());
}
else
return _instance;
}
void FeatureManager::Register(const std::string& name, Feature* f)
{
_registry[name] = f;
}
Feature* FeatureManager::Lookup(const std::string& name)
{
std::map<std::string,Feature*>::iterator it = _registry.find(name);
if( it != _registry.end())
{
return (*it).second;
}
else
{
return NULL;
}
}
File: test.cpp
#include <iostream>
#include “FeatureManager.h”
#include “Feature.h”
int main(int argc, char **argv)
{
FeatureManager * mgr = FeatureManager::GetInstance();
//loop through the features provided
for(int i = 1; i<argc; i++) {
Feature *f = mgr->Lookup(argv[i]);
if(f)
f->print();
}
}
Test run
$ ./test 1 2
1
2
$ ./test 2
2
Comments