PDA

View Full Version : Tying MVC together with AS3


NickZA
06-10-2008, 01:29 PM
Hi

I'm building the framework of a game at the moment and am using the MVC approach. The State DP is integrated into this structure to define gamestates. Also, there are a numerous singletons in the class structure including 2 discrete data models, the logger and the console.

While broadcasting events out from these objects enables loose coupling, the array of controllers (states) need direct access to the data models. For MVC, this is typical.

So, my problem is getting a data model reference out to the various states that perform the game logic. My problem is essentially twofold:

1. I want to maintain loose coupling to keep my codebase extensible.
2. I want to use the singleton pattern for singleton objects, so that there is a single, accessible point of access to the model for all objects (such as states) to reference each singleton. (I am not worried about enforcing single instantiation, only about referencing a single instance.)
3. I want to give my controller objects an implicit way of referencing the data models (preferably in keeping with point 1), eg. I want to avoid passing arguments, using globals, etc.


When the model is created, I can do one of the following to get it's reference to the individual states:

--Create the singleton objects and have any states call a static getInstance method of the Singleton superclass -- except static functions aren't inherited. This is my preferred route but I can't get it working, see here:
http://www.kirupa.com/forum/showthread.php?t=223798&page=35
(In spite of my response there, I later realised I was no better off with the given "solution".)

--Create semi-singleton objects (no instantiation enforcement, but still have non-static getInstance() methods referencing a static instance variable). I like this option, but can anyone clarify why Singleton's need a static getInstance() method? Am I safe in dropping it?

--Every time a new state is called, pass the data model ref into the new state, from either the old state or the StateManager (which holds a permanent reference to it), as a parameter. I don't like this: it just seems ridiculous to be passing params when in reality every state should, by it's very nature, be able to access the model. In fact, the model(s) should be able to be accessed by pretty much any object in my program that wishes to access it.

--Don't enforce singleton behaviour, instead store the ref in global variable. I smell rubbish...!


Long post, I know. Any ideas? :confused:

-Nick

creynders
06-16-2008, 09:38 AM
I'll answer you questions in a different, but probably more understandable order:
Create semi-singleton objects (no instantiation enforcement, but still have non-static getInstance() methods referencing a static instance variable). I like this option, but can anyone clarify why Singleton's need a static getInstance() method? Am I safe in dropping it?
You need some kind of static access point. Whether it's a property or method is irrelevant, but it will have to be static. Why? Because the whole point of the singleton class is to leave the responsibility of instantiation out of the hands of the user of the class. If you create a non-static method ( aka a INSTANCE method ) you already need an instance to ... access the instance. As you can see that's a bit like the chicken and the egg.
Create the singleton objects and have any states call a static getInstance method of the Singleton superclass -- except static functions aren't inherited. This is my preferred route but I can't get it working
And you won't get it working. There's a reason why you haven't found a extendable singleton class online, because it's not possible. For each singleton class you'll have to write a getInstance method and and a instance property, there's no escaping that.
For a singleton class you need a static property which stores the single instance of the class and a static method to access that static property. Since static functions and variables aren't a part of the inheritance chain, what you're trying to do is impossible.

So, the real solution is:
drop the idea of a super singleton class.
Implement the singleton pattern in each class that needs it.
Let the various states access the singletons directly.

NickZA
06-16-2008, 11:16 AM
Hi Creynders

Remember receiving useful help from you in the past, good to see you again.

I didn't think that there was a way around it and since writing this had started to come around to your way of thinking -- thanks for the shove, however :p. I think it was needed.

As you say, there are a ton of different methods for implementing faux singletons in AS3, and not one of them can satisfy all criteria of a true singleton, obviously.

Thanks again.

-Nick

swivelmaster
08-26-2008, 07:27 PM
Why not use an MVC framework that has already been built for you?

We've been using PureMVC at my company since before I started working here and it solves all of those problems.

creynders
08-27-2008, 07:18 AM
Yep, puremvc is magnificent. The learning curve might be a bit steep if you're not familiar with mvc, but it's worth the trouble. And the most excellent part of the framework is the huge amount of documentation, tutorials and info found in the forums.

swivelmaster
09-01-2008, 10:32 PM
I'd actually say the documentation for PureMVC both hurts and helps. When I started here, my boss explained a little bit about MVC in general, a bit about PureMVC, then showed me their incredibly complicated chart which demonstrates the structure.

My eyes glazed over.

Then he said, "Yeah this chart isn't very good. Ignore this. I'll just show you what we're doing with it."

Unfortunately, PureMVC doesn't solve *every* question about design, so we've been able to code some pretty rickety stuff that started out as a quick fix and then sat around for months before it was cleaned up. This is also a byproduct of just how flexible Flex and AS3 are and the fact that it's possible to put business logic in your view components (generally a bad thing to do) in Script tags. So be careful!

NickZA
12-04-2008, 03:25 PM
Hah, what a coincidence.

I just did a search on "as3 loose coupling" in google, and this was my first result. Is it any coincidence that I happen to be working on MVC once again, and retrying it from a new angle? :)

As it happens, I dropped the idea of using singletons a while back. I just read this article about why not to use them, and to me all these arguments make sense:

http://www.as3dp.com/2008/11/26/we-don’t-need-no-stinkin’-singletons-why-to-avoid-the-singleton-pattern-in-actionscript-30-programming/

ANYHOW, to my question....

I am still trying to "tie MVC together with as3", as the topic goes. I've built a functioning model and view (or parts of the overall, anyway). "Functioning" doesn't mean "extensible" though, and so here comes my (new) question on this topic.

I have a package net.blah.model containing 2 classes:

MapData (the actual data model)
MapGenerator (creates/affects the model, but is really sort of a controller)

I also have other packages net.blah.view and net.blah.controller.

MapData has methods which make changes to it's own data. These are specified as internal and so they can be called by MapGenerator, which needs to affect the model. That is why I encapsulated them in the same package. At first this seemed fine, but if I'd thought this througha little better I would have realised... :mad:...

MapGenerator isn't the only thing that will be able to affect the map. During the game, for example, you can tunnel through walls (think Roguelikes). This means the MapData model will be affected by game controller logic directly (note, my initial distinction was that the MapGenerator is not "in-game" but rather "pre-game" logic, so this didn't matter much). So now I realise I will have to change the MapData model on the fly -- meaning that my current "tight-coupling of classes within the same package" method no longer works. The controller classes for in-game will be in the net.blah.controller package and thus will not have access to internal methods of MapData.

This brings me to my point. I have often considered setting up a competely event-based application framework, and in this case I wonder if it is not the only real solution in the absence of something like friend classes which C++ has. Even if AS3 had friends, this would promote tight-coupling.

Is interaction between the Model, View and Controller in AS3 best implemented using event broadcast between the members of the triad? For that matter, is this a good assumption when using other languages such as Java? And does this seem to you to be the best solution for my game MVC, as it presently does to me? Are there any pitfalls to be aware of?

Many thanks to anyone who reads allthe way through this. :rolleyes:

Regards,

-Nick

creynders
12-05-2008, 07:52 AM
Is interaction between the Model, View and Controller in AS3 best implemented using event broadcast between the members of the triad? For that matter, is this a good assumption when using other languages such as Java? And does this seem to you to be the best solution for my game MVC, as it presently does to me? Are there any pitfalls to be aware of?

I'd say yes and no. Since communication through events promotes loose coupling. The dispatcher doesn't need to be aware of what exactly is listening (or even if anything is listening) and the listeners don't need to care where they get the events from.
So the model should signal updates to it's data through events, but I don't think it should distribute that data through events. The controller and view should listen to the updates and then use a direct reference to the model (or a reference to a common interface) to access the updated data.

And then about your "internal" model problem: what you could do (if you don't want to refactor your model) is create a proxy for the model, which is public and then access the model through the proxy. In the proxy class you can use the flash.utils.Proxy class to pass all property and method calls to the model and back.

NickZA
12-05-2008, 08:51 AM
Hi Creynders, thanks for stopping by.

The controller and view should listen to the updates and then use a direct reference to the model (or a reference to a common interface) to access the updated data.

Just so I know I'm on the right track here, the direct reference would really just be the event.target, wouldn't it? (and as you say, as specific class or a general interface) Particularly in the absence of static refs / singletons.

And then about your "internal" model problem: what you could do (if you don't want to refactor your model) is create a proxy for the model, which is public and then access the model through the proxy. In the proxy class you can use the flash.utils.Proxy class to pass all property and method calls to the model and back.

I certainly do want to refactor my model in as correct a fashion as possible at this stage. But I gues this is a plan B. From what I understand (having only just looked into the Proxy class for the first time now that you mention it) it doesn't seem a very "pure" solution.

Groady
12-09-2008, 03:24 PM
@NickZA

Regarding your internal-model issue I agree with creynders in either using a proxy OR refactoring your model by including an entry method visible to your game controlers. You would either simply change your interal methods to public or create a new method which will interface with the relevant existing methods. It makes sense (to me) that all traffic going in and out of your model package will go through the MapData model.

On a side note, you say you've abandoned the Singleton design pattern. How are your view's and controllers getting their reference to the model? Through their constructors? Just curious.

NickZA
12-10-2008, 01:31 PM
Okay...

I've looked up flash.utils.Proxy in a couple of places and still don't have a clear idea of what it's for in this instance, so let me offer this idea first:

For each of M, V and C (each encapsulated in its own package): Setup a proxy class in (and for) each package. This proxy will create one instance of each class internal to this package which would need to be used by clients external to this package. The Proxy will take method requests from outside clients and will use a given classname to correctly qualify and call that method on the real object (of which, as stated, there is only one instance). Obviously, the method used on the proxy to do all this will be public.

So the proxy not only creates singular references to key class instances, it also holds them so that external classes can do what they need to do, whenever they need to do it.

Right, now that I've proposed that -- is this basically what flash.utils.Proxy does? :confused:

EDIT: In response to your question, Groady, yes at the moment I create the MapData instance and then pass it into the Renderer instance via it's constructor. It's the lesser of various evils, and I don't want to keep it that way, ultimately.

-Nick

Groady
12-10-2008, 02:24 PM
With your view and controller packages I'm not sure proxies are necessary. For example, Views should only retain a reference to their specific controller and the model. If a View had access to a Proxy in the controller package wouldn't it have access to the proxy methods of all th other controllers? Same for controllers referencing a proxy in the view package.

I could see value in having 2 proxies in the model, one for reading (referenced by view's only) and one for writing (referenced by controllers only).

It's an iteresting topic and I enjoy hearing how other people implement their own MVC patterns.

NickZA
12-10-2008, 02:38 PM
Groady,

I see what you're saying, good point about "too much access".

Doesn't bring me any closer to an answer though, and this is driving me mad. :mad:

I take it that flash.util.Proxy doesn't do the same thing, then?

Here is what I want -- MVC without the following:

-singletons
-static refs
-weird hacky bits that are really just AS3 specific

.. And with:

-good OO design that would be acceptable in another language eg. Java if I port this code (which I might well be doing), meaning a truly encapsulated object interface for everything.

The thing that screws me here is package access levels, and that may really just be because I'm more used to the way C++ works. I want to know how this kind of thing is ideally done in ECMAscript languages (eg. AS3) and Java where a package system is used. Without static refs and singletons, the only way I can see to getting this going is by using a "overseer" class (eg. but not necessarily proxy) which instantiates everything once, then manages who gets what references. What do I need here? Is there some DP that will allow me to do this?

EDIT: Am I complicating this too much? @Groady, you said "I'm not sure proxies are necessary". Is this because I'm using constructors to pass in references? Should I just leave it at that, is this all I need? At the moment I have no controllers as such, just the document class which calls the map generation function, spits out a map which I then pass into the renderer as a constructor param, which then renders it. Should I just proceeed along these lines for the controller(s)? With either the document class or some sort of master controller in charge of instances?

I think I need to go away and think a little more.

-Nick

creynders
12-11-2008, 09:19 AM
flash.utils.Proxy is like a dynamic class which gives you the oppotunity of intercepting all calls to methods and properties.
So, let's say you've got a class SomeProxy which inherits from flash.utils.Proxy, then if you do this:
var sp : SomeProxy = new SomeProxy();
sp.whatever = 5;

inside the SomeProxy class you're able to intercept the call to the "whatever" property (which in this case doesn't really exist) and run any code you want.
For instance you could've written this inside SomeProxy:

//SomeProxy.as
private var someObject : SomeOtherClass = new SomeOtherClass();
override flash_proxy function setProperty(name:*, value:*):void {
if( someObject.hasOwnProperty( name ) ){
someObject[ name ] = value;
}
}

A proxy is like a middle man, it takes all calls and passes them on.
In your case you could use it to make properties and methods of another class public, while in reality they are internal.
flash.utils.Proxy has one SERIOUS flaw and that's that you loose all code hints to the class it proxies.
In the example I gave SomeOtherClass has a property "whatever", but since the SomeProxy class doesn't, you won't see "whatever" as a code hint when you use the SomeProxy class.
flash.utils.Proxy can be the perfect solution for some specific cases, but in general I WOULD discourage the use of it.

MichaelxxOA
12-12-2008, 01:35 AM
Do you have any specific pieces of code you are worried about in your framework?

NickZA
12-13-2008, 07:02 PM
Do you have any specific pieces of code you are worried about in your framework?

No, I am trying to piece together the bigger picture.

MichaelxxOA
12-15-2008, 11:32 PM
...
Without static refs and singletons, the only way I can see to getting this going is by using a "overseer" class (eg. but not necessarily proxy) which instantiates everything once, then manages who gets what references. What do I need here? Is there some DP that will allow me to do this?
...


Have you looked into Dependency Injection (http://martinfowler.com/articles/injection.html)?


...
MapData (the actual data model)
MapGenerator (creates/affects the model, but is really sort of a controller)
...


Not MVC related, but have you tried approaching the relationship between map data and map generation differently? IMO MapData doesn't do enough and MapGenerator does too much.

Why not make all of MapData's "internal" methods "public" and discard MapGenerator once MapData has been created?

Also, thinking of these two classes as GameMap and GameMapParser seems to fit better in my brain.

A Controller (as in MVC) should interpret user interactions like mouse clicks and key presses into actions that can be carried out by the Model. I mention this because you'll find yourself creating a Controller for each View instead of a Controller for the Model.

NickZA
12-16-2008, 07:07 AM
@Michael

Worth mentioning that I've come somewhat further with this, so am happy with the way numerous things stand at the moment.

Re dependency injection -- I have indeed, but maybe I need to again. I shall do so again today -- thanks.

Re the relationship between MapData/MapGenerator -- there is much more here than meets the eye. I will have top-level narrative generator as well, so the flow works as follows (read arrow as "gets fed into"):

NarrativeGenerator(output is NarrativeData) -> MapGenerator(output is MapData)

I'm perfectly happy with this. If you'd read the thread carefully you would noticed all along that I my aim is to avoid making methods & members public without good reason. I don't only work in ActionScript: I work to OO standards that are good across a range of languages.

Re your statement on controllers -- I'm aware of that, thanks.

MichaelxxOA
12-16-2008, 04:09 PM
So, I actually spent quite a bit of time reading over this thread. Not much info about how your stuff is working so I had to make a few assumptions. Generator makes sense now though, thought maybe it was creating a map from data in a file.

Why are you trying to avoid public methods?

NickZA
12-16-2008, 04:32 PM
That's because I'm interested in the general concepts of how to implement MVC in AS -- the current codebase being of little importance. I think the assumption that my code has any bearing on that would be a little bit presumptuous ;) For those who are hunting for snippets, there are SWF decompilers up for grabs here (http://www.sothink.com/product/flashdecompiler/).

Regarding public methods, do you know what access specifiers are for? If you do, you will know the answer to your question.