重构违反“无需重复”原则的代码

时间:2016-05-28 14:25:30

标签: c++ templates inheritance member-function-pointers

Assume class Animal which does Eat, Sleep and Make Noise.
Assume class Mammal : public Animal which also does MakesBaby
Assume that Mammal also does Eat, Sleep and Make Noise which it inherits from Animal.

此代码扩展自this question中提供的代码,Dan Masek昨天回答了该代码。

此代码的问题是:

  1. 哺乳动物不能继承Animal(编译错误)
  2. 哺乳动物不能进食,睡眠或吵闹
  3. 代码遭受“不必要的重复”,因此遇到了鲍勃·马丁叔叔的嘲笑和嘲笑。
  4. 我希望这段代码尽可能优秀,但我被困住了。

    我认为两个类之间的相似性围绕着定义函数的typedef,而函数是指向类中成员的指针。我试着模仿这个,但遇到了很多编译错误,从而表明我完全无法理解如何解决这个问题。因此这篇文章。

    对于那些看过我上一篇文章的人,我添加了一个#ifdef来禁用打印(这是Arduino代码,所以STD不可用),我尽可能地添加了断言。我还添加了哺乳动物课程。

    //  Set to false to suppress Serial.print output
    #define TRACING true
    class Animal
    {
    public:
        enum {
            CMD_EAT = 1
            , CMD_SLEEP
            , CMD_MAKENOISE
            , COMMAND_COUNT
        };
    
        //  Define a pointer to a function within this class that takes
        //  an INT as its argument
        typedef void(Animal::*animalFunc)(int); // U THING THIS IS THE PROBLEM - CAN IT BE TEMPLATED? IF SO, I COULD NOT FIGURE OUT THE SYNTAX TO MAKE IT WORK
    
        struct commandInfo
        {
            unsigned int code;
            animalFunc handler;
            char const* name;
        };
    
        Animal(char const* name) : _name(name) {
            registerCommands();
        }
    
        //  Register all commands supported by this class
        virtual void registerCommands() {
    #if TRACING
            Serial.print(F("Registering "));
            Serial.print(getName());
            Serial.println(F(" Actions:"));
    #endif
            unsigned int index = 0;
            registerCommand(index++, CMD_EAT, "EAT", &Animal::eat);
            registerCommand(index++, CMD_SLEEP, "SLEEP", &Animal::sleep);
            registerCommand(index++, CMD_MAKENOISE, "MAKE NOISE", &Animal::makeNoise);
    
            assert(index == 3);
        }
    
        //  register using an index rather than code so that we don't overload the meaning of code.
        void registerCommand(unsigned int index, unsigned int code, char const* action, animalFunc fn) {
            assert(code < COMMAND_COUNT);
            _commands[index].code = code;
            _commands[index].name = action;
            _commands[index].handler = fn;
    #if TRACING
            Serial.print( "Registering: Index=");
            Serial.print(index);
            Serial.print(F(", Code="));
            Serial.print(_commands[index].code);
            Serial.print(F(", Action="));
            Serial.println(_commands[index].name);
    #endif
        }
    
        void report(unsigned int code, char const* msg) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            Serial.print(msg);
            Serial.print(info.code);
            Serial.print(" [");
            Serial.print(info.name);
            Serial.println("]");
    #endif
        }
    
        void exec(int code, int value) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            report(code, "Executing: ");
    #endif
            (this->*info.handler)(value);
        }
    
        char const* getName() {
            return _name;
        }
    
        //  base class methods
        virtual void eat(int times) {
            while (times-- > 0) {
                reportDoFunc(CMD_EAT);
            }
        }
    
        virtual void sleep(int times) {
            while (times-- > 0) {
                reportDoFunc(CMD_SLEEP);
            }
        }
    
        virtual void makeNoise(int times) {
            while (times-- > 0) {
                reportDoFunc(CMD_MAKENOISE);
            }
        }
    
        virtual void showActions() {
    #if TRACING
            Serial.print(getName());
            Serial.print(F(" Implements "));
            Serial.print(COMMAND_COUNT);
            Serial.println(F(" actions:"));
            for (int i = 0; i < COMMAND_COUNT; i++) {
                showAction(i);
            }
    #endif
        }
    
        void showAction(unsigned int index) {
    #if TRACING
            Serial.print(F("Index: ["));
            Serial.print(index);
            Serial.print(F("] code: ["));
            Serial.print(_commands[index].code);
            Serial.print(F("] "));
            Serial.println(_commands[index].name);
    #endif
        }
    
    private:
        char const* _name;
    
        //  Define an array of pointers to action functions
        commandInfo _commands[COMMAND_COUNT];
    
        //  return the command associated with code
        commandInfo getCommandInfo(unsigned int code) {
    #if TRACING
            Serial.print(F("IN getCommandInfo: for code="));
            Serial.print(code);
            Serial.println(F(":"));
    #endif
            for (int i = 0; i < COMMAND_COUNT; i++) {
    #if TRACING
                Serial.print(F("Checking Index: ["));
                Serial.print(i);
                Serial.print(F("], Code: ["));
                Serial.print(_commands[i].code);
    #endif
                if (code == _commands[i].code) {
    #if TRACING
                    Serial.print(F("] FOUND "));
                    Serial.println(_commands[i].name);
    #endif
                    return _commands[i];
                }
    #if TRACING
                Serial.println("]");
    #endif
            }
    
            //  Invalid command code for this object
            assert(false);
        }
    
        void reportDoFunc(unsigned int code) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            Serial.print(code);
            Serial.print(F(": "));
            Serial.println(info.name);
    #endif
        }
    
    };
    
    //class Mammal : public Animal {    <-- THIS FORM FAILS: "No matching function call to Animal::Animal()
    class Mammal {
        public:
        enum {
            CMD_BIRTHS_BABY = 1,
            COMMAND_COUNT
        };
    
        typedef void(Mammal::*mammalFunc)(int);     // <-- Only difference from animalFunc is class name
    
        struct commandInfo {
            unsigned int code;
            char const* name;
            mammalFunc handler;                 // <-- Only difference from parent class is mammalFunc
        };
    
        //  Constructor
        Mammal(char const* name) : _name(name) {    // <-- Identical to parent (Animal) class 
            registerCommands();
        }
    
        //  Register all commands supported by this class
        virtual void registerCommands() {
    #if TRACING
            Serial.print(F("Registering "));
            Serial.print(getName());
            Serial.println(F(" Actions:"));
    #endif
    
            //  CAN THIS LIST BE ABSTRACTED INTO A TABLE SO REGISTER COMMAND CAN INHERIT?
            unsigned int index = 0;
            registerCommand(index++, CMD_BIRTHS_BABY, "BIRTHS BABY", &Mammal::birthsBaby);
        }
    
        //  Only difference from Animal::registerCommand is last argument (mannalFunc instead of animalFunc)
        void registerCommand(unsigned int index, unsigned int code, char const* action, mammalFunc fn) {
    
            //  Assure that we don't have more commands than we thought...
            assert(index < COMMAND_COUNT);
    
            //  Update values of current placeholder struct's values
            _commands[index].code = code;
            _commands[index].name = action;
            _commands[index].handler = fn;
    
    #if TRACING
            report(code, "Registering: ");
    #endif
        }
    
        //  This is identical to Animal::exec
        void exec(int code, int value) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            report(code, "Executing: ");
    #endif
            (this->*info.handler)(value);
        }
    
        //  This is identical to Animal::report
        void report(unsigned int code, char const* msg) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            Serial.print(msg);
            Serial.print(info.code);
            Serial.print(" [");
            Serial.print(info.name);
            Serial.println("]");
    #endif
        }
    
        //  This is identical to Animal::getName
        char const* getName() {
            return _name;
        }
    
        //  This is identical to Animal::showActions
        virtual void showActions() {
    #if TRACING
            Serial.print(getName());
            Serial.print(F(" Implements "));
            Serial.print(COMMAND_COUNT);
            Serial.println(F(" actions:"));
            for (int i = 0; i < COMMAND_COUNT; i++) {
                showAction(i);
            }
    #endif
        }
    
        //  This is identical to Animal::showAction
        void showAction(unsigned int index) {
            assert(index < COMMAND_COUNT);
    
    #if TRACING
            Serial.print(F("["));
            Serial.print(_commands[index].code);
            Serial.print(F("] "));
            Serial.println(_commands[index].name);
    #endif
        }
    
        //  Concrete Action
        virtual void birthsBaby(int times) {
            while (times-- > 0) {
                reportDoFunc(CMD_BIRTHS_BABY);
            }
        }
    
    private:
    
        //  Inherit?
        char const* _name;
    
        //  commandInfo is Mammal-specific. Can it be generic?
        //  Define an array of pointers to action functions
        commandInfo _commands[COMMAND_COUNT];
    
        commandInfo getCommandInfo(unsigned int code) {
    #if TRACING
            Serial.print(F("IN getCommandInfo: for code="));
            Serial.print(code);
            Serial.println(F(":"));
    #endif
            for (int i = 0; i < COMMAND_COUNT; i++) {
    #if TRACING
                Serial.print(F("Checking Index: ["));
                Serial.print(i);
                Serial.print(F("], Code: ["));
                Serial.print(_commands[i].code);
    #endif
                if (code == _commands[i].code) {
    #if TRACING
                    Serial.print(F("] FOUND "));
                    Serial.println(_commands[i].name);
    #endif
                    return _commands[i];
                }
    #if TRACING
                Serial.println(F("]"));
    #endif
            }
    
            //  Invalid command code for this object
            assert(false);
        }
    
        //  This is identical to Animal::reportDoFunc
        void reportDoFunc(unsigned int code) {
            commandInfo info = getCommandInfo(code);
    #if TRACING
            Serial.print(code);
            Serial.print(F(": "));
            Serial.println(info.name);
    #endif
        }
    
    };
    
    int main() {
        Animal *pAnimal = new Animal("ANIMAL");
        pAnimal->exec(Animal::CMD_EAT, 1);
        pAnimal->exec(Animal::CMD_SLEEP, 1);
        pAnimal->exec(Animal::CMD_MAKENOISE, 2);
        pAnimal->showActions();
    
        Mammal* pMammal = new Mammal("MAMMAL");
        pMammal->exec(Mammal::CMD_BIRTHS_BABY, 2);
        pMammal->showActions();
        pMammal->exec(Animal::CMD_EAT, 2);
    
        delete pMammal;
        delete pAnimal;
    }
    

0 个答案:

没有答案