Как объединить две функции с одинаковыми условиями? - PullRequest
0 голосов
/ 31 августа 2018

Я быстро написал следующий класс для этого вопроса.

Я ищу способ объединения addFruit() с removeFruit(), чтобы уменьшить код.

Они оба используют идентичные условия, но просто разные вызовы функций в конце.

Мой код:

#include <iostream>
#include <string>
#include <vector>

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        if (str == "apples")
            addToVec(apples, count);
        else if (str == "oranges")
            addToVec(oranges, count);
        else if (str == "lemons")
            addToVec(lemons, count);
        else if (str == "melons")
            addToVec(melons, count);
        else if (str == "bananas")
            addToVec(bananas, count);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }
    void removeFruit(const std::string &str)
    {
        if (str == "apples")
            removeFromVec(apples);
        else if (str == "oranges")
            removeFromVec(oranges);
        else if (str == "lemons")
            removeFromVec(lemons);
        else if (str == "melons")
            removeFromVec(melons);
        else if (str == "bananas")
            removeFromVec(bananas);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }
private:
    void addToVec(std::vector<int> &vec, int count)
    {
        vec.push_back(count);
    }
    void removeFromVec(std::vector<int> &vec)
    {
        vec.pop_back();
    }
    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
};

Есть ли какой-нибудь умный способ объединить две функции, чтобы я мог уменьшить код?

Ответы [ 9 ]

0 голосов
/ 31 августа 2018

А как насчет решения ниже? Это также позволит вам легко добавлять / удалять известные фрукты, добавляя / удаляя строки в конструкторе.

#include <iostream>
#include <string>
#include <vector>
#include <map>

class MyClass
{
public:
    MyClass()
    {
        allowedFruits["apples"] = {};
        allowedFruits["oranges"] = {};
        allowedFruits["lemons"] = {};
        allowedFruits["melons"] = {};
        allowedFruits["bananas"] = {};
    }
    void addFruit(const std::string &str, int count)
    {
        auto it = allowedFruits.find(str);
        if(it != MyClass::allowedFruits.end()){
            it->second.push_back(count);
        }
        else {
            std::cout << "Unknown Fruit : " << str << '\n';
        }
    }
    void removeFruit(const std::string &str)
    {
        auto it = allowedFruits.find(str);
        if(it != allowedFruits.end()){
            // my be some check here
            it->second.pop_back();
        }
        else {
            std::cout << "Unknown Fruit : " << str << '\n';
        }
    }
private:
    std::map<std::string,std::vector<int>> allowedFruits;
};
0 голосов
/ 31 августа 2018

Я бы сделал это:

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        doToFruit(str, [&](std::vector<int> &vec){ addToVec(vec, count); });
    }
    void removeFruit(const std::string &str)
    {
        doToFruit(str, [&](std::vector<int> &vec){ removeFromVec(vec); });
    }
private:
    template<typename Callable>
    void doToFruit(const std::string &str, const Callable &func)
    {
        std::pair<const char*, std::vector<int>&> fruits[] = {
            {"apple", apples}, {"oranges", oranges}, {"lemons", lemons},
            {"melons", melons}, {"bananas", bananas}
        };
        for (auto i : fruits)
            if (str == i.first)
                return func(i.second);
        std::cout << "Unknown Fruit : " << str << '\n';
    }
    void addToVec(std::vector<int> &vec, int count)
    {
        vec.push_back(count);
    }
    void removeFromVec(std::vector<int> &vec)
    {
        vec.pop_back();
    }
    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
};

Вы можете использовать указатель на участников , если вы хотите повысить производительность:

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        doToFruit(str, [&](std::vector<int> &vec){ addToVec(vec, count); });
    }
    void removeFruit(const std::string &str)
    {
        doToFruit(str, [&](std::vector<int> &vec){ removeFromVec(vec); });
    }
private:
    template<typename Callable>
    void doToFruit(const std::string &str, const Callable &func)
    {
        auto iFound = fruits.find(str);
        if (iFound != fruits.end())
            func(this->*(iFound->second));
        std::cout << "Unknown Fruit : " << str << '\n';
    }
    void addToVec(std::vector<int> &vec, int count)
    {
        vec.push_back(count);
    }
    void removeFromVec(std::vector<int> &vec)
    {
        vec.pop_back();
    }
    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
    static std::unordered_map<std::string, std::vector<int> MyClass::*> fruits;
};

std::unordered_map<std::string, std::vector<int> MyClass::*> MyClass::fruits = {
    {"apple", &MyClass::apples}, {"oranges", &MyClass::oranges},
    {"lemons", &MyClass::lemons}, {"melons", &MyClass::melons},
    {"bananas", &MyClass::bananas}
};
0 голосов
/ 31 августа 2018

Можно использовать код, который выбирает используемый вектор и затем выполняет действие:

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        auto vec = selectVector(str);
        if(vec != nullptr)
            addToVec(*vec, count);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }
    void removeFruit(const std::string &str)
    {
        auto vec = selectVector(str);
        if(vec != nullptr)
            removeFromVec(*vec);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }
private:

    std::vector<int> *selectVector(const std::string &str)
    {
        if (str == "apples")
            return &apples;
        else if (str == "oranges")
            return &oranges;
        else if (str == "lemons")
            return &lemons;
        else if (str == "melons")
            return &melons;
        else if (str == "bananas")
            return &bananas;
        else
            return nullptr;
    }

    void addToVec(std::vector<int> &vec, int count)
    {
        vec.push_back(count);
    }
    void removeFromVec(std::vector<int> &vec)
    {
        vec.pop_back();
    }
    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
};
0 голосов
/ 31 августа 2018

Я бы пошел немного функционально, передав функцию для применения.

#include <iostream>
#include <string>
#include <vector>
#include <functional>

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        searchAndApplyHelper(str, std::bind(&MyClass::addToVec, *this, std::placeholders::_1, count));
    }
    void removeFruit(const std::string &str)
    {
        searchAndApplyHelper(str, std::bind(&MyClass::removeFromVec, *this, std::placeholders::_1));
    }

private:

    template <class Func>
    void searchAndApplyHelper(const std::string str, Func f)
    {
        if (str == "apples")
            f(apples);
        else if (str == "oranges")
            f(oranges);
        else if (str == "lemons")
            f(lemons);
        else if (str == "melons")
            f(melons);
        else if (str == "bananas")
            f(bananas);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }

    void addToVec(std::vector<int> &vec, int count)
    {
        vec.push_back(count);
    }
    void removeFromVec(std::vector<int> &vec)
    {
        vec.pop_back();
    }
    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
};

Я сделал это с шаблоном, но вы также можете использовать std::function.

0 голосов
/ 31 августа 2018

Самым простым решением может быть использование std::map для этих векторов:

std::map<std::string,std::vector<int>> fruitVecs;

Ключевые значения карты: "apples", "oranges", "bananas" и т. Д.

Таким образом, вы можете легко получить доступ к соответствующему вектору для любой операции через карту.

0 голосов
/ 31 августа 2018

Без изменения интерфейса вы можете сделать это так:

std::vector<int>& pickVector(std::string str) {
    // put all the switch here and return a reference to the correct vector
}

void addFruit(const std::string &str, int count)
{
   addToVec(pickVector(str),count);
}
0 голосов
/ 31 августа 2018

Чтобы придерживаться вашего паттерна из нескольких различных векторов, я бы рекомендовал использовать внутреннее перечисление, определяющее метод, тогда как выбор вектора можно сделать в одном месте:

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        addOrRemoveFruit(str, count, Method::ADD);
    }
    void removeFruit(const std::string &str)
    {
        addOrRemoveFruit(str, 0, Method::REMOVE);
    }

private:
    enum class Method
    {
        ADD,
        REMOVE
    };

    void addOrRemoveFruit(const std::string &str, int count, Method method)
    {
        if (str == "apples")
            addOrRemoveFruitImpl(apples, count, method);
        else if (str == "oranges")
            addOrRemoveFruitImpl(oranges, count, method);
        else if (str == "lemons")
            addOrRemoveFruitImpl(lemons, count, method);
        else if (str == "melons")
            addOrRemoveFruitImpl(melons, count, method);
        else if (str == "bananas")
            addOrRemoveFruitImpl(bananas, count, method);
        else
            std::cout << "Unknown Fruit : " << str << '\n';
    }

    void addOrRemoveFruitImpl(std::vector<int> &vec, int count, Method method)
    {
        if (method == Method::ADD)
             vec.push_back(count);
        else
             vec.pop_back();
    }

    std::vector<int> apples;
    std::vector<int> oranges;
    std::vector<int> lemons;
    std::vector<int> melons;
    std::vector<int> bananas;
};

Однако еще лучше использовать карту:

class MyClass
{
public:
    void addFruit(const std::string &str, int count)
    {
        fruits[str].push_back(count);
    }
    void removeFruit(const std::string &str)
    {
        if (fruits.count(str) > 0)
            fruits[str].pop_back();
    }

private:
    std::unordered_map<std::string, std::vector<int>> fruits;
};
0 голосов
/ 31 августа 2018

Используя C ++ 17, вы можете использовать необязательные аргументы:

void addRemoveFruit(const std::string &str, std::optional<int> count = std::nullopt)
{
    if (str == "apples")
        addRemoveVec(apples, count);
    else if (str == "oranges")
        addRemoveVec(oranges, count);
    else if (str == "lemons")
        addRemoveVec(lemons, count);
    else if (str == "melons")
        addRemoveVec(melons, count);
    else if (str == "bananas")
        addRemoveVec(bananas, count);
    else
        std::cout << "Unknown Fruit : " << str << '\n';
}

и

void addRemoveVec(std::vector<int> &vec, std::optional<int> count = std::nullopt)
{
    if(count.has_value()) {
        vec.push_back(count.value());
    } else {
        vec.pop_back();
    }
}

Таким образом, существующие вызовы addFruit / removeFruit необходимо изменить только на addRemoveFruit без изменения переданных параметров.

0 голосов
/ 31 августа 2018

Создайте дополнительную функцию, например, determineTargetVector(const std::string &str), который возвращает соответствующий вектор, в который вы хотите вставить / удалить элемент, чтобы у вас не было лишних условий. Также хорошо иметь только одну ответственность за каждую функцию.

Например:

std::vector<int> *determineTargetVector(const std::string &str)
{
    if (str == "apples")
        return &apples;
    else if (str == "oranges")
        return &oranges;
    else if (str == "lemons")
        .
        .
        .
    else
        //something invalid, to check for in superior function
        return nullptr;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...