如何处理运行时在对象

时间:2017-01-17 15:48:15

标签: c++ oop

我的项目遇到了一些设计问题,我希望得到一些帮助。我想出了一个例子,我想概述了我遇到的问题。我是软件设计的新手,如果我完全错过了什么,请原谅我。

在这个例子中说我有:

struct Book {
    std::string author_first_name;
    std::string author_last_name;
    int year_published;
    double price_in_dollars;
 };

class BookCase {
    std::vector<Book> all_books;
    // Rest of class implementation
 }

我从一个文件或多个文件中读取所有书籍并将它们存储在BookCase中。然后我想在BookCase上做一堆操作,在运行时定义,这就是我被困住的地方。

假设用户想要按作者对书籍进行排序然后导出,下次按名字排序或按价格排序,或者想要添加新操作可能会删除在X年之前发布的书籍。

我的问题是:

  1. 放置这些操作的最佳位置在哪里。我可以轻松地将这些操作添加到BookCase:

    class BookCase {
    
        void SortByLastNameAscending();
        void SortByLastNameDescending();
        void SortByPriceAscending();
     // etc...};
    

    我知道这是错误的,这会使类膨胀,并且每次添加新操作时都必须更改类。我可以拥有像类甚至名称空间“BookCaseProcessor”的东西,它具有所有操作并在那里添加新的操作。是否有更优雅的方式来处理这样的事情,也许是我缺少的设计原则。

  2. 如何以一种很好的方式将操作串在一起。假设用户想要RemoveBooksBeforeYear(),RemoveBooksThatCostLessThan(),SortByLastNameDescending()。下次他们跑步时,他们只想做一件事,或者10件事。现在,我能想到的唯一解决方案就是一堆if语句。

3 个答案:

答案 0 :(得分:1)

我认为最简单的方法是使用std :: function。

假设你实现了各种各样的操作:

void SortByLastNameAscending(BookCase&) {...}
void SortByLastNameDescending(BookCase&) {...}
void SortByPriceAscending(BookCase&) {...}

您可以填充要执行的简单操作列表:

std::vector<std::function<void(BookCase&)>> pipeline;

//populate the pipeline
if(user_choice == "sort") {
  pipeline.emplace_back(SortByLastNameAscending);
}

按顺序执行:

BookCase data;
for(auto& op : pipeline) {
  op(data);
}

答案 1 :(得分:1)

软件设计问题很少是黑白的,而且往往是主观的。我会尽力提出一些解释。

我认为将支持操作放入BookCase本身并没有错。提供课堂合同没有比班级更清楚的地方!任何Processor样式的添加只会使界面不太清晰。您很快就会发现有必要让这个Processor成为班上的朋友,而且您会发现它比仅仅在BookCase中使用排序方法更糟糕。

我可能不会创建多个SortBy<Field>...方法,但相反,会将其模板化或使用标签分发,但它可能更多的是品味偏好。但是,我肯定不会对不同版本的升序与降序进行劝阻。这没有任何意义,使用“升序/降序”&#39;作为sort函数的标记参数。

答案 2 :(得分:0)

我认为混淆来自于您的班级被命名为BookCase。这不是班级的内容。书柜,更一般地说,图书馆应该有一个明确的顺序。

您实际处理的是BookSelection。 (根据您的要求,您可能仍需要保留BookCase;或者文件可以是书架。)

此外,由于重点是排序和修改,list可能比vector更有意义。