Problem:
What is an effective design pattern to allow an end user to indirectly modify the state of data in an object based on a prioritized list of user specified modifers?
Explanation : This is very general of course, but hang in there for a second so I can elaborate - I'm not a software person by trade so I don't know the appropriate language to use. Say I have a class called 'Grid' who's purpose is to:
- Store a regular 2D Cartesian grid of 'colors'
- Allows the user to initialize all the colors to some state
The Grid tracks the "evolution" of the colors. By this I mean the state of the data in the grid can change by some function Grid(n+1) = f( Grid(n) ). Where F( Grid(n) ) is a function that transforms a grid at step n to step n+1. An example of this could be
if a node to my right is red and I am not red:
then
swap colors
else
do nothing
So a "grid" at step n that looks like this:
BLUE BLUE BLUE
BLUE BLUE RED
BLUE BLUE BLUE
Will look like this at step n+1
BLUE BLUE BLUE
BLUE RED BLUE
BLUE BLUE BLUE
And at n+2 this:
BLUE BLUE BLUE
RED BLUE BLUE
BLUE BLUE BLUE
I will leave this F( Grid(n) ) as an opaque process because this is not where I have a design issue - I do mention this so that some design decisions I make in the future will have some context.
- The user is allowed to modify the data at every step via a multiset that uses a comparison object based on a priority. The multset contains an object that somehow modifies the state of my grid of colors.
Ok What do I mean by this? Take a look :
class Grid {
private:
vector< vector< colors > > colorGrid;
multiset< shared_ptr< SetGrid >, comp > setRules;
// ... //
public:
Grid( dims d2 ); // dims is a typedef for pair<int,int>
// ... //
void addSetRule( shared_ptr< SetGrid > sg );
// ... //
void applyRules( void );
};
Where I have defined SetGrid to be an ABC:
class SetGrid {
protected:
int priority;
colors c;
public:
SetGrid(colors clr ) : c(clr){};
int getPriority() const;
virtual void apply( vector< vector< colors > > & cg) const=0;
};
The 'comp' object in the multiset declaration compares priorities through the getPriority() member function. So now you may see whats going on. Classes Derived from SetGrid can change the state of coloGrid by Grid::applyRules() function:
void Grid::applyRules( ){
multiset< shared_ptr< SetGrid >,comp >::iterator it;
for (it = setRules.begin(); it != setRules.end(); ++it)
(*it)->apply( colorGrid );
}
The multiset orders items it contains base on their priority. So at this point a user could derive a class from SetGrid to set all colors in a users specified column to RED and derive another class from SetGrid whos function is to set a subset of a row to GREEN after RED is set. Example:
G(n):
BLUE BLUE RED
BLUE RED BLUE
BLUE BLUE BLUE
setRules now contains a "set columns to RED" object with high priority and a "set a subset of a row to GREEN" with lower priority, so the state changes as follow:
set column to RED ( say I set column 0 to RED) :
RED BLUE RED
RED RED BLUE
RED BLUE BLUE
And now set a subset of a row GREEN (say row 1, items 0 and 1):
RED BLUE RED
GREEN GREEN BLUE
RED BLUE BLUE
And now we can apply our Function F( G(n) ) -> G(n+1) and repeat this process however many times we like or until something bad happens.
So that is the functionality I desire from this 'Grid' class - What is the name of the design pattern I have used/what is a better design pattern to achieve this goal? I feel like what I have done is acceptable - but I hope there is a more elegant solution that is more expressive/compact than what I have.
A full listing of my simple example is on this codepad page - Note that I compiled with g++4.5 -std=c++0x -Wall (no optimizations) and use things defined in the new c++11 standard. I did not put it here because in total the full code is ~160 lines, a little too much for the eyes on one page.