Acquaman Coding Paradigms and Patterns - acquaman/acquaman GitHub Wiki
This page details some of the coding patterns and paradigms used in Acquaman, in order to assist with usage of classes, inheriting from classes, or simply to provide a guideline for how to approach the creation of classes in the future.
Model/View
Acquaman uses an adapted Model/View paradigm for all classes. Classes are either designed to model a concept, or display that modelled concept to a user. In general:
- Model classes do things
- View classes visualize things
- One model instance can be viewed by many view instances, or different types of views (one-to-many)
- One view may only look at one model (one-to-one)
Acquaman uses a slightly modified model/view paradigm from the one in Qt, however:
- All classes that aren't views can be considered models
- Views are generally passed a model in their constructor, listen to signals from the models to change their display, and call slots to update values in the model with they are edited
- Can use composition - One model may be made up of one or more other models
- One view can instantiate the views for its model's composite models
State Machines
Many of the classes in Acquaman represent state machines. In general the approach in Acquaman has been to allow subclasses to handle as much of the responsibility for state switching as possible, using implementation functions. This, for instance, will allow specific types of scan controllers to run processes specific to them should a user choose to cancel them.
class AMScanController{
[...]
void cancel(); // Calls cancelImplementation
virtual void cancelImplementation() = 0;
[...]
};
The pure function means that a ScanController which inherits from AMScanController has to specify a cancelImplementation:
class SGMSuperScanController : public AMScanController{
[...]
void cancelImplementation(); //Specifies what a SuperScan should do when cancelled
[...]
};
Singletons
Many of the classes in Acquaman use the singleton pattern, eg There is only one SGM beamline, and it should be globally accessible from anywhere within SGM Acquaman's code. In order to create and use a singleton class, the usual convention is used in Acquaman:
- Include a static instance of the class within itself, marked either protected or private
- Ensure the constructor for the class is private
- Create a public getter function which initializes the instance, if it hasn't been already, and returns it
Creation:
class ExampleSingleton{
private:
static ExampleSingleton* exampleInstance_;
ExampleSingleton(); //Constructor is private, to ensure only place that the singleton can be constructed is within the class itself
public:
static ExampleSingleton* s() {
if(exampleInstance_ == 0)
exampleInstance_ = new ExampleSingleton(); //Only place constructor is ever called
return exampleInstance_;
}
};
Usage:
#include "ExampleSingleton.h"
[...]
ExampleSingleton* referenceToSingleton_ = ExampleSingleton::s();
[...]
This ensures that where ever ExampleSingleton::s() is called from, it was always refer to the same object as any other call.
Registrations
Some Acquaman classes use a registration system to manage a relationship between co-ordinated types. This is usually managed through a 'support' class, which has a templated register function within it.
Registering:
AMAppControllerSupport::registerClass<SGMXASScanConfiguration, AMExporterGeneralAscii, AMExporterOptionGeneralAscii>(matchIDs.at(0), dbSGM->connectionName());
Calling:
AMExporterOption* autoExporterOption = AMAppControllerSupport::createExporterOption(config);
Const Keyword
- Like Qt, Acquaman makes use of the const keyword
- Many classes have member functions that are declared
const QString name() const;
This indicates that calling this function won’t make any changes to the instance you call it on
- Acquaman also returns many const objects (or const pointers to objects) ~ This means that you can access some member, but you are not allowed to change it
Note The const keyword is pervasive: if a function returns a const pointer you can only call const functions on it
Const lValue References
As with Qt, many object values are passed to functions as const lValue references. This allows Qt to avoid unnecessary copying of parameters which represent potentially large data classes. As such class instances are often passed to functions with the argument format:
void someFunction (const SomeClass& lValueRefToClass);
Copying of lValueRefToClass will be avoided unless the operator overload = is called:
void someFunction (const SomeClass& lValueRefToClass)
{
lValueRefToClass.isEmpty(); //This call to member function isEmpty does not prompt a copy. IsEmpty() can only be called if it specifies it is const ( bool SomeClass::isEmpty() const; )
memberVersionSomeClass_ = lValueRefToClass; //This calls operator =() for someClass (usually performing a deep copy)
}
Copying will also be performed on any class which is stored as a member variable, and then returned through some getter:
class SomeClass {
public:
SomeOtherClass memberOtherClass() const {
return memberOtherClass_; // This call will invoke the copy constructor for SomeOtherClass.
}
private:
SomeOtherClass memberOtherClass_;
};
Note At the moment the version of Qt used for Acquaman does not inherently support the C++11 standard, as such move semantics (as defined in move constructors being passed rValue references) are not supported.