Re: friendship case - good or bad
Tristan Wibberley <maihem-nn1@maihem.org> wrote in
news:1187824032.17389.19.camel@localhost.localdomain:
On Tue, 2007-08-21 at 09:49 +0000, werasm wrote:
I've read the following somewhere:
"Friendship is the strongest form of coupling there is. That is, you
introduce a high degree of dependency when you declare a friend,
since the friend becomes aware of the private details of a class.
This means that it is more difficult to change those private details,
since there are other things around that depend on them. It doesn't
necessarily indicate poor design, but a design that doesn't use
friends may be preferable to one that does."
I would say member functions were slightly more strongly coupled.
Whilst fully agreeing with it, I suppose friendship could be a good
tool (enhance encapsulation) if an interface dictates who may use it.
Often an interface (or ABC) is realized by its client. It makes
natural sense that only its client should use it (and is often the
intent). This does not provide any additional coupling as the client
is already coupled to the ABC.
friends of classes that are supposed to be OO-ish (ie inherit from an
abstract base class) are probably a bad idea, yeah, but OO isn't the
only way to do things and is often the wrong choice. But for other
styles of programming (and for the abstract base class) they merely
define a part of the interface to the class where you can use
instances of the class in ways other than "name.member()". You could
support foo(name) without requiring an equivalent public "foo_impl"
member (it can be private or protected instead) as an example. If the
thing you are making into a friend is a part of the interface to the
class then it is *supposed* to be tightly coupled.
Sadly, what we really need is a module system so that we can develop
classes and interfaces that require systems of objects to implement and
yet only expose the interface we want to our end user. Unfortunately,
today we only have friendship to allow classes which need to know more
of our internals than our target users, but this allows access to
everything. Interestingly enough, there is a pattern called "Private
Interface" that can be used to help in this situation. The idea is that
you create an interface that you want a specific instance of a class to
use and then inherit it privately. You can (but don't have to) make all
of the implementation methods private. At somepoint, you can pass your
interface to another class and it can then call those methods through
the interface even though no one else can. This method can eliminate
the need for friendship, but is somewhat more inconvenient. It does
break that strong dependency though.
example:
struct IOnlyForHelpers
{
virtual int GetValue() = 0;
virtual void DoSomethingSpecial() = 0;
}
class HelperClass
{
IOnlyForHelpers * m_pMainDude;
public:
HelperClass(IOnlyForHelpers * pMainDude) : m_pMainDude(pMainDude)
{}
void HelpMe() {
if (m_pMainDude->GetValue() > 0)
m_pMainDude->DoSomethingSpecial();
}
};
class MainDude : private IOnlyForHelpers
{
HelperClass * m_pHelper;
public:
MainDude()
{ m_pHelper = new HelperClass(this); }
private:
virtual int GetValue() { return 5; }
virtual void DoSomethingSpecial() { /* something */ }
}
Hope that helps in some way,
joe