|Think about Loose Coupling|
Re^5: scripting frameworks - a cursory glanceby krazyk (Beadle)
|on Mar 26, 2010 at 16:43 UTC||Need Help??|
First, please take a look at one of the sample apps, showing how Applications and Commands are related and how they share data using a cache. Specifically, look at the Application's init() method and the run() methods in the commands.
You're right it wouldnt. That was a mis-wording. But: the command object should access application object resources via $self->app->$resource not the current way $self->$resource where $resource might be a slot in the application object or a slot in the command object.
It sounds like you are misunderstanding the conceptual relationship between Commands and Applications. Commands, in general, are not supposed to access Application object resources because Commands are usually to remain decoupled from Applications entirely. Commands could conceivably be used outside of an Application. There are exceptions, and for those relatively uncommon cases, the Metacommand is provided (see an example of a Metacommand in use).
The obviously-evil $self->$resource (where $self is a Command and $resource is a slot in the application object) is certainly not allowed. For Metacommands, access to Application object resources is supported exactly the way you endorse (though with slightly different syntax $self->get_app->$resource).
And: the cache is a resource that the command requires the application to implement and provide. You mentioned uploading Commands to CPAN separately. What sort of interface should a command have with the application? Should the application boldly and indiscriminately put any numbers of slots in the command? Or should there be one well-defined interface via one method between command and object, namely $self->app
I agree; that's what the cache provides -- one well-defined interface (albeit using two methods instead of one): Cache interface. The commands should (and must) always use that interface. The Application will only interact with the command using this mechanism...
This code (in CLI::Framework::Application):
Should raise a red flag, because over time as various commands require various application-level resources, you are going to have to manually keep coding the wedding between application and object here with additional slot injection instead of one well-defined interface via one method between command and object, namely $self->app
No. Here, I'm sharing ALL session data with the command. The cache is NOT one example of potentially many future resources. It is a container for all resources that will ever be shared between the application and its commands.
Any given Application will set shared data in the cache (during init(), for example) and the Application will automatically pass on a reference to the cache so the Command has access to it (without requiring that the Command be strongly-coupled to the Application).
n-fold fanout Just think of what you're doing... let's just say there are 3, as you put it, "instance variables kept in both Application and Command. ", cache1, db_handle, interprocess_communication. Now, you have 4 commands. With your approach, you install all 3 of these accessors in all 4 commands, for a total of 12 accessors for sharing data between application and command. With my approach, only the app accessor is shared between application and command, for a total of 4 accessors. As the number of shared resources grow, your "accessor bloat" grows linearly. My "bloat" is constant at 1.
Wrong. You've mis-quoted me here. I said "...an instance variable (a reference to the Cache object) [is] kept in both Application and Command." Once again, that Cache object is a single container for all shared data. Keeping a single reference in the Application and Command objects and providing access to it via one accessor in each superclass is quite a bit different than keeping numerous accessors in every single command subclass.
Because all shared data is managed via the Cache and all commands implement a common interface to the Cache (by virtue of inheritance from CLI::Framework::Command), there is no "accessor bloat" -- the only accessor related to shared data is the one documented here: cache accessor in Command class.
Finally, what happens when each command needs a cache in addition to the application-wide cache? It can no longer have a local cache via $self->cache because the Application decided for the Command what names it would use within its own object.
No, CLI::Framework::Command defines an interface that includes the set_cache() method, which is used by the Application. If some future command needs a private cache for some reason, there's no reason it could not implement one. Sure, it couldn't use the method names in its parent class, but that's an elementary namespace issue, an intrinsic property of object inheritance.
I believe all of these things are explained in the docs. If not, I'd gladly accept suggestions on how to clarify. I think that the reason for the disconnect is on both ends -- it's tough to explain all these details and it takes patience to read the explanations. C'est la vie.