Hi Carlos,
I've said this before: I feel bad sometimes that Flex taught folks some
questionable programming practices.
The short answer:
-Don't reference concrete classes
-Choose composition patterns over inheritance
The long answer:
Yishay and Andrew are headed in the right direction. A good goal for Royale is
to try to avoid references to concrete classes (other than what your class
subclasses). Referencing concrete classes essentially breaks PAYG by creating
an unreplaceable chain of dependencies. Flex referenced too many concrete
classes and we all paid for it.
But addition to using interfaces, we should try to remember that Royale is more
about composition than inheritance. Beads are composed into instances to
implement functionality. Hopefully, many beads can be re-used on other
instances with different base class ancestry if the API surfaces are designed
correctly.
And, not only are beads best referenced via their interfaces, you can also ask
"has" instead of "is". IOW, you can ask if an instance "has" a bead instead of
just testing if the instance "is" (implements) an interface.
The first word in PAYG is Pay. PAYG is not Free-AYG. Somebody has to pay.
The more features you use, the more you pay in download size and performance,
especially at startup. The point of PAYG and beads is to enable optimization.
If you want to make cookies and you buy each ingredient separately it will take
up more space and weight because of the packaging as opposed to buying one bag
of ready-to-bake cookie mix. But the point of having separate ingredients is
so you can replace white flour with whole wheat or rye or gluten-free easily.
If it turns out there is significant overhead in some set of beads, you can
choose to inline them, just like any other inlining optimization. That is a
manual process today. Inlining is effectively copying code, so it sounds like
that is what you are doing today. But the copying of code should only be a
last resort after proving that the composition patterns have too much overhead.
The inlining will have its own overhead, usually in download size, as you
mentioned.
I explained the "exploded component" concept a while back. It attempts to
illustrate composition by showing how, in MXML, a component can really be
declared as its beads. For example:
<js:Button>
Could be written more verbosely as:
<js:UIBase>
<js:beads>
<js:TextModel />
<js:ButtonView />
<js:ButtonController />
</js:beads>
</js:UIBase>
Then a ToggleButton could be implemented by simply replacing the controller
(and probably the view).
<js:UIBase>
<js:beads>
<js:TextModel />
<js:ToggleButtonView />
<js:ToggleButtonController />
</js:beads>
</js:UIBase>
So it is only for convenience, so you don't have to type all of these tags,
that we create a top-level component (TLC) called Button that pre-packages some
set of beads and write glue code that proxies some of the bead APIs to the
TLC's API such as:
public class Button extends UIBase
{
public function set label(value:String):void
{
model.label = value;
}
}
The point, relative to this topic, is there is a cost to this convenience,
which is the line or two of glue code.
So, with beads, it is only for convenience that the TLC implements these PAYG
interfaces. If you package up the implementation of IClassSelectorListSupport
into a bead, that bead should be able to be composed into different classes
with different base classes. We actually have a choice of whether to add the
glue code that proxies that implementation to the API surface. If you do that
proxying, then other code can look like:
if (myInstance is IClassSelectorListSupport)
(myInstance as IClassSelectorListSupport).someAPi = "foo";
But the other option is the "has" pattern, which looks like:
Var foo:IClassSelectorListSupport =
myInstance.getBeadByType(IClassSelectorListSupport);
If (foo)
foo.someAPI = "foo";
I've not done any measuring of which is better, but the latter does not require
glue code, which allows someone to just drop in any IClassSelectorListSupport
bead on some other random instance they've created and not make any assumptions
about base classes or proxy code to interfaces. For example:
<my:Instance>
<my:beads>
<j:JewelClassSelectorListSupport />
</my:beads>
</my:instance>
This instance, declared in MXML without the overhead of creating a TLC and
proxying code to the API surface, would work with the "has" code, but not the
"is" code.
Is it all worth it? I don't know. I'm waiting to see folks try it, especially
in more complex apps. But for sure, composition "has" patterns are more
flexible than traditional "is" patterns.
This "has" pattern allows us to sort of have multiple inheritance or "mixin"
type patterns, but it is done by accessing the beads directly not by proxying
code to the TLC so "inheritance" isn't an accurate word. TLCs are more for
convenience, to avoid repeating a lot of tags in MXML or calling addBead a lot,
but we should try to make our code even more flexible by not requiring folks to
work with TLCs.
So, in theory, you should try to package the implementation of
IClassSelectorListSupport into a bead and see if you run into problems adding
it to Group and other base class ancestries. And then consider using the "has"
pattern or at least "is" with interfaces instead of concrete classes.
Think "beads" not TLCs.
My 2 cents,
-Alex
On 4/30/19, 7:48 AM, "Carlos Rovira" <[email protected]> wrote:
Hi Yishay,
El mar., 30 abr. 2019 a las 13:27, Yishay Weiss (<[email protected]>)
escribió:
> It seems to me this is more an AS3 issue than a Royale issue. It would be
> cool to enhance AS to include features such as Mixins, but as long as we
> have a simple single inheritance model the solution you implemented is
> probably optimal.
>
>
If you and others think is the best we can get, maybe we can left as is.
Maybe we can just optimize as Andrew said making that a class to be
implemented by the rest of classes, in that way all share that and changing
just one class will change the 3 implementors at once...
> Also, in my opinion you should always check to see if a said object
> implements an interface, rather than extends a class. If you stick to that
> your solution works fine.
>
>
the problem with this is what I stated before. I expect users to use
UIBase/StyledUIBase classes instead or interface in this class of context.
And the problem in this case is that we can't generalize a code since not
all components will be "StyledUIBase". For me this is the most serious
problem
--
Carlos Rovira
https://nam04.safelinks.protection.outlook.com/?url=http%3A%2F%2Fabout.me%2Fcarlosrovira&data=02%7C01%7Caharui%40adobe.com%7Cf54ca163f75d412f90ff08d6cd7aeafe%7Cfa7b1b5a7b34438794aed2c178decee1%7C0%7C0%7C636922325199044381&sdata=FtMO5gSLB5oio5pynuXAdg1cuxJHJkiDYev3crELiXA%3D&reserved=0