<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 mixins. 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.