繁体   English   中英

嵌套c ++类中的封装困难

[英]encapsulation difficulty in nested c++ classes

我们都熟悉封装和抽象的概念,但是有时这可能会导致我对这些技巧或方法或解决问题的方法感到好奇的障碍。

这里我们有一个嵌套的c ++类:

#include <iostream>
using namespace std;
class Foo {
  public:
    int get_foo_var()
    {
      return foo_var;
    }

    void set_foo_var(int a)
    {
      foo_var = a;
    }
  private:
    int foo_var;
};

class Bar {
  public:
    Foo get_foo()
    {
      return foo;
    }
  private:
    Foo foo;
};

int main()
{
  Bar bar;
  bar.get_foo().set_foo_var(2);
  cout << bar.get_foo().get_foo_var() << endl;
}

如您在此处看到的,get_foo()返回foo_var(它的值)的副本,这意味着它不是对原始对象的引用,更改它不会执行任何操作,因此什么也不会更改。 一种解决方案可能是将get_foo()更改为返回引用而不是值的方式,但这与封装的概念形成了鲜明的对比。

在不违反软件设计原则的情况下有什么解决方案?

更新
一个指出通过bar类中的函数设置foo_var的方法:

class Bar {
  public:
    void set_foo_var(int a) {
      foo.set_foo_var(a);
    }
  private:
    Foo foo;
};

但是我认为这违反了封装和抽象! 整个抽象概念是,如果“ foo”与“ Foo”相关,而“ bar”与“ Bar”相关,则大多数foo操作应在Foo类中进行,而某些操作可在其他类中进行。 那第一次情景呢? (这种情况下,foo操作与Bar无关,因此在bar中操作foo是愚蠢的!)

您是要返回某物的副本还是对某物的引用是高层设计的决定。 根据上下文,可能需要两种方式。

在此特定示例中,您可以在Bar添加相应的方法来修改其背后的Foo

class Bar {
  public:
    void set_foo_var(int a) {
      foo.set_foo_var(a);
    }
  private:
    Foo foo;
};

这是好事还是坏事? 答案是:我们无法告诉您。 通常,很难认真讨论具有“ Foo”和“ Bar”之类名称的优秀类设计。 好的和坏的取决于实际的实际使用情况! :)

让我们从纯粹的概念层面来看一分钟。 这就是您的设计所说的:

  1. 每个Bar实例都有一个概念上的Foo实体(因为我可以从Bar中获取Foo,并且其状态取决于我从哪个Bar中获取它)。
  2. 每个Foo实例都属于它所来自的Bar实例(因为对Foo进行的操作更改了它来自的Bar-下次我从Bar请求Foo时,会反映以前的Foo的更改)。
  3. 一个Foo与它的Bar具有相同的生存期(因为我可以在Bar的生存期内随时要求它,只要Bar存在,我就可以使用它,并且get_foo()的调用者不管理返回的Foo的生存期宾语)。

另一种看待它的方式是,Foo已被设计为Bar内部状态的一部分,即“概念成员变量”,无论它是否实际上是通过这种方式实现的。

给定您的公共接口已经告诉您的内容,对私有成员返回非const引用真的会破坏封装吗? 您是否可以更改实现,以使Foo不是私有成员变量,但仍使用相同的公共接口? 是的,你可以。 唯一会迫使您更改公共接口的实现更改也将迫使您更改上述概念性接口。

实施经验法则可能会被过度应用。 超越机械原理,转而关注概念设计。 假设您对您的设计含义表示满意,在这种情况下,我说返回对私有成员变量的引用不会破坏封装。 至少那是我的看法。

另一种选择是使Foo和Bar紧密耦合。

class Bar {
  public:
  Foo get_foo()
  {
    return foo;
  }
  set_foo(Foo new_foo)
  {
    // Update foo with new_foo's values
    foo = new_foo;
  }
  private:
  Foo foo;
};

在这种情况下,Foo会在请求时反映Bar的内部状态的一部分,但与它来自的Bar无关。 您必须显式调用set_foo()来更新Bar。 没有这个要求,Foo实际上在概念上是成员变量,无论您如何实现它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM