0

Dependency injection allows you to avoid referencing objects and scopes that are external to the object directly. You instead reference the injected dependency.

I want to know the best practice in the case of a global function library. I store my functions in the server scope currently. I want my project to be open source soon and easy for other developers to understand. I want the application to be easier to write unit tests for and be more loosely coupled.

Should I inject the server scope into EVERY object that uses a global function?

Or should I make an exception to the rule and put "server.functions.myFunction()" scope directly in the component when referencing these functions?

Here are code examples:

Method 1:

<cfcomponent>
    <cffunction name="init">
        <cfscript>
           server.functions.myFunction();
        </cfscript>
    </cffunction>
</cfcomponent>

Method 2:

<cfcomponent>
    <cffunction name="init">
        <cfargument name="serverScope" type="struct" required="yes">
        <cfscript>
            variables.serverScope=arguments.serverScope;
            variables.serverScope.functions.myFunction();
        </cfscript>
    </cffunction>
</cfcomponent>
Jeromy French
  • 11,812
  • 19
  • 76
  • 129
  • 1
    How many functions in this global library, and what percentage of them are going to be used by each of the components you're gonna inject them into? I'm wary of things like "global function libraries", and would look to decompose them more into purpose-based components, and inject *those* into the components that need them. It's difficult to tell without seeing the functions in the lib, but it sounds like some reorganisation might be something to look at here? – Adam Cameron Nov 02 '12 at 22:50
  • Can I ask why you're using the server scope over the application scope? – NotJustClarkKent Nov 02 '12 at 22:52
  • @NotJustClarkKent I have over 100 sites that use the same functions. I manually synchronize updates to the server scope when uploading new source code. – Bruce Kirkpatrick Nov 02 '12 at 22:56
  • @AdamCameron Examples: email and other validation. Query based generation of select element (and other HTML elements). Custom HTML/javascript interfaces. File/directory i/o, cfhttp request publishing/verification, etc. Almost everything the language has built in is wrapped with a custom version that does more. like instead of cfinsert, I have my own for dynamic queries. I know some of my functions need to become objects if they try to maintain state, but some functions are not meant to be objects. I wouldn't mind putting them into components if that is what the community expects to see. – Bruce Kirkpatrick Nov 02 '12 at 23:06
  • @NotJustClarkKent I should note that my actual framework does put data in both Application and Server scope depending on what is unique to a domain or to the entire server. I put all these variables inside another key to prevent pollution of namespace. For example, Application.zos and Server.zos is where everything goes. – Bruce Kirkpatrick Nov 02 '12 at 23:09
  • Yeah, this is a shortfall of CF not having static methods. Even though these functions of yours would not compose an object in the stateful sense, I'd still be thinking of componentising them as per something like java.lang.Math (for example), which is not used as an object, but a collection of same-themed static methods. That said, you'd then have MORE things to inject around the place, hence I'm not suggesting this sort of thing is an ANSWER to what you're asking. – Adam Cameron Nov 02 '12 at 23:12
  • I'd create a virtual for all the global stuff and just reference them as part of the local structure; but I'm lazy and not building an open source project either. food for thought maybe? – genericHCU Nov 02 '12 at 23:43
  • virtual = mapping, correct? All of my sites are sharing a single mapping currently where 99% of the source code resides. I'm also using railo's compiled archive feature, so there is less I/O then usual. I put things in server scope to avoid using cfinclude/createobject later. @AdamCameron suggests switching from server.functions. to cached server.functions.database component for better organization - I may do that. My goal is to offer an alternative CFML publishing platform & framework like FarCry/Mura. My code is fast, 9 years in development. Trying to make it the best it can be. – Bruce Kirkpatrick Nov 02 '12 at 23:54
  • Do you need a cross-engine solution, or is a Railo-only acceptable? – Peter Boughton Nov 03 '12 at 00:06
  • @PeterBoughton I think you are suggesting to override the built-in tags/functions via the web-inf railo folders. This requires a restart of railo in order for those to update correct? I'd like to have an upgrade process that has zero downtime. Please let me know what you are thinking though. I am currently only concerned with Railo, but that could change over time. – Bruce Kirkpatrick Nov 03 '12 at 00:13
  • I'm trying to establish requirements - you didn't mention Railo _at all_ in the question (or indeed until 8 comments in) but using the compiled archive feature shows that you're already doing engine-specific optimisations. – Peter Boughton Nov 03 '12 at 00:33
  • The custom built-in function treats the files as trusted - I'm not sure if it's possible to reset that cache without a restart, but it might be. – Peter Boughton Nov 03 '12 at 00:33
  • @PeterBoughton http://blog.getrailo.com/post.cfm/railo-3-1-building-your-own-built-in-function - I researched this and found that the railo engine can be restarted independent of the java server in order for the tags/functions to update, but your Railo sessions will become invalid. This Railo feature is nice, but I want my application to be able to update with no restart. – Bruce Kirkpatrick Nov 03 '12 at 01:00
  • Exactly, that entry doesn't say you _must_ restart the engine - it _might_ be possible to clear the cached version without restarting. Is something that would need investigating. As for restarts making sessions invalid, see second section here: http://blog.getrailo.com/post.cfm/railo-3-3-beta-released - sessions can be backed up to a cache and thus "maintained even across engine restarts". – Peter Boughton Nov 03 '12 at 01:52
  • @PeterBoughton I did test the railo context's library/functions folder shortly after you commented and wasn't able to get it to use a new version of the code without restarting Railo. My custom function did work after restart. Storing sessions in the database or disk slows down the application quite a bit, but you are right that this would all work as a complete solution. When I tested the library/function tag, I found it to be no faster then referencing the server scope. A local scope function was also twice as fast in a tight loop. It seems nested structs are bad for performance. – Bruce Kirkpatrick Nov 03 '12 at 03:29

1 Answers1

1

I never got around to formalising my answer to this. based on what you say in the comments, I would do this:

  • decompose your global library into more purpose-specific components, which are then treated as objects (stateful), or - for all intents and purposes static libraries (not stateful).
  • yes, use dependency injection to inject just the ones you need for a given situation.

I would not break encapsulation just for the sake of expedience.

Adam Cameron
  • 29,677
  • 4
  • 37
  • 78