how to properly write good "abstract" code

120 views
Skip to first unread message

Atul Kumar

unread,
Nov 15, 2015, 8:44:54 PM11/15/15
to Haxe
Hello community,

I am trying to abstract the rendering part in my application and seem not to find a good pattern. So I thought maybe someone could help me with some tipps. So here is how I tried to tackle that so far:

For example I wanted to write a Picutre Gallery where you can Swipe through some picures. So I have a Gallery class which takes items of type "Picture". So lets try to abstract "Picture". Lets say I target flash and js. I could define typedefs with conditional compiler arguments:

package gallery;

class Gallery {
 
function addPic(picture: gallery.typedefs.DisplayObject){...}
}


package gallery.typedefs;

#if flash
 
typedef DisplayObject = flash.display.DisplayObject;
#else
 
typedef DisplayObject = {x: Float, y: Float};


Thats not so great already.. because I have to put the typedef definition somewhere with my "Gallery" library. So that means if somebody wants to use my Gallery Library and wants to add another target it is required to add that to the "gallery.typedefs" package. As far as I can see there is no way to have it outside.

But ok.. so be it. To the next problem.

So, we still need a renderer for js. Let's implement pixijs. Guess if I want to get that in my typedefs I have to add a compiler condition (like "-D pixi"). That could look like that then:


package gallery.typedefs;

#if flash
 
typedef DisplayObject = flash.display.DisplayObject;
#elseif pixi
 
typedef DisplayObject = pixi.core.display.DisplayObject;
#else
 
typedef DisplayObject = {x: Float, y: Float};


but actually that does not work. Pixis DisplayObject does not have object.x. Instead it has object.position.x.

Alright.. but there is this great haxe feature called "Abstracts". Let's try and use that:

...
#elseif pixi
 
abstract DisplayObject(pixi.core.display.DisplayObject) {
   
public var x(get, set): Float;
   
public function get_x(): Float { return this.position.x}
   
public function set_x(value: Float) {
     
return this.position.x = value;
   
}
 
...
 
}
...


Looks good.. but actually isn't.. because it does not work either. Because if I do:

gallery.addPic(new pixi.core.sprites.Sprite(texture));

I get a compiler error that addPic() expects and instance of gallery.typedefs.DisplayObject.

That is to bad.. because I would have expected that the abstract becomes pixi.core.display.DisplayObject, and since pixi.core.sprite.Sprite is inheriting from that class it should work. So it seems abstracts are not aware of inheritance?

So in the end all this means the only way I can find to abstract this stuff is to do it like in the old days: use an Interface and write wrapper classes which acts as a mediator or extend the plattform specific classes and implement that interface. Also not so nice, because that adds a bit of runtime overhead. But I can not come up with something better.

I thought maybe there is something I am missing.. maybe somebody have some hints or suggestions? Maybe there is a macro for that?

Cheers
Message has been deleted

Justin L Mills

unread,
Nov 15, 2015, 10:02:33 PM11/15/15
to haxe...@googlegroups.com
Maybe consider a totally different approach.
Rather than work with the display object directly you can work with a
virtual object do calculations on it and then just update your images
position in the render cycle, if you feed the width and height to the
virtual object you can do calculations of hit test and position etc...
One main advantage is that you can then work with quite low level
rendering and have code that is quite platform independant and keep the
graphics structures flat, doing stuff like using one RenderTextures and
dealing with all object updates in a single function and work with very
lean classes. For instance you can have a structure that calculates if
an object is compleatly outside the render window so if you want to hook
that up to create and destroy objects automatically just keeping the
reference to the image name in a structure then you can minimize
resources. And rather than creating and destroying Sprites or similar
you can just use a Pool and grab one that is not used and repopulate
it's image.

Domagoj Štrekelj

unread,
Nov 16, 2015, 3:19:54 AM11/16/15
to Haxe
Hello,

I recently found myself in a similar situation. Hopefully some of my insight will help you figure out your next move.

My first thought was much like yours - use abstracts to, well, abstract away the disparities between platform-specific implementations. Abstracts are a powerful tool, but this just wasn't the right time or place to use them. I quickly fell into a trap much like the one you describe and I found myself writing a lot of accessors and modifiers. Writing code like this felt "improper", even wasteful. Dissatisfied with how things were turning out I started looking through some GitHub repositories of projects akin to my own. This is usually a good course of action when stuck - look at how others do it, and try to do it better.

Ultimately, I settled on creating a class that wrapped around and exposed platform-specific code through conditional compilation. I try to keep this class as "virtual" as possible - which is why abstracts, in this case, sound like a natural solution. There's not a lot of overhead thanks to the compiler's DCE, and I'm able to organise my code as long as I define / expose properties the wrapping class expects.

Looking back, however, I feel like I could have adopted something along the lines of HaxeFlixel's type limits had I planned things better. Maybe I'll save that for the next refactor.

Yoni Gueta

unread,
Nov 16, 2015, 6:26:18 AM11/16/15
to Haxe

your abstract DisplayObject is missing a "to" pixi.DisplayObject statement. that's why the compiler is complaining:

example:

Atul Kumar

unread,
Nov 16, 2015, 6:21:04 PM11/16/15
to Haxe
Thanks for the suggestions. Yes.. I was thinking about wrappers and that kind of way, but was not satisfied with the overhead they would create (even tough it might be minimal).

@Yoni.. thanks a lot. That was exactly what I was looking for. Somehow I missed that in the Documentation. Works like a charm!

Now the only "not so nice" part is, that I have to define those abstracts inside my library. So still library code has to be changed when u want to add another target. But I guess there is no other way.. on the other hand it would probably go into ugly c++ "#ifndef" direction.

Yoni Gueta

unread,
Nov 17, 2015, 4:51:44 AM11/17/15
to Haxe
cool :)

as for your other issue, while its not exactly a "simple" solution, awe6 framework did pretty much what you need:



Atul Kumar

unread,
Nov 17, 2015, 4:46:08 PM11/17/15
to Haxe
Thanks for the link. Very interesting.

Even though I never wrote any macro code I think I am understanding what they do. It seems to address exactly that use case.

I am not sure if I want to use that layer of complexity yet.. but it is good to know how a solution could look like. Amazing how much is possible in Haxe.


Cheers
Reply all
Reply to author
Forward
0 new messages