I've implemented an undo /redo framework and have a set of command classes that support changing various attributes of the character class. There are several 10+ derived classes each of which is almost exactly the same except for the specific attribute of the character class it operates on.
I would like to push this cookie cutter code into the base class and somehow have the derived class supply the member to operate on. Is this possible?
Note that some of the attributes are bools and some are doublesclass character
{
public:
double attribute1;
double attribute2;
do开发者_Go百科uble attribute3;
double attribute4;
//etc
bool boolattribute1;
bool boolattribute2;
//etc
};
class CharacterCommand { }
class CharacterCommandAttribute1 : public CharacterCommand { }
class CharacterCommandAttribute2 : public CharacterCommand { }
class CharacterCommandAttribute3 : public CharacterCommand { }
class CharacterCommandAttribute4 : public CharacterCommand { }
void CharacterCommandAttribute1::redo()
{
std::vector<character*> chars = this->textbox->getSelectedCharacters(...);
for(size_t c=0;c<chars.size();++c)
{
character * ch = chars[c];
ch->attribute1 = this->doValue;
}
}
void CharacterCommandAttribute1::undo()
{
std::vector<character*> chars = this->textbox->getSelectedCharacters(...);
for(size_t c=0;c<chars.size();++c)
{
if(c < this->undoValues.size())
{
character * ch = chars[c];
ch->attribute1 = this->undoValues[c];
}
}
}
void CharacterCommandAttribute2::redo()
{
std::vector<character*> chars = this->textbox->getSelectedCharacters(...);
for(size_t c=0;c<chars.size();++c)
{
character * ch = chars[c];
ch->attribute2 = this->doValue;
}
}
void CharacterCommandAttribute2::undo()
{
std::vector<character*> chars = this->textbox->getSelectedCharacters(...);
for(size_t c=0;c<chars.size();++c)
{
if(c < this->undoValues.size())
{
character * ch = chars[c];
ch->attribute2 = this->undoValues[c];
}
}
}
Edit To clarify: Each of the specific command classes is derived from a base class and provide overrides for pure virtual undo and redo functions. These specific classes are stored in a QUndoStack that calls the command's virtual redo or undo as appropriate.
Each specific command is responsible for the functionality of a single user interface attribute setting for a set of 'characters' (the current selection). The specific command stores the existing state of the attribute of each character in a vector and uses these to provide undo state.
Everything works fine; I just feel that there is too much copy & paste repetitive code with only significate difference being the
ch->attribute2 = this->doValue; (refer to ::redo above)
and
ch->attribute2 = this->undoValues[c]; (refer to ::undo above)
Keep in mind that the 'attribute2', 'doValue', and undoValues vector types are the same within a given derived class but vary between the set of derived classes. This is the detail that prevents me from moving almost everything into the base class.
You should be able to implement the undo/redo methods inside your base class, and if you give all your classes public derivation from the base class, you'll be able to modify the underlying data without having to derive a hundred new classes.
For instance, this will work fine:
class base {
public:
int x;
void test() {x = 1;}
};
class derived : public base { };
int main() {
derived temp;
temp.test();
return 0;
}
If you want to have one function be able to effect all the char attributes (and another for all the bools), this should work:
class base {
public:
int x;
char y;
void change_char(char& attr) { attr = ';'; }
void test() {x = 1;}
};
class derived : public base { };
int main() {
derived temp;
temp.change_char(temp.y);
return 0;
}
Hope this helps.
The solution I came up with seems to work out very well. I'm going to post it for other's reference and to see the community response. Thanks
I changed the CharacterCommand base class into a template class:
template<typename DO_TYPE, typename UNDO_TYPE>
class CharacterCommand : public TextBoxCommand
{
public:
...
//pure virtual functions that have to be overloaded in the base class
virtual UNDO_TYPE getUndoValue(xite::Character * ch) = 0;
virtual void performDo(xite::Character * ch, const DO_TYPE& doValue) = 0;
virtual void performUndo(xite::Character * ch, const UNDO_TYPE& undoValue) = 0;
protected:
int startSel;
int endSel;
std::vector<UNDO_TYPE> undoValues;
DO_TYPE doValue;
};
An example derived class:
class CharacterHScale : public CharacterCommand<double,double>
{
public:
...
double getUndoValue(xite::Character * ch);
void performDo(xite::Character * ch, const double& doValue);
void performUndo(xite::Character * ch, const double& undoValue);
...
};
In this way the base class is responsible for acquiring and manipulating the set (std::vector) of target characters and the derived class is reponsible for setting / unsetting the single attribute via the virtual performDo and performUndo functions that each derived class must implement.
精彩评论