Introducing Composure for Haxe (with Dependency Injection)

Over the years I have realised that inheritance is massively overused by developers, and that by replacing it with a solid composition design, a lot of code becomes a lot more reusable.

Some languages and platforms have native support for composition (e.g. Unity3D), but for the languages I use there was nothing, so about two years ago I built a lightweight composition framework for AS3 called Composure, I’ve recently completely rebuilt it for Haxe, utilising Haxe’s awesome typing and macro systems to make this small library really powerful.

Composure can be installed via haxelib using the commandline:
haxelib install composure

From Inheritance to Composition

The limitations of inheritance are pretty well documented, but for those not familiar, consider the example of the “TextLabelButton” at the bottom.

Those familiar with Unity3D will be familiar with GameObjects and Components, the two core building blocks used to build things. In Composure these two core types are known as ComposeGroups and Traits.

Each conceptual item in your game/app will be a ComposeGroup, with a bunch of Traits attached to it. Any time while your app is running these traits can be added/remove, changing the behaviour of on object completely. Say, for example, you have a player’s character on-screen and wish to switch from the user controlling the character to computer control, you would simply remove your MouseKeyboardControllerTrait and replace it with a ComputerControllerTrait.

These ComposeGroups must be added to the ComposeRoot object to be active, and can also be parented to other ComposeGroup objects (forming a heirarchy).

A Simple Example

Here is a simple example of several Traits, added to a ComposeGroup interacting with each other. Notice that LogTrait extends AbstractTrait, this isn’t necessary for Traits but adds some additional functionality to your Traits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
static function main(){
    var root:ComposeRoot = new ComposeRoot();
   
    var item:ComposeGroup = new ComposeGroup();
    item.addTrait(new MessageTrait("Hello World"));
    item.addTrait(new LogTrait());

    root.addItem(item);
}
class MessageTrait{

    public var msg:String;

    function new(msg:String){
        this.msg = msg;
    }
}
class LogTrait extends AbstractTrait{

    @inject
    public var msgProvider(default, set_msgProvider):MessageTrait;
    private function set_msgProvider(value:MessageTrait):MessageTrait{
        this.msgProvider = value;
        trace(value.msg);
    }

    function new(msg:String){}
}

In this example, a single ComposeGroup is added to the root. It has two simple Traits attached to it, and using the @inject metadata, the LogTrait gets a reference to the MessageTrait.

Check out the Composure Wiki at github for more examples.
Or checkout the version history at haxelib.

The Limitations of inheritance and the TextLabelButton example

GUI libraries typically have some version of these classes:
The SimpleButton, a class which changes it’s visual state when the mouse is rolled over and again while the mouse is pressed, it also fires click events (when active) and handles tab indexing for use in forms. Visually it is an image, and if it has any text on it, the text is part of that image.
The TextLabel, a class which displays some text (which can be set via code), behind which is a rectangular panel, this panel might be styled by CSS (or whatever) and must be resized at runtime to match the size of the text plus configurable margins.
The TextLabelButton, a class which combines both of these other classes into a tabbable button with configurable text, which can be resized to fit the text.

In the world of inheritance, it immediately becomes unclear which class (SimpleButton or TextLabel) should be inherited.
There are several solutions used to deal with this (inline methods, code duplication, multiple-inheritance, etc), but none of them deal with the core issue of inheritance (and OOP in general), which is the conceptual one-to-one relationship between an object and a class. Say for example, you now wanted to add a CheckBox to the TextLabelButton and remove the tabbing behaviour, you would create your subclass and you’d probably have to edit the SimpleButton and CheckBox classes to work as intended.

In Composure, each of these classes would simply be a ComposeGroup with multiple traits attached (e.g. TabbableTrait, ButtonHoverTrait, TextLabelTrait, IsActiveTrait). Each UI component can reuse traits from others. Whilst you will have more classes in the short term, as you build more atomic behaviours into trait (which can easily reference each other) you will over time start cutting down on code while keeping your code untangled and legible. If you wanted to add a CheckBox and remove the Tabbing behaviour, you would only need to modify the list of Traits you include in your new UI Component (i.e. include some from the CheckBox, omit the TabbableTrait).

4 Comments

  1. Have you been into DCI pattern (data, context and interaction – http://en.wikipedia.org/wiki/Data,_context_and_interaction)? Could Composure be used to accomplish DCI in Haxe? (https://plus.google.com/u/0/110749797245734628255/posts/VC49Z8EMobW)

    Regards / Jonas

    • tom

      May 1, 2013 at 5:03 pm

      It seems to have roughly similar goals, although I couldn’t comment on it’s similarity to other DCI frameworks.

      The AS3 version of composure evolved out of an AS3 AOP library I built about 5 years ago, so there are some commonalities.

  2. Looks rad! I wonder is it fast enough for managing action game with a lot of entities interacting among themselves and reacting to player actions.

    • I’m confident that the fundamental structure is efficient, although there are a few lists and things I’m still yet to fully optimise.
      I’ll do some tests soon.

Leave a Reply

Your email address will not be published.

*

© 2017 Thomas Byrne

Theme by Anders NorenUp ↑