Question

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:

#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. 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.

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:

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. 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!)

Was it helpful?

Solution 2

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).
  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).
  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).

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.

Given what your public interface is already telling you, how does returning a non-const reference to a private member really break encapsulation? Could you change the implementation so that Foo isn't a private member variable, yet still use the same public interface? 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.

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. You have to explicitly call set_foo() to update Bar. Without that requirement, Foo really is conceptually a member variable regardless of how you implement it.

OTHER TIPS

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:

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". What's good and bad depends on the actual, real usage scenario! :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top