Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composition, Forwarding and Wrapping

On the subject of "prefer composition over inheritance", my teacher says this:

  • composition: the existing class becomes a component of the new one
  • forwarding: each instance method in the new class, invokes the corresponding method on the contained instance of the existing class and returns the results
  • wrapper: the new class encapsulates the existing one

I don't understand very well this three concepts so I try to write down some code:

//Composition is this
Class A{
    public void doSomething(){
        //do some code
    }
}

Class B{
    A a = new A();
    public void doOther(){
        a.doSomething();  //this is forwarding
    }
}

And so what is wrapping?

like image 298
Alessandro Picardi Avatar asked Sep 01 '25 01:09

Alessandro Picardi


1 Answers

Your first example is not composition.

Composition is a "has-a" relationship between 2 objects, where one object (the composed object) is a field member of the other (the owner).

Composition would be:

class DemoA {
    DemoB b; //composition
}

We would say "DemoA is composed of DemoB".

If the DemoB object is still usable when DemoA becomes unreachable, we consider DemoB to be aggregated. For example, keys can still be used although the keyring may have been destroyed. The keyring will be composed of keys, but does not own them, which expresses aggregation.


Your forwarding example looks fine, although the requirements do say "return the results", which may suggest the methods should not be void:

class DemoA {
    private DemoB b;

    public int doSomething() {
        return b.doSomething(); //forwarding/delegating
    }
}

As for wrappers, they encapsulate the behavior of the object, exposing new (and usually more complex) behaviors. An example would be DataInputStream, which wraps an InputStream to allow you to read String objects and more, when InputStream is only capable of primitive data.

class DemoB {
    public int read() {
        //...Read one int of data
    }
}

class DemoA {
    private DemoB b;

    public DemoA(DemoB b) {
        this.b = b;
    }

    public List<Integer> readUntil(List<Integer> dataList, Supplier<Boolean> condition) {
        while(condition.get())
            dataList.add(b.read());

        return dataList;
    }
}

In this example, DemoA wraps DemoB to expose the readUntil behavior, something DemoB was incapable of doing.

This is somewhat a silly example, but hopefully expresses the point: Our DemoB object cannot perform behavior that we need (readUntil), so we wrap it in a type that handles the behavior for us, so we aren't constantly rewriting such behavior.

like image 81
Vince Avatar answered Sep 02 '25 16:09

Vince