【问题标题】:Data hiding worth for simple data containers简单数据容器的数据隐藏价值
【发布时间】:2021-08-25 11:55:09
【问题描述】:

在我的公司,我们从 XML 生成代码。代码生成器生成包含 Messages 的头文件,每条消息只包含数据。注意我们在设置或返回数据时不做任何验证;此外,我们不必关心状态,即数据 x 和消息中的数据是独立的;如果 x 改变了,我们不需要改变 y 的状态。

当前头文件

class somemessage
{
private:
    Field _field;
    .......
public:
    Field& getfield(){...}
    const Field& getfield() const {...}
    void setfield(const Field& field){....}
} ;

如果只是数据,我们还需要在这里隐藏数据吗?我们是否需要在这些标头中使用 getter 和 setter,或者可以将其简化如下。

struct  somemessage
{
    Field field;
};

我们可以在需要时使用 const 将消息设为只读,如下所示。

void message_consumer(const somemessage& message)
{
    message.field = somevalue; // compilation error
}

这种方法有什么缺点,使用访问器和修改器有什么优点?

【问题讨论】:

  • 封装不是必须的。它有很多优点和一些缺点。是否应该使用它是基于意见的。
  • fwiw,恕我直言,getter 和 setter 封装出错了,您也可以将它们公开,尤其是当您返回引用时。一旦你返回一个非常量引用,所有的封装都会丢失
  • 如果你有传递的 getter 和 setter,那么你就没有隐藏任何数据。
  • Getter/setter 对是封装和数据隐藏的对立面。
  • 在更实用的优势方面,在许多 IDE 中设置函数断点比设置数据断点更容易。

标签: c++ oop functional-programming


【解决方案1】:

如果你有以下模式:

class A {
public:
    void SetFoo(const Foo& newFoo) {
        f = newFoo;
    }
    const Foo& GetFoo() const {
        return f;
    }
protected:
private:
    Foo f;
};

也就是说,你有一个 getter/setter 对,它们所做的只是有一个 return 语句和一个赋值表达式,那么就不需要数据成员是私有的,而只需将数据成员设为 public 并删除getter/setter 对。

class A {
public:
    Foo f;
protected:
private:
};

如果您的 getter/setter 做其他事情,或者以任何方式更复杂,那么是的,拥有一对 getter/setter 就可以了。

就使用structclass 而言,如果类型严格地只有数据,我会选择struct;没有函数,没有构造函数。如果出于某种原因数据类型需要函数,则应将其声明为class

【讨论】:

  • 这在语义上等同于 OP 建议的 struct somemessage
  • @CoryKramer 阐明结构与类表示
  • structclass 之间的唯一区别分别是默认publicprivate 成员可见性。任何诸如“如果它有方法”之类的规则都是基于意见的区别。
  • @CoryKramer 是的;但是,就理解和一致性而言,如果您认为struct 只是数据成员的集合,而class 则更复杂,那么当您看到struct 时,您可以更快地理解代码,您会立即知道“此类型只有数据成员”,您不必担心任何其他实现细节。
  • @Casey 这纯粹是约定俗成的。我选择 structclass 是基于我想首先列出 private 还是 public 成员。 “当您看到 struct 时,您会立即知道“这种类型只有数据成员””通常是错误的。当然,如果你坚持你的约定,那很好,但仍然只是约定
【解决方案2】:

像这样写一个getter:

Field& getfield(){...}

不是封装。用户可以这样做:

Field& decapsulated = x.getField();

现在他们有了对私有成员的引用,他们可以用它来做他们喜欢做的事。 setter 中的所有检查和记账都是徒劳的,因为用户不需要它来修改私有成员:

decapsulated = some_other_field;

适当的封装具有优势。尽管只有公共成员的普通旧结构也有它们的位置。但是,如果您所做的只是编写不封装数据的样板,则可以放弃样板。最终由您决定使用什么。封装有很多优点,但不是必须的。

返回非常量引用的 Getter 可用作便利方法。它们可以为用户提供访问类数据的简便方法。例如与std::vector::operator[]std::vector::at() 进行比较。尽管不应将其与数据封装相混淆。

【讨论】:

  • 谢谢@ber。如果我删除了非常量访问器,在这种情况下仍然值得拥有 getter 和 setter 类吗?如果是的话,如果您能提供一些用例,我将非常感激?
  • @user982042 当他们写“Getter/setter 对是封装和数据隐藏的对立面”时,我支持 Moldbnilo。尽管我试图将其排除在答案之外,因为它是基于边缘意见的。方法应该是关于行为而不是关于数据。
  • @user982042 我记得一个很棒的 5 分钟会议演讲,演讲者使用了一个人去酒吧的类比。 Getter 和 Setter 就像调酒师打开你的钱包,数着里面的钱,减去你必须支付的钱,然后把剩下的钱放回去。当我去酒吧时,更像是我有一个方法pay(amount) 而不是get_money()set_money(amount)
  • @user982042 在我认为的面向对象和具有所有公共成员的数据结构之间存在广泛的差异。没有对错之分,您只需要决定您的数据结构在该范围内的哪个位置
  • @user982042 不是我记得的谈话,而是同一个人,同一主题。 youtube.com/watch?v=GMrjuuczZkQ eg 10:10 “getter 和 setter 是个大问题。每次看到你就知道它不是面向对象的”。我的意思是写 getter 和 setter 是可以的,只是不要为了它自己的缘故或在你的代码中添加一些面向对象,因为它不是
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-24
  • 2013-06-28
  • 1970-01-01
  • 1970-01-01
  • 2019-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多