如何在相同条件下合并两个函数?

时间:2018-08-31 10:00:37

标签: c++ design-patterns

我很快为这个问题写了下面的课。

我正在寻找一种将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 :(得分:6)

进行其他功能,例如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;
}

答案 1 :(得分:3)

最简单的解决方案可能是对这些向量使用std::map

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

地图的键值为"apples""oranges""bananas"等。

因此,您可以轻松地通过地图访问相应的矢量。

答案 2 :(得分:2)

可以使用选择要使用的向量然后执行操作的代码:

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

答案 3 :(得分:2)

下面的解决方案呢?这也将允许您通过在构造函数中添加/删除行来轻松添加/删除已知水果。

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

答案 4 :(得分:1)

无需更改界面,您可以这样做:

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

答案 5 :(得分:1)

我会通过传递要应用的功能来简化功能。

#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

答案 6 :(得分:0)

使用C ++ 17,您可以使用可选参数:

mb_*

和:

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,而不必更改传递的参数。

答案 7 :(得分:0)

我会这样做:

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

如果您想获得更好的性能,可以使用pointer to members

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

答案 8 :(得分:-1)

要坚持使用多个不同向量的模式,我建议使用一个内部枚举来确定方法,而向量的选择可以在一个地方完成:

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