Accessing stored settings in model through out application - best practice
Support Staff2 Posted by Shaun Smith on 13 Jul, 2010 01:26 PM
Sounds good to me, though it depends what kind of stuff you're
loading from that file - lumping it all into a VO and wrapping it
in a single Model (perhaps with a name like SettingsModel)
might not be the best approach. Are the values you are
loading all related? Do they really belong together?
Support Staff4 Posted by Shaun Smith on 13 Jul, 2010 03:57 PM
What I'm suggesting is that the concept of a Settings Model (or
a bunch of separate Settings-type Models) is not usually a good
one. It sounds like those values don't represent application state,
but rather simple settings that will not be altered once the app is
running. Perhaps after loading and parsing the settings you could
simply pass the values through to relevant application actors as a
part of your setup process. For example setting a URL property on a
Service. It really depends on the app though.
To answer your original question though: wrapping a VO in a
Model, mapping that Model as a singleton, and injecting that Model
into other parts of your system is often a valid usage pattern.
7 Posted by Thomas Troelsen on 01 Aug, 2010 08:28 AM
I have an additional question related to this discussion:
I have created a RemoteAct (actor that works as a proxy for my remote services) and a SettingsAct (actor that parses an XML documents and store all settings in a VO).
When i initialize the RemoteAct I want to acces the SettingsAct in the RemoteAct's constructor to fect eg. the URL of the service. So I inject the SettingsAct in the RemoteAct through an interface:
public class RemoteAct extends Actor
public var settings:ISettings;
private var _endpoint :String;
private var _destination :String;
private var _source :String;
/** constructor */
public function RemoteAct()
_endpoint = settings.getSetting("zend_endpoint");
_destination = settings.getSetting("zend_destination");
_source = settings.getSetting("zend_gateway");
(further details omitted)
The SettingsAct is mapped to ISettings correctly in my MainContext class. I can inject and access the settings (ISettings) in my Mediators, but when I run the above example and initialize my RemoteAct, an exception is thrown.
The trace(settings) results in null.
It seems to me the SettingsAct has not yet been initialize when I try to inject it, eventhough I inject it into my startup command before i inject the RemoteAct:
public class StartupCmd extends Command
public var settings:ISettings;
public var remote:RemoteAct;
(further details omitted)
1) Is this bad practice to access an application's settings as in the above example?
2) Why can't I inject and access the ISettings in my RemoteAct actor class, and what should I do instead?
Support Staff8 Posted by Shaun Smith on 01 Aug, 2010 11:22 AM
Dependencies injected via setter/property injection are not available until after the instance has been created - it's pretty easy to visualize, just imagine doing it by hand: first you create the new instance and then you set the properties.
The most common solution to this problem is to remove the code from your constructor and place it into a public method with [PostConstruct] metadata placed above it, like so:
public function init():void
// all dependencies have now been satisfied
Constructor Injection Solution
Instead of using property/setter injection you can use constructor injection, simply define your dependencies as constructor arguments.
A word of warning about constructor injection: Due to a bug in the Flash Player (pre 10.1), full type information for constructor arguments is only available after the affected class has been instantiated at least once. To work around this bug, SwiftSuspenders (the default DI/IoC framework that Robotlegs uses) checks if type information for the arguments is available when performing constructor injection. If not, SwiftSuspenders will create one throw-away instance of the class. Because of this behavior, it is important not to start any complex processes in constructors of classes that are used with constructor injection.
9 Posted by Thomas Troelsen on 01 Aug, 2010 10:15 PM
Thanks a lot Shaun for both the quick answer and the in-depth explanation - it works perfectly. All theese answers you and your team provide are really great lessons in system architechture and best practices.
Just a quicik note to my question 1) in the above post. Is it bad practice to inject the ISettings into the RemoteAct class as in my example?
I make a tight coupling by requiring an ISettings implementation before I can use my remote service, but on the other hand: I would never want to hardcode eg. the server url into the service class, so I thought it was a reasonably solution?
Theoretically, yes, it could be considered bad practice: settings, when you think about it, are just dependencies and the whole point of automated dependency injection is that you can flatten out these dependencies so that you don't have to "reach through" other objects to get what you need.
Another approach would be to simply set these values up as named dependencies and then inject them directly into your RemoteAct (which would more accurately be called a Service). Something like this:
// In startup, or in a configuration Command:
injector.mapValue(String, "your_zend_endpoint_value", "ZendEndpoint");
// In your Service:
public var endpoint:String;
In general, God-Objects (usually with names like "Manager", "Config" or "Settings") create a lot of coupling in your system and should be avoided.
As a newbie to Robotlegs I have been following all of the tutorials and examples which have been great but I wondered if you knew of an example which utilises a best practice way of dealing with these config settings? Particularly Service endpoints.
Are you suggesting from your example above that the service endpoint is held in the context file - rather than read in from an external XML file?
I don't know of any "best practice" demos that load config settings at runtime unfortunately. Loading from an external XML file would probably be the best way to go for something like service end points - in my example above I was just showing the minimum steps to accomplish that.. the mapValue call could very easily be supplied values from an XML file.
you should be able to get your endpoints loaded into the context for immediate mapping if you instantiate the context later in as3. Its just a one off to load the xml and then dump that into the context for set up. I dont think you can do it all at once in the contex.