So I have these Service Objects that are instantiated during onApplicationStart(). During the onRequestStart(), I make a pointer in the variables scope to each, so they can just be called by their name (cause 'UserService') is the same as ('variables.UserService') and the latter takes less to type. GO TEAM CODE PRETTY-FICATION!
Anyways, during the processing of the Request, I'm finding I have to pass (as a reference) the entire variables scope to all my functions, just in case they have need of a Service. Heck, I have a Config object that holds the application's configuration settings, so if a method needs to know the path to the controller's folder, it needs to be able to get to the variables scope where I stored my Objects.
But with all this object passing, I feel I'm not doing this in the most optimal way.
From the application.cfc point of view, the variables scope has those objects in it. But after code in the application cfc does some includes of files which might include other files, those lower-level files don't seem to have access to that upper-level variables scope (espeically when they are called from the context of other services and methods which have their own respective variables scope)
So is there a simpler way of doing this, or is this what I am relegated to, since I am choosing to take an OOP design approach rather than procedural?
An alternative to the variables scope might be to use the request scope. I think this is the pattern frameworks like ColdBox and FW/1 use (I could be wrong about that). I know that they both make use of a main 'RC' variable that is passed into functions, and the RC variable contains all of the other variables needed for any part of the request process (like within service function calls). You might look at the FW/1 docs to see how that works.
As for where to store objects, it sounds like you might be needing to use a Dependency Injection system (like DI/1, ColdSpring, or WireBox). Then you don't have to worry at all about where objects are stored (other than in the configuration file for ColdSpring or the "Convention over Configuration" approach of DI/1, or "annotations and conventions" approach of WireBox).
Since I'm also a neophyte with OOP, I'm sure I've misstated some of the above, and hopefully someone with more experience will chime in and set things straight.
You know, I had my own "RC" object which was a RequestContext, but it only contained a copy of the FORM, URL and CGI scopes. It is stored in the REQUEST scope because it change son a per-request basis. So if I understand what you're suggesting, I could add a pointer to my variables-scoped objects into the RC and then as long as I pass that RC to my methods and invoked/included files, it should point all the way back to those services.
I am going to look at FW/1 and see how they do things; hopefully they have some good documentation which explains their process. I know Sean Corfield's a darn good CFML developer.
As for the Dependency Injection, I know that I currently AM doing DI, but there's a chance I'm not doing it right. For example, I have a Data Source object, a User Gateway object (that needs a data source) and a User Service object (that needs a gateway, which needs a data source). When I build these objects, I do them in that order, and then inject one into the other, so that at the end I have a User Service object who has a Gateway property which is the Gateway Object which has a Data Source property, that is the Data Source object. And it all seems to work great, I can make calls like:
UserService.getGateway() <- Returns the Gateway Object.
UserService.getGateway().getDataSource() <- Returns the DataSource Object of the Gateway Object.
UserService.getGateway().getDataSource().getUsername() <- Returns the Username property of the DataSource Object of the Gateway Object.
So for the most part, I have just been communicating with Service Objects. But I think I get what you mean. If I had a RequestContext which contained all my needed data for a request, I could just inject him into locations that may need to reference that data.
I think this "Put it in the RC" approach might be a good resolve. Thanks for the input Carl!
I wasn't suggesting putting your objects into RC, just any variable values that the service objects may need to do their jobs. Frameworks typically do what you are doing - combine the URL and Form scopes into RC (I'm not sure about CGI though) automatically. Then you add in any other variables that may need to be passed into service objects (including, I think, the return values from previous service objects).
If you truly do DI, you don't have to have to build the objects in order and pass them into one another, or create the objects and store them in the variables or request scope. The DI framework does it for you. The details of how you "wire up" the dependencies vary between the DI frameworks, but essentially you code your objects to know only that they depend on another object (whether through an XML config file, an annotation on the component/property definition, or just a defining a property that is an object). The DI framework then goes out and creates the dependency objects for you (or if the dependency is a singleton object, it creates and caches the singleton somewhere and passes it in wherever it is needed).
OK. I found the Reference Manual for FW/1, and I'll give that a read through.
I think I get what you mean by DI (specifically DI/1 'framework' calling itself a framework).
My framework current DOES build the inital objects off of an XML file (auto-wiring objects into others, like the aforementioned User Gateway Object into the User Service's Gateway property.) I think if I give both FW/1 and DI/1 a read up on their manuals (and hope that they are written well to explain their process), this will help to give me insight about simplifying this inter-object dependency and what may be the most optimal way of referencing that.
I do like how Sean simplified (convention) the MVC process.
Well, FW/1 was a good read, but as odd as it sounds, everything was making sense until they got to the point about views and layouts.
I have not been able to determine what the difference between them is, as both contains output, but layouts invokes some kind of cascading capture and insert process (a possible convention the FW uses)
I can see how they actually pass in the FW's variables scope into the controller's init() function so that it has a variables.fw which points to that location, giving the controller access to the methods and data in the FW's variables scope. That seems to be the cornerstone of sharing the data across the application.
Oh, so views and layouts together would compromise what before might have been taken care of, procedurally, by a Templating engine.
That makes sense, and would answer why layouts "cascade" upwards. Good insight Carl.
I was told that in OOP, I should not have my objects be aware of anything not directly passed in to it.
Sure I could make application.DataSource, but my issue is that I have multiple objects and methods that have need of that data, and it is not advised to simply have the code reference "application.DataSource", but to instead have an argument that has the application.DataSource passed to it, and which point it can refer to it as arguments.DataSource.
My issue is that I'm getting to a point where some of the objects I'm storing in the application.cfc's variables scope aren't available at the currently executing template (be it invoked, cf-module'd or included) because the internal executing templates have their own variables scope. So what I need to do is, in essence, find a structured way in which I can pass in the application.cfc's variables scope as part of a structure/variable that will exist any any level of executing template.
Thanks, Aegis! You paint the picture clearly.
I share your preference for OO and 'information hiding'. Like you, I have found that combining ColdFusion's native rules of thumb with those of OO can be complex. However, I have often solved difficult design problems by looking instead into the converse of a rule.
Take your own design, for example:
some of the objects I'm storing in the application.cfc's variables scope
According to this, the variables are privately owned by the Application.cfc component. Therefore, by your OO reasoning, which is quite correct, any template or component that requires such a variable must obtain it via a function call. That would imply instantiating Application.cfc.
However, that would break one of the fundamental rules of ColdFusion. Generally, Application.cfc is not designed to be instantiated.
It is at a point like this that I would reevaluate the use of variables scope in Application.cfc. In fact, one obvious solution is to store variables meant for template requests in the request scope within onRequestStart. As they say, what's in a name?
Then again, Carl suggested this, too. You actually thought of it earlier yourself! So, I'll stop trying to sell this fridge to an Eskimo.
I think you hit the nail square on the head. And after doing research into other frameworks, they DO pass the application's variable scope into other objects in order to share their information. Many also seem to make use of a "Request Context" object that holds data for the current request and can be passed around the different areas of the framework.
I guess ColdFusion doesn't have a "traditional" OOP background, but I believe this SHOULD get me to a point where I can pass the highest tier in and have it available at lower contexts. Thanks!