Здравствуйте
Давно хотел начать писать свой блог, но никак не мог придумать с чего начать.
Проблема о которой я напишу - не надуманная, а вполне реальная. Выглядит она так:
- class SomeMenu : public Menu
- {
- ...
- virtual void Command( const char* command, const char* arg )
- {
- if ( strcmp( command, "Command1" ) == 0 )
- {
- // some commands
- }
- else if ( strcmp( command, "Command2" ) == 0 )
- {
- // ...
- }
- else if ( ... )
- {
- ...
- }
- ......
- else
- {
- Menu::Command( command, arg );
- }
- }
- };
Уверен, что многие читающие не видят в этом проблему и отчасти они правы. Если условий не больше 10 и в каждом по 2-3 команды, то такой код, действительно, очень прост и довольно легко поддерживается. А теперь представьте, что условий стало больше 50-100 и в половине из них больше десятка строк, а код подлежит дальнейшей модификации и поиску ошибок...
Сразу становится понятно, что нужно что-то делать...
Для решения этой проблемы, я написал такой шаблон:
- template<typename T>
- class DefaultCompareFn
- {
- public:
- bool operator ()(T op1, T op2) const
- {
- return op1 == op2;
- }
- };
- template<typename Key, typename Value, class KeyCompare = DefaultCompareFn<Key> >
- class Factory
- {
- Value mResult;
- KeyCompare mCompFn;
- public:
- struct Item
- {
- Key key;
- Value value;
- };
- Factory( const Item* items, size_t size, Key key, Value defaultValue ) :
- mResult(defaultValue), mCompFn()
- {
- for( size_t i = 0; i < size; i++ )
- {
- if (mCompFn(items[i].key, key))
- {
- mResult = items[i].value;
- break;
- }
- }
- }
- operator Value()
- {
- return mResult;
- }
- };
Применяется это так:
- struct strcomparer
- {
- bool operator()( const char* str1, const char* str2 ) const
- {
- return strcmp( str1, str2 ) == 0;
- }
- };
- class SomeMenu : public Menu
- {
- ...
- typedef void (SomeMenu::*CommandFn)(const char*);
- typedef Factory<const char*, SomeMenu::CommandFn, strcomparer> SomeMenuFactory;
- static const SomeMenuFactory::Item sCommandHandlers[];
- static const size_t sCommandHandlersSize;
- void Command1(const char* arg)
- {
- ...
- }
- void Command2(const char* arg)
- {
- ...
- }
- void Command(const char* command, const char* arg)
- {
- CommandFn fn = SomeMenuFactory( sCommandHandlers, sCommandHandlersSize, command, 0 );
- if (fn != 0)
- {
- (this->*fn)(arg);
- }
- else
- {
- Menu::Command( command, arg );
- }
- }
- };
- const SomeMenu::SomeMenuFactory::Item SomeMenu::sCommandHandlers[] =
- {
- { "Command1", &SomeMenu::Command1 },
- { "Command2", &SomeMenu::Command2 },
- };
- const size_t SomeMenu::sCommandHandlersSize = sizeof(SomeMenu::sCommandHandlers)/sizeof(SomeMenu::sCommandHandlers[0]);
Но у этого кода есть недостаток - функции CommandX не могут быть виртуальными или расположенными вне класса. Надеюсь в будущем устранить этот недостаток...