Refactor code that violates “Needless Repition” principle

随声附和 提交于 2019-12-25 06:27:28

问题


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 code is extended from code presented in this question which was answered yesterday by Dan Masek.

The problems with this code are:

  1. Mammal can't inherit from Animal (compile error)
  2. Mammal cannot Eat, Sleep or Make Noise
  3. Code suffers from "Needless Repetition", thus meeting with the scorn and derision of Uncle Bob Martin.

I want this code to be as excellent as possible, but I am stuck.

I think that the similarities between the two classes center around the typedef that defines a function which is a pointer to a member in the class. I tried templating this, but ran into numerous compile errors, thus demonstrating my utter failure to comprehend how to solve this problem. Hence this post.

For those who saw my previous post, I have added an #ifdef to disable printing (This is Arduino code, so STD is not available), and I added asserts wherever I could. I also added the Mammal class.

//  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;
}

来源:https://stackoverflow.com/questions/37500149/refactor-code-that-violates-needless-repition-principle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!