Skip to main content
Added usage example
Source Link
9000
  • 24.4k
  • 4
  • 53
  • 80

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend a Node implementation:

class Sprite 
  extends NodeBaseImpl 
  implements NodeHolder, ResourceHolder 
{
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used:

List<NodeHolder> nodes = new ArrayList<>;ArrayList<>(5);
nodes.add(new Sprite(...));
nodes.add(new Minimap(...));
nodes.add(new HitpointBar(...));
// can add more NodeHolders
...

// Rotate them all
for (NodeHolder nh : nodes) { nh.asNode().rotate(15); }

Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial, and only one per interface.

You double the number of interfaces, though, because you use nnnHolder interface dispatch to explicitly do what e.g. Go does automatically.

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend a Node implementation:

class Sprite 
  extends NodeBaseImpl 
  implements NodeHolder, ResourceHolder 
{
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used:

List<NodeHolder> nodes = new ArrayList<>;
nodes.add(new Sprite);
// can add more NodeHolders
...

// Rotate them all
for (NodeHolder nh : nodes) { nh.asNode().rotate(15); }

Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial, and only one per interface.

You double the number of interfaces, though, because you use nnnHolder interface dispatch to explicitly do what e.g. Go does automatically.

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend a Node implementation:

class Sprite 
  extends NodeBaseImpl 
  implements NodeHolder, ResourceHolder 
{
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used:

List<NodeHolder> nodes = new ArrayList<>(5);
nodes.add(new Sprite(...));
nodes.add(new Minimap(...));
nodes.add(new HitpointBar(...));
// can add more NodeHolders
...

// Rotate them all
for (NodeHolder nh : nodes) { nh.asNode().rotate(15); }

Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial, and only one per interface.

You double the number of interfaces, though, because you use nnnHolder interface dispatch to explicitly do what e.g. Go does automatically.

added 225 characters in body
Source Link
9000
  • 24.4k
  • 4
  • 53
  • 80

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend a Node implementation:

class Sprite 
  extends NodeNodeBaseImpl 
  implements NodeHolder, ResourceHolder  
{
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used.:

List<NodeHolder> nodes = new ArrayList<>;
nodes.add(new Sprite);
// can add more NodeHolders
...

// Rotate them all
for (NodeHolder nh : nodes) { nh.asNode().rotate(15); }

Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial, and only one per interface.

You double the number of interfaces, though, because you use nnnHolder interface dispatch to explicitly do what e.g. Go does automatically.

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend Node:

class Sprite extends Node implements NodeHolder, ResourceHolder {
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used. Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial.

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend a Node implementation:

class Sprite 
  extends NodeBaseImpl 
  implements NodeHolder, ResourceHolder  
{
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used:

List<NodeHolder> nodes = new ArrayList<>;
nodes.add(new Sprite);
// can add more NodeHolders
...

// Rotate them all
for (NodeHolder nh : nodes) { nh.asNode().rotate(15); }

Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial, and only one per interface.

You double the number of interfaces, though, because you use nnnHolder interface dispatch to explicitly do what e.g. Go does automatically.

Source Link
9000
  • 24.4k
  • 4
  • 53
  • 80

tl;dr: composition is good, implementation inheritance is... an unfortunate choice.

What you try to achieve is to make Sprite look simultaneously like a Resource and like a Node. You don't need this. You need a way to expose the Resource interface and the Node interface.

Imagine:

// traditional part

interface Node {
  Position getPosition();
  // anything else
}

interface Resource {
  void allocate(...);
  void destroy();
}

// composition interfaces

interface NodeHolder {
  Node asNode(); // the only method
}

interface ResourceHolder {
  Resource asResource(); // the only method
}


// reusable resource helper

class ResourceImpl implements Resource {
  public void allocate(...) { /* some implementation */ }
  public void destroy() { /* some implementation */ }
}

// you could have a default Node implementation the same way, 
// but you don't have to; you could directly extend Node:

class Sprite extends Node implements NodeHolder, ResourceHolder {
  private Resource my_resource;
  public Sprite(...) {
    // whatever construction needed
    my_resource = new ResourceImpl();
  }

  public Resource asResource() { return my_resource; } // composition!
  public Node asNode() { return this; } // composition again! :)
  // ...
} 

Now all your resource-managing code operates on ResourceHolders, and all your node-handling code operates on NodeHolders. No code assumes that any object inherits a specific base class; only interfaces are used. Implementations can be inherited or composed, to your taste. Only code to copy all over are methods asNode(), etc, which are usually trivial.