setter /getter和分层数据结构

Setters/getters and hierarchical data structures

本文关键字:分层 数据结构 getter setter      更新时间:2023-10-16

我知道在类中使用公共字段被认为是一个坏主意。但是,当您的类包含大量分层数据结构和字段时,最好的方法是什么?例如:

class A {B d1; C d2; D d3;}
class B {E d4; F d5;}
class E {G d6; int d7;}

在C语言中很容易访问这样的数据结构,例如ptr_to_A->d1.d4。D7等等……但使用setter/getter的方法是什么呢?

在c++中使用setter和getter时,像A.get_d1().get_d4().get_d7()这样的表达式似乎不太方便,它们强制返回引用。由于一些结构非常大,按值返回似乎是一个糟糕的主意。

在这些情况下您使用哪种方法或编码风格?也许去掉setter/getter,让这些字段公开?

我知道在你的类中有公共字段被认为是一个坏主意。

这是来自过去十年Java开发的一个全面的声明。您应该在逐个成员的基础上考虑一个成员应该是公共的还是私有的。有时,公共数据成员是正确的想法。考虑以下问题:

    我需要在这个成员上维护一个不变量吗?
  1. 这个成员可以有一个无效的值吗?
  2. 我是否希望接口给出该成员的替代表示?

如果以上任何一个问题的答案是肯定的,那么你可能需要使用getter。

还要考虑单独设置成员是否有意义。也许您应该使用构造函数设置成员,并且希望提供一些其他接口来修改这些成员。

在c++中使用setter和getter时,像a.t get_d1().get_d4().get_d7()这样的表达式似乎不太方便

虽然有相当深的数据结构嵌套并不罕见,但通常一段特定的代码不应该研究得太深。如果是这样,我想它可能做得比它应该做的更多,超出了它的单一责任。然而,如果从A对象中获取d7是一个常见的任务,也许A应该在其接口中公开它:

int A::get_d7 {
  return get_d1().get_d4().get_d7();
}

由于某些结构非常大,按值返回似乎是一个糟糕的主意。

实际上,在现代c++中,这根本不是问题。按值传递应该被视为对象传递的默认模式。这是因为现在可以移动临时对象,这实际上是一种非常有效的复制形式。

在我的编码风格中,类不应该公开公共的"原始"数据成员,而应该只公开getter和setter(即使它们是简单的一行方法)。

这是因为代码可以在将来升级,并且单行方法可以扩展到更复杂的东西(或者可以添加一些仅用于调试的构建特性来检查一些不变量等),因此最好为客户端保持接口一致(如果您公开"原始"数据成员,这是不可能的)。

您可以避免使用get_()前缀,只将数据成员视为具有简单(没有get_...)名称的"属性",例如

class Shape
{
public:
  ....
  COLORREF Color() const // Just Color() i.e. the property name, without get_...
  {
     return m_color;
  }
private:
  COLORREF m_color;
};

客户端代码如下:

Shape s;
COLORREF someColor = s.Color();

对我来说很好。

对于setter,可以使用如下语法:

Shape& Color(COLORREF color)
{
    m_color = color;
    return *this;
}

并编写如下客户端代码:

Shape s;
s.Color(...).Draw(); // set color and draw shape

如果属性的类型比COLORREF更复杂(它是32位的DWORD),您可以使用这样的模式:

std::wstring Name() const // getter
{
    return m_name;
}
Shape& Name(std::wstring name) // setter
{
    // Pass by value and move from the value (C++11 move semantics)
    m_name = std::move(name);
    return *this;
}

如果您只是将类用作纯粹的数据结构,并且没有与您想要封装的数据相关的行为,则使用struct代替并直接访问字段。Bjarne Stroustrup推荐这种方法。这相当于使用class并将所有成员声明为public,但将其称为struct可以更清楚地表明它只不过是一个简单的数据集合。

如果你要做的不仅仅是存储数据,那么就使用getter和setter。

在c++中使用setter和getter时,像A.get_d1().get_d4().get_d7()似乎不方便,而且它们强制返回引用。因为有些结构非常大,返回按价值计算似乎是个糟糕的主意。

不,您可以选择是按引用返回还是按值返回。