【问题标题】:Is it considered a good practice to define virtual get and set functions in C++?在 C++ 中定义虚拟 get 和 set 函数是否被认为是一种好习惯?
【发布时间】:2015-04-19 16:59:04
【问题描述】:

如果我有一个简单的 2 级类层次结构,例如,这个:

// level 1
class Spare_Part{
private:
    string name;
    double price;
public:
    Spare_Part();
    string getName() { return name; }
    double getPrice() { return price; }
    virtual int getQuantity() { return -1; }; // may also define it as pure virtual
};
//level 2
class On_hand : public Spare_Part{
private:
    int quantity;
    string location;
public:
    On_hand();
    int getQuantity(){ return quantity; }
};

我想使用指向基类“Spare_part”的指针访问“On_hand”类中的成员“quantity”,因此我将“getQuantity”函数设为虚拟函数。代码工作正常,但是,我觉得我不应该有一个 get/set 函数(即使是一个虚拟函数)来访问在层次结构中某处定义的成员。这真的被认为是一种不好的做法,应该通过重新设计类来避免吗? 编辑:整个层次结构有点复杂。除了“On_hand”类别之外,还有通过合同供应商提供的零件类别。假设是我无法知道供应商有多少零件可用,这就是为什么基类中不包含“数量”。

【问题讨论】:

  • 上面的代码是不好的做法。它包含标志值 (-1),并且不必要地使用了多态性(为什么手头上没有的备件只不过是没有位置和数量的备件?拥有一个可选的(子集)属性不足以证明多态性,特别是因为(在上述情况下)很明显该属性并不是真正可选的(因为它在根类型中公开!)甚至您的访问器:spare_part 不仅仅是平面数据吗?
  • 补充一下 Yakk 的说法:听起来你正在用派生类的信息污染你的基类。也许您正在尝试获取 Spare_Part* 的列表,但能够讲述有关 On_hand 的信息,并且您希望在不使用联合或进行动态转换的情况下做到这一点。也许您应该考虑在层次结构中间实现您需要的另一个类。
  • @Yakk 整个层次结构有点复杂。除了“On_hand”类别之外,还有通过合同供应商提供的零件类别。假设是我无法知道供应商有多少零件可用,这就是为什么基类中不包含“数量”。

标签: c++ function polymorphism virtual


【解决方案1】:

在这种情况下,是的,这是不好的做法;如果在Spare_Part 上调用getQuantity 没有意义,则它不应该编译。它绝对不应该只返回一个标志值。

可能需要重新设计您的课程。多态性应该用于对 is-a 关系建模,但您的用途主要是在类上标记更多数据。您应该改用组合,也许创建一个包含Spare_Part、数量和位置的Part_Store 类。

【讨论】:

    【解决方案2】:

    让我们试着整理一下。

    基类调用其派生类来读取属性并没有错。在某些情况下,已知基类的每个实现都必须具有属性,并且基类提供了一些需要该属性的功能,并且在所有合理的实现中都是相同的,除了该属性。 在这种情况下,这样做绝对没有错。

    作为替代方案,如果您确实确定在任何合理的实现中始终使用简单的固定类型变量来实现属性,则可以将受保护的变量添加到基类,然后让实现编写给它。

    这完全取决于您需要多大的灵活性。

    在你的情况下,我认为你的概念继承模型有缺陷,这会让你的情况失去任何意义。

    您是说On_hand 派生自Spare_Part,这意味着,根据定义,每个On_hand 都是Spare_Part。这对我来说听起来很奇怪,因为“在手”听起来更像是备件的质量,而不是它的特殊情况。也许在Spare_Part 中添加一个optional<On_Hand> 会更好。

    或者,更好的是,我怀疑你想要类似的东西

    struct On_Hand_Info { int quantity; string location; };
    std::map<Spare_Part, On_Hand_Info> on_hand;
    

    编辑

    看到你最近的更新,也许你应该这样做:

    struct IRetrievalInformation {
      virtual int getQuantity() const=0;
    };
    
    class SparePart{
      string name;
      double price;
      std::unique_ptr<IRetrievalInformation> retinfo;
    public:
      Spare_Part();
      string const& getName() const { return name; }
      double getPrice() const { return price; }
      IRetrievalInformation const& getRetrievalInformation() const {
        assert(retinfo);
        return *retinfo;
      }
      IRetrievalInformation& getRetrievalInformation() {
        return const_cast<IRetrievalInformation&>(
          const_cast<SparePart const*>(this)->getRetrievalInformation()
        );
      }
    };
    
    class OnHandRetrieval : public IRetrievalInformation {
      int quantity;
      string location;
    public:
      On_hand();
      int getQuantity() const final override { return quantity; }
    };
    

    PS:看在上帝的份上,不要同时使用驼峰和下划线。

    【讨论】:

      【解决方案3】:

      IMO、getter 和 setter 在几乎所有情况下都必须被视为一种不好的做法,甚至是一种反模式。使用 OOP 的全部意义在于隐藏实现细节(嗯,不仅如此,而且还有其他)。 Getter/setter 旨在公开实现细节,因此它破坏了 OOP 原则之一。最后,如果您使用的是 getter/setter,为什么不使用旧的纯 C 结构呢?在这种情况下,类与结构相比没有任何优势。

      嗯,有 种情况,getter/setter 看起来不错。关键是,有太多的程序员将它们用作结构的替代品。原因通常是——他们不了解如何以 OOP 方式设计应用程序。

      在您的情况下,您不需要 getter/setter。想想你将如何进一步使用这个类。它会有任何额外的功能还是只是存储一些数据?如果是这样,也许只使用 POD 结构?

      进一步阅读:

      [1]http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html

      [2]http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html

      [3]http://typicalprogrammer.com/doing-it-wrong-getters-and-setters/

      [4]Why use getters and setters?

      [5]http://berryllium.nl/2011/02/getters-and-setters-evil-or-necessary-evil/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-04
        • 2011-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多