<mixin>
is like
<class>
but rather than creating something you
can instantiate, <mixin>
creates a template
that can be mixed in (added to) more than one other class. A
<mixin>
is like an interface, but it is
allowed to have state and implementation.
This page describes an advanced usage of
<mixin>
. For a more basic usage, see Chapter 33. Extending
Classes in the Developers Guide.
<mixin>
s are a shorthand for creating
intermediate anonymous classes (exactly as we do in our implementation):
<class name="a" extends="d" with="b,c" />
is meant to be _exactly_ equivalent to:
<class name="cd" extends="d" implements="c"> ... body of c ... </class> <class name="bcd" extends="cd" implements="b"> ... body of b ... </class> <class name="a" extends="bcd"> ... body of a ... </class>
Except that the intermediate classes cd
and
bcd
are anonymous. So it is a shorthand for re-using the
mixin
bodies in multiple classes with potentially
different superclasses. The primary difference between
trait
and mixin
is that
trait
simply disallows any case where you could not
simply use textual substitution, whereas mixin
appeals to
the class inheritance semantics to resolve those cases (including being able
to make super calls to overridden mixin
methods). In the
cases where there are no conflicting attributes, mixins
can be optimized to traits
and you can eliminate the
intermediate classes.
The simple model of how mixin inheritance works is that if you say:
<class name="sundae"
extends="icecream"
with="sauce,whippedcream,nuts">
methods are looked up first in the class ( sundae
) then
in each of the mixins
( sauce
, then
whippedcream
, then nuts
) and
then in the super class chain ( icecream
and then in any
superclasses of icecream
). The mixin
s are searched left-to-right, and then the superclass chain is searched. The
key point is that mixin
s precede the superclass, and
mixin
s are searched in the order they are listed.
This same search order is used when looking
for methods, or attributes, or invoking super calls.
The following example demonstrates the use of mixin
s. See the comments in the code for a description of how it works.
<canvas
width
="100%
" height
="400
" layout
="axis: y; spacing: 25
">
<!--- The base class of all ice cream desserts -->
<class
name
="icecream
">
<attribute
name
="flavor
" type
="color
" value
="vanilla
"/>
<method
name
="toString
">
return this.presentAttribute('flavor', 'color') + " icecream";
</method
>
<view
width
="100%
" height
="100%
" bgcolor
="${parent.flavor}
"/>
</class
>
<!--- This parlour only offers walnuts -->
<class
name
="walnuts
" extends
="view
">
<attribute
name
="bgcolor
" value
="brown
"/>
<attribute
name
="ground
" type
="boolean
" value
="false
"/>
<attribute
name
="opacity
" value
="${this.ground?.5:1}
"/>
<method
name
="grind
">
this.setAttribute('ground', true);
</method
>
<method
name
="toString
">
return (this.ground?"ground ":"") + "walnuts";
</method
>
</class
>
<!--- This is the 'toppings' mixin, which could be added to various desserts -->
<mixin
name
="toppings
">
<attribute
name
="sauce
" type
="color
" value
="chocolate
"/>
<attribute
name
="whippedcream
" type
="boolean
" value
="true
"/>
<attribute
name
="cherry
" type
="boolean
" value
="true
"/>
<view
name
="layers
" width
="100%
" layout
="axis: y; spacing: 0
" bgcolor
="${canvas.bgcolor}
">
<view
width
="25
" height
="25
" x
="${(parent.width - this.width)/2}
" visible
="${classroot.cherry}
" bgcolor
="red
"/>
<walnuts
name
="nuts
" width
="100%
" height
="10
" visible
="${classroot.nuts}
" ground
="${classroot.ground}
"/>
<view
width
="100%
" height
="25
" visible
="${classroot.whippedcream}
" bgcolor
="snow
"/>
<view
width
="100%
" height
="10
" bgcolor
="${classroot.sauce}
"/>
</view
>
<attribute
name
="nuts
" type
="boolean
" value
="false
"/>
<attribute
name
="ground
" type
="boolean
" value
="false
"/>
<method
name
="toString
">
return super.toString() + " with " +
this.presentAttribute('sauce', 'color') + " sauce" +
(this.whippedcream?", whipped cream":"") +
(this.nuts?(", " + this.layers.nuts.toString()):"") +
(this.cherry?", with a cherry on top":"");
</method
>
</mixin
>
<!---
Ok! Let's build a dessert. A sundae is based on icecream, and
we mixin toppings. The resulting class has the attributes of
icecream and toppings, without us having to make toppings
inherit from icecream.
-->
<class
name
="sundae
" extends
="icecream
" with
="toppings
">
<method
name
="toString
">
return super.toString() + " sundae";
</method
>
</class
>
<class
name
="sundae2
" extends
="icecream
" with
="toppings
">
<method
name
="toString
">
return super.toString() + " sundae";
</method
>
</class
>
<sundae2
visible
="false
"/>
<attribute
name
="sauces
" value
="['chocolate', 'lavender', 'olive', 'orange', 'plum', 'snow', 'thistle', 'violet']
"/>
<attribute
name
="icecreams
" value
="['bisque', 'honeydew', 'lemonchiffon', 'lime', 'mintcream', 'papayawhip', 'peachpuff', 'salmon', 'tomato', 'wheat']
"/>
<!--- Here's how you order your sundae -->
<view
name
="recipe
" x
="5%
" width
="90%
" layout
="axis: y; spacing: 3
">
<text
>
Build your own sundae:
</text
>
<checkbox
name
="cherry
">Cherry?
</checkbox
>
<view
name
="nutchoice
" layout
="axis: x; spacing: 3
">
<checkbox
name
="nuts
">Nuts?
</checkbox
>
<checkbox
name
="ground
" enabled
="${parent.nuts.getValue()}
">Ground?
</checkbox
>
</view
>
<checkbox
name
="whippedcream
">Whipped Cream?
</checkbox
>
<view
name
="saucechoice
" layout
="axis: x; spacing: 3
">
<text
>Sauce:
</text
>
<combobox
name
="sauce
" editable
="false
">
<replicator
nodes
="$once{canvas.sauces}
">
<textlistitem
text
="${this.presentValue()}
" type
="color
"/>
</replicator
>
</combobox
>
</view
>
<view
name
="icecreamchoice
" layout
="axis: x; spacing: 3
">
<text
>Ice Cream:
</text
>
<combobox
name
="icecream
" editable
="false
">
<replicator
nodes
="$once{canvas.icecreams}
">
<textlistitem
text
="${this.presentValue()}
" type
="color
"/>
</replicator
>
</combobox
>
</view
>
</view
>
<!--- And here's your WYSIWYG sundae -->
<sundae
id
="yoursundae
" width
="100
" height
="150
" x
="${(this.parent.width - this.width)/2}
" flavor
="${recipe.icecreamchoice.icecream.getValue()}
" sauce
="${recipe.saucechoice.sauce.getValue()}
" nuts
="${recipe.nutchoice.nuts.getValue()}
" ground
="${recipe.nutchoice.ground.getValue()}
" whippedcream
="${recipe.whippedcream.getValue()}
" cherry
="${recipe.cherry.getValue()}
"/>
<!--- If you want the recipe for your sundae, push the button -->
<button
x
="${(this.parent.width - this.width)/2}
" onclick
="description.format('You built a %s', yoursundae)
">
Tell me the recipe!
</button
>
<!--- And the recipe will show up here -->
<text
name
="description
" x
="5%
" width
="90%
" multiline
="true
"/>
</canvas
>
Copyright © 2002-2010 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.