【问题标题】:Get the user input for factory class in C++获取 C++ 中工厂类的用户输入
【发布时间】:2017-12-12 08:49:41
【问题描述】:

在工厂设计中,当我使用工厂类创建新对象时,我会要求用户输入他/她的输入,然后在工厂类中从用户那里获取输入,然后使用这些输入创建对象。

在工厂类中获取用户输入是否可行? 我应该如何在工厂类中获取用户输入?

工厂类在下面;

   Type *Factory::create_type(int Type){

switch(Type){

    case 1:{
        return new A(this->getUserTime(),this->getUserValue());
    }
    case 2:{
        float min = this->getUserMin();
        float max = this->getUserMax();

        if(this->validMinMax(min,max))
            return new B(this->getUserSpeed(),this >getUserValue(),min,max);
        else 
            return NULL;
    }
    case 3:{
        float min = this->getUserMin();
        float max = this->getUserMax();

        if(this->validUserMinMax(max,min))
            return new C(this->getUserSpeed(),this->getUserValue(),max,min);
        else 
            return NULL;
    }
    case 4:{
        return new D(this->getUserDistance(),this->getUserSpeed(),this->getUserValue());
    }
}}

工厂类的输入函数之一;

float Factory::getUserValue(){
float m;
std::cout<<"\n enter value:";
std::cin>>m
return m; }

【问题讨论】:

  • 我建议您尝试提出一个不需要使用magic numbers 的实现。枚举是一种方式。继承另一个。模板和专业化是第三个。
  • 首先,带着那个裸指针离开这里。使用std::unique_ptrstd::shared_ptr。至于输入,实际上你在一个开关中只有 4 种不同的方法。我会将它们提取到单独的方法中,如果需要,创建另一个使用枚举来决定调用哪个方法的工厂或方法。我首选的解决方案是使用另一个工厂/类,因为您将“做什么”与“如何制造”分开。
  • @OscardeLeeuw 这些智能指针的唯一问题是:您将特定的使用权强加给用户 - 但他/她实际上可能需要另一个...当然,他/她可以接受out 并将其分配给其他类型,但在这种特定情况下,作为用户,我希望能够将裸指针直接分配给我需要使用的智能指针(我自己)。

标签: c++ object design-patterns user-input factory-pattern


【解决方案1】:

简答:不,不要在工厂类中获取用户输入。

长答案:如果没有更多上下文(例如项目规模会很有趣),很难为单个课程的一部分提供建议。但是请考虑Single Responsibility Principle,这是SOLID 原则之一。

它告诉我们给一个模块/类/函数完全一个责任。因此,如果您有一个类是工厂(一项职责)并从用户那里读取输入(第二项职责),则违反了该原则。

相反,您应该考虑类似的事情

Type *Factory::create_type(int Type, const UserInput &input)
{
    switch (Type)
    {
        case 1:
            return new A(input.getTime(), input.getValue());
        case 2:
            float min = input.getMin();
            float max = input.getMax();

            /* and so on */

    }
}

还有一些

class UserInput
{
    public:
        Time getTime() const;
        float getMin() const;
        float getMax() const;

        /* and so on */
};

或者,工厂可以将UserInput 作为构造函数参数,因此它被这样调用

UserInput input;
// read input

auto Instance = Factory(input).create_type(3);

这样做您遵循另一个重要原则,Separation Of Concerns

但是,我不能再告诉你什么是最适合你的情况,因为我不了解整个情况。你会发现;-)

【讨论】:

  • @Burak:小补充:为什么会有这个原则?为了更好的可重用性!示例:想象有一天您需要工厂从文件中获取输入。你必须重新编写工厂,但不能在旧应用程序中再使用它(如果你需要重新编译它),所以最后,你必须维护两个版本......@Tobi :这就是为什么我宁愿选择另一个更通用的名称而不是UserInput(只需Parameters?——假设现在是一个内部类......)。
  • @Aconcagua 实际上,在我给一个类命名 ParametersUserInput 之前,我会尝试找到一个更好的名称来描述和总结时间、价值、最小值和最大值的归属一起。但在这个例子中,我想不出一个更好的名字。
  • 实际上有同样的问题......“参数”没有更好地描述这些关系,你是对的,但至少它更中性,因为输入不一定需要来自用户(文件、Windows 注册表...)。
【解决方案2】:

正如其他人所建议的,如果需要,您的工厂可以采用输入参数来确定要创建哪个实例。例如,对于创建不同形状的简单形状工厂,您可以使用字符串/枚举来确定要创建的形状。但我强烈建议您的工厂按值向这些实例返回 Unique Ptr,这称为接收器,以便内存现在由请求者管理和拥有。

class Shape
{
public:
    virtual ~Shape() {}
    virtual void draw() = 0;
};

class Circle : public Shape
{
public:
    virtual void draw() override { // Implementation }
};

class Square : public Shape
{
public:
    virtual void draw() override { // Implementation }
};


class ShapeFactory
{
public:
    std::unique_ptr<Shape> createShape(const std::string& shapeType)
    {
        if(shapeType == "Circle")
            return std::make_unique<Circle>();
        else if(shapeType == "Square")
            return std::make_unique<Square>();
        else
            return nullptr;
    }
};

【讨论】:

  • 请不要使用字符串作为选择选项的参数。不仅可以创建 nullptr,而且每次调用该方法时,您都必须记住确切的关键字是什么。偶然写出 'Circel' 或 'circle' 非常简单,因为您或其他程序员忘记了它是否以大写字母开头。使用枚举,即使每次添加形状时都必须在枚举中添加另一个条目,但它的正确性和易用性是值得的。
  • @OscardeLeeuw 也许我们有一个儿童教育游戏,std::string 参数是最好的选择。但是返回nullptr 仍然很危险。我建议改为抛出异常。
猜你喜欢
  • 1970-01-01
  • 2011-01-28
  • 2017-06-18
  • 2022-10-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 2011-12-18
相关资源
最近更新 更多