Is it bad design to pass the containing object as an argument to a method of a contained object?

StackOverflow https://stackoverflow.com/questions/8715893

  •  13-04-2021
  •  | 
  •  

Question

Is it bad design to pass the containing object as an argument to a method of a contained object, like in the simplified example below?

class A
{
    private B _containedObject;

    public A(B b)
    {
            _containedObject = b;
            //...
    }

    public void SomeMethod()
    {
            //...
            _containedObject.SomeMethod(this);
            //...
    }
}

class B
{
    public void SomeMethod(A a)
    {
            //do something with a
    }
}

P.s. The above example is simplified to illustrate merely the containment relation and passing the containing object to the contained object, and does not in itself illustrate the purpose of doing so. Rest assured though that there is a purpose.

Was it helpful?

Solution

There are times when you might want to pass the containing object as a parameter to a contained object. As other posters have noted, it is called "double dispatch". The Smalltalk language provides a classic example.

The example is about adding together integers and floats (or other types of numbers). In C++ or Java, your integer class would have polymorphic methods like:

Integer {
     add(Float) {...}
     add(Integer) {...}
     add(UserDefinedType) {...}
}

Each add method could implement the different kinds of addition correct. The compiler uses the type information to select the right method. The statement Integer(6).add(Float(1.0)) would invoke the Integer::add(Float) method.

Smalltalk, unlike C++ and Java, is dynamically typed. A Smalltalk class can only have one method named add.

Integer::add(Object)

The compiler does not know the class of "object". For that reason, Smalltalk cannot implement the add the same way as C++ or Java. Instead, Smalltalk uses double dispatch. It works like this:

   Integer {
          add(Object o) {
              return o.addToInteger(this)
          }        
    }

The other number classes like Float, Fraction and even Integer implement the method addToInteger().

Float {
      addToInteger(Object o) {
          //Perform float + in math
          return result
     }
}

Double dispatch solves the problem of how to implement man operations polymorphically in a dynamically typed language. But there is a more common use: the visitor pattern. I'll put that in a separate answer.

OTHER TIPS

in this simple example it looks like a form of double dispatch. in which case I would pass A as a method argument of B.

void SomeMethod(A a)
{
    a.DoSomething(this);
}

however, this is just one example. there are other times where passing in the object as a ctor argument may be more beneficial.

No, this is not bad design and is used quite often. You do, however, have to be careful that you don't cause a memory leak if you do this. When you pass the container object to the inner object, it will hold a reference to the parent until the inner is deleted.

That means if you new up an A, and it news up a b, if you let all other references expire A will still be around because B is holding a copy.

If the parent of an object exists and is known at the time the object is constructed, and the object will never have any other parent, it's often useful to have each child object keep a reference to the parent when it is constructed. One will then be able to ask a child object to do something involving its parent without having to pass the parent as an argument, since the child will already know who its parent is.

One wrinkle with this concept is that, depending upon what the classes do, one may want to allow objects of the parent type to limit who can create objects with them as parent. If the parent and child types will be in the same assembly, this can easily be done with an internal modifier on the child's constructor. If one wishes to allow the possibility that the parent object might be of a class defined in a yet-unwritten assembly, but provide such parent objects the compile-time assurance that they will not be "attached" without their "consent", things are a bit trickier, but could probably be accomplished reasonably well by using a private constructor and a public factory method:

public interface IAssuredParent<T>
static childClass createAttachedChild<T,U>(T parent) where T:IAssuredParent<U>
{...};

If the parent defines P private class XYZ and implementsIAssuredParent<XYZ>, it will be the only class with access to type XYZ, and thus the only class which can successfully call createAttachedChild with a pair of type parameters that can accommodate an actual parameter of type P and comply with the type constraints.

Maybe the most common reason for using this technique is the visitor pattern. I'm having a terrible time finding an example I like on the Web, so I'll just use class diagram from the Wikipedia article. Have a look at this diagram: http://upload.wikimedia.org/wikipedia/commons/5/59/VisitorPatternUML.png

Now image a car part accepting a visitor object:

Engine myEngine;
CarElementPrintVistor myVisitor;
myEngine.accept(myVisitor);

But inside the accept() method, the car part turns right around and passes itself to the visitor!

myVisitor.visit(this)

Check out the Visitor design pattern for more information.

In my point of view this is not a so good design in order to improve it you could declare an interface of your object A and declare your SomeMethod after like:

void SomeMethod(IMyInterface a)

then your A class will looks like:

public class A : IMyInterface
{
  ....
}

So this method will give an abstraction in implementation.

In general better design == less relations.

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