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

时间:2014-03-05 18:12:55

标签: c++ encapsulation abstraction

我们都熟悉封装和抽象的概念,但有时候这可能会导致一个障碍,我对技巧或方法或者你称之为解决问题的任何东西感到好奇。

这里我们有一个嵌套的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是愚蠢的!)

2 个答案:

答案 0 :(得分:4)

是否要返回某个内容的副本或引用是一个高级别的设计决策。根据具体情况,可能需要这两种方式。

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

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

这是好还是坏?答案是:我们不能告诉你。一般来说,用“Foo”和“Bar”这样的名字来认真谈论好的班级设计是很困难的。什么是好的和坏的取决于实际的,真实的使用场景! :)

答案 1 :(得分:1)

让我们从一个纯粹的概念层面看一下这一点。这就是你的设计所说的:

  1. 每个Bar实例都有一个概念Foo实体(因为我可以从Bar获得一个Foo,它的状态取决于我从哪个Bar获得它。)
  2. 每个Foo实例都属于它来自的Bar实例(因为Foo上的操作改变了它来自的Bar - 下次我从Bar请求Foo时,反映了之前的Foo的更改)。
  3. Foo与Bar的生命周期相同(因为我可以在Bar的生命周期中随时询问它,只要Bar存在,我就可以使用它,而get_foo()的调用者不会管理它的生命周期返回的Foo对象)。
  4. 另一种看待它的方式是,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在概念上都是一个成员变量。