简体   繁体   English

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

[英]encapsulation difficulty in nested c++ classes

We all are familiar with the concept of encapsulation and abstraction but sometimes this may lead to an obstacle I'm curious about the tricks or methods or whatever you call them to solve the problem. 我们都熟悉封装和抽象的概念,但是有时这可能会导致我对这些技巧或方法或解决问题的方法感到好奇的障碍。

here we have a nested c++ class: 这里我们有一个嵌套的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;
}

as you see here, get_foo() returns a copy of foo_var(it's value) which means it is not the reference to the original one and changing it does nothing, thus nothing is changed. 如您在此处看到的,get_foo()返回foo_var(它的值)的副本,这意味着它不是对原始对象的引用,更改它不会执行任何操作,因此什么也不会更改。 one solution might be changing to get_foo() in a way that returns a reference and not a value but this is of course in contrast with the concept of encapsulation. 一种解决方案可能是将get_foo()更改为返回引用而不是值的方式,但这与封装的概念形成了鲜明的对比。

what are the solutions to solve this problem without breaking software designing principles? 在不违反软件设计原则的情况下有什么解决方案?

UPDATE 更新
one pointed out setting foo_var by a function in bar class: 一个指出通过bar类中的函数设置foo_var的方法:

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

but I think this violates encapsulation and abstraction! 但是我认为这违反了封装和抽象! the whole concept of abstraction is if "foo" is related to "Foo" and "bar" is related to "Bar", that most of foo manipulations should be done in Foo class and some manipulations can be applied in other classes. 整个抽象概念是,如果“ foo”与“ Foo”相关,而“ bar”与“ Bar”相关,则大多数foo操作应在Foo类中进行,而某些操作可在其他类中进行。 what about the first situtation? 那第一次情景呢? (the situtation in which foo manipulation has nothing to do with Bar and so manipulating foo in bar is stupid!) (这种情况下,foo操作与Bar无关,因此在bar中操作foo是愚蠢的!)

Whether you want to return a copy of or a reference to something is a high-level design decision. 您是要返回某物的副本还是对某物的引用是高层设计的决定。 Both ways can be required, depending on the context. 根据上下文,可能需要两种方式。

In this particular example, you could add a corresponding method in Bar to modify the Foo behind it: 在此特定示例中,您可以在Bar添加相应的方法来修改其背后的Foo

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

Is this good or bad? 这是好事还是坏事? The answer is: we cannot tell you. 答案是:我们无法告诉您。 Generally, it's hard to seriously talk about good class design with names like "Foo" and "Bar". 通常,很难认真讨论具有“ Foo”和“ Bar”之类名称的优秀类设计。 What's good and bad depends on the actual, real usage scenario! 好的和坏的取决于实际的实际使用情况! :) :)

Let's look at this from a purely conceptual level for a minute. 让我们从纯粹的概念层面来看一分钟。 This what your design says: 这就是您的设计所说的:

  1. There exists one conceptual Foo entity for every Bar instance (because I can get a Foo from a Bar, and its state depends on which Bar I get it from). 每个Bar实例都有一个概念上的Foo实体(因为我可以从Bar中获取Foo,并且其状态取决于我从哪个Bar中获取它)。
  2. Each Foo instance belongs to the Bar instance it came from (because operations on a Foo change the Bar it came from - the next time I ask for a Foo from a Bar, the previous Foo's changes are reflected). 每个Foo实例都属于它所来自的Bar实例(因为对Foo进行的操作更改了它来自的Bar-下次我从Bar请求Foo时,会反映以前的Foo的更改)。
  3. A Foo has the same lifetime as its Bar (because I can ask for it at any time in the Bar's lifetime, I can use it as long as Bar exists, and the caller of get_foo() does not manage the lifetime of the returned Foo object). 一个Foo与它的Bar具有相同的生存期(因为我可以在Bar的生存期内随时要求它,只要Bar存在,我就可以使用它,并且get_foo()的调用者不管理返回的Foo的生存期宾语)。

Another way of looking at it is that Foo is already designed as part of Bar's internal state, a "conceptual member variable", regardless of whether it is actually implemented that way. 另一种看待它的方式是,Foo已被设计为Bar内部状态的一部分,即“概念成员变量”,无论它是否实际上是通过这种方式实现的。

Given what your public interface is already telling you, how does returning a non-const reference to a private member really break encapsulation? 给定您的公共接口已经告诉您的内容,对私有成员返回非const引用真的会破坏封装吗? Could you change the implementation so that Foo isn't a private member variable, yet still use the same public interface? 您是否可以更改实现,以使Foo不是私有成员变量,但仍使用相同的公共接口? Yes, you could. 是的,你可以。 The only implementation changes that would force you to change the public interface ALSO force you to change the conceptual interface described above. 唯一会迫使您更改公共接口的实现更改也将迫使您更改上述概念性接口。

Implementation rules of thumb can be over-applied. 实施经验法则可能会被过度应用。 Move past mechanics and look at conceptual design instead. 超越机械原理,转而关注概念设计。 Assuming you're OK with what your design is implying, in this case I say that returning a reference to a private member variable does NOT break encapsulation. 假设您对您的设计含义表示满意,在这种情况下,我说返回对私有成员变量的引用不会破坏封装。 At least that's my take on it. 至少那是我的看法。

An alternative is to have Foo and Bar less tightly coupled. 另一种选择是使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;
};

In this case, Foo reflects some part of Bar's internal state at the time it was requested, but isn't tied to the Bar it came from. 在这种情况下,Foo会在请求时反映Bar的内部状态的一部分,但与它来自的Bar无关。 You have to explicitly call set_foo() to update Bar. 您必须显式调用set_foo()来更新Bar。 Without that requirement, Foo really is conceptually a member variable regardless of how you implement it. 没有这个要求,Foo实际上在概念上是成员变量,无论您如何实现它。

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

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