我正在重新组织我的ColdFusion目录结构,并对有经验的CF开发人员如何组织较小的cffunction库感到好奇。
我对精心制作的组件(对象)并不感到好奇,因为我对随着时间推移而建立起来的几十个小实用函数感到好奇。
由于我不喜欢详细的语法,所以我只是将包含一堆常见cffunction的lib.cfm包含在内。我可以将它们重构为cfcs,我可以创建对象,以便在变量范围上有更好的隔离。
有更好的方法吗?
答案 0 :(得分:19)
这是2007年6月13日blog post I did的转载。我已经使用这种方法很长一段时间了,效果很好! YMMV。
谁不喜欢用户定义的函数(UDF)?如果您已经完成了任何编程,那么您可能已经广泛使用它们。人们对他们最大的问题是如何在你的应用程序中包含和组织它们。
我发现大多数人都会创建一个Utils.cfc或UDFs.cfc,并将他们想要使用的UDF剪切并粘贴到组件中,如下所示:
<!--- UDFs.cfc --->
<cfcomponent output="false">
<cffunction name="init" access="public” returntype="Any" output="false">
<cfreturn this>
</cffunction>
<cffunction name="myUDF1" access="public" returntype="Any" output="false">
</cffunction>
<cffunction name="myUDF2" access="public" returntype="Any" output="false">
</cffunction>
</cfcomponent>
一旦您将应用程序将使用的所有UDF粘贴到组件中,您将需要使UDF可用于您的应用程序。我见过的几乎所有人都将组件加载到应用程序范围中。如果您正在使用Application.cfc,或者只是在使用它时将其添加到Application.cfm中,则会将以下行放入onApplicationStart()
:
<cfset application.functions = CreateObject("component", "udfs").init()>
无论您使用的是Application.cfc还是Application.cfm,结果都是一样的;您的所有UDF都可用于您的应用程序,您可以在整个过程中自由使用它们。唯一的区别是您使用的变量名称。我使用application.functions,有些使用application.utils或application.udfs;无所谓,结果是一样的。
虽然这种方法存在一个问题,但它很麻烦且UDF组件会变得很大。拥有如此巨大的组件文件的问题是编辑它变成了一场噩梦,因为滚动数千行代码并不是很有趣,而且我也注意到CFEclipse陷入了巨大的文件。确实代码崩溃确实提供了一些缓解,但必须有更好的方法。
我想要的是为我正在使用的每个UDF创建一个文件,以及我的应用程序自动加载它们的方法。这背后的原因是,如果我需要编辑myUDF1
,我可以打开文件myUDF1.cfm
并编辑我需要的内容。我还希望能够从CFLib.org中获取UDF,只需将它们放入我的应用程序中,而无需编辑任何内容。如果我需要从我的应用程序中删除UDF,那就像删除UDF文件并重新初始化我的应用程序一样简单。
为了实现我想要的目标,我将UDFs.cfc修改为11行代码:
<!--- UDFs.cfc --->
<cfcomponent output="false">
<cfset variables.udfdir = GetDirectoryFromPath(GetCurrentTemplatePath()) & "udfs">
<cfset variables.q = "">
<cffunction name="init" access="public" returntype="Any" output="false">
<cfreturn this>
</cffunction>
<cfdirectory action="list" directory="#variables.udfdir#" filter="*.cfm" name="variables.q">
<cfoutput query="variables.q">
<cfinclude template="udfs\#name#">
</cfoutput>
</cfcomponent>
究竟是怎么回事?
简而言之,这就是发生的事情:我在UDFs.cfc的同一目录中有一个名为udfs
的目录。这是我放置所有UDF CFM文件的目录。 UDFs.cfc所做的是在调用该目录时扫描该目录,并自动包含它找到的每个CFM文件。因此,它会自动将UDFs文件夹中的任何UDF加载到自身中(通常称为“mixin”)。
所以我的目标已达成!我有自己的文件中的每个UDF,所以我不必滚动浏览一个巨大的组件文件来找到它。我现在可以轻松打开和编辑它。只需查看目录,我就知道我的应用程序正在使用哪些UDF。我可以通过将浏览器中的文本保存到目录中的文件中,自动从CFLib.org添加UDF。此外,如果我不再需要在我的应用程序中使用UDF,我只需从目录中删除该文件,并在下次重新初始化时将其从我的应用程序中删除。所有这一切都是在不必触及主UDFs.cfc文件的情况下完成的。
下面是一个UDF CFM文件的示例。该文件名为fullLeft.cfm
,位于UDF目录中。
<!--- fullLeft --->
<cffunction name="fullLeft" access="public" displayname="fullLeft" returntype="string" output="false">
<cfargument name="str" type="string" required="true">
<cfargument name="count" type="numeric" required="true">
<cfif not refind("[[:space:]]", arguments.str) or (arguments.count gte len(arguments.str))>
<cfreturn Left(arguments.str, arguments.count)>
<cfelseif reFind("[[:space:]]",mid(arguments.str,arguments.count+1,1))>
<cfreturn left(arguments.str,arguments.count)>
<cfelse>
<cfif count-refind("[[:space:]]", reverse(mid(arguments.str,1,arguments.count)))>
<cfreturn Left(arguments.str, (arguments.count-refind("[[:space:]]", reverse(mid(str,1,arguments.count)))))>
<cfelse>
<cfreturn left(arguments.str,1)>
</cfif>
</cfif>
</cffunction>
答案 1 :(得分:1)
我认为这取决于您的编程风格,请选择您最熟悉的风格。我发现最简单的方法是在application.cfm中,在应用程序范围内将变量设置为包含所有实用程序函数的cfcomponent:
<cfif not isDefined("application.utilities")>
<cfset application.utilities = createObject("component", "Utilities")>
</cfif>
现在,您可以从任何地方调用application.utitlies中的方法。请注意,如果您对cfcomponent进行了更改,则必须使用新的Utilities实例刷新应用程序变量。
答案 2 :(得分:1)
如果您正在使用Application.cfc(如果您不是我强烈建议从Application.cfm迁移到它 - 它非常容易),您可以使用所有UDF方法构建baseComponent.cfc并拥有Application。 cfc继承自baseComponent。然后在onRequestStart方法中设置一个名为request.app = this;
的变量对于entiure请求,您可以使用request.app.methodname()来访问UDF。它非常简单的处理UDF的方式
另外,如果您愿意,可以让所有cfcs继承自同一个baseComponent,以便所有cfcs都将这些util函数作为本机方法。使单元测试cfcs非常简单,因为cfcs不需要回复对UDf组件的传递(注入)引用,它们是它的后代!
这种方法的一个挑战是cfc的extends属性不能是表达式......因此,根据您打包组件的方式,这可能很难实现。处理它的最简单方法是使用coldfusion映射。
HTH 乔恩
答案 3 :(得分:1)
我们使用.cfm文件作为函数库,并使用cfinclude调用相应的文件。一些.cfm文件已从cflib.org下载,其他文件由我们编写。这些文件位于名为UDF的目录中,该目录是另一个目录的子目录,该目录映射到正斜杠字符。 cfinclude语句只是:
<cfinclude template="/UDF/filename.cfm">
此方法使函数可用于服务器上的所有应用程序。
我们也更喜欢几种小型库方法。每个库都是特定于主题的(数学,字符串,列表数组等)
答案 4 :(得分:0)
选项:你是否使用带有cffunctions的大型单个文件并将其包括在内?
答:我已经做到了,但我做得越来越少。我喜欢利用继承和cfcexplorer选项:您是使用大型单个文件作为cfcomponent并调用creatobject / cfinvoke吗?
答:是的,我经常这样做
选项:您是否将每个实用程序函数放在其自己的cfc中并调用createobject / cfinvoke?
答:如果我希望以后添加其他功能,我可能会这样做
选项:您是否使用cfimport taglib语法?
答:我这样做的事情
选项:您是否使用CustomTags
答:不是很长时间。 cfc在这方面更好
选项:或cfmodule?
答:不是很长时间。 cfc在这方面做得更好。调用者。*范围可能使调试变得困难
答案 5 :(得分:0)
我意识到这是一个老问题,但我对这些问题使用了一些不同的方法。
实用功能/单一方法与'注射'
我创建了一个'核心'或'实用程序'cfc。在其中我打包了所有的实用程序类型函数:
viewRecord()
dao和核心checkSecurity()
函数等)lpad()
,capitalize()
等)cfscript
的某些标签包装(例如包裹exit()
的{{1}})在<cfexit>
上,我创建了这个对象的实例,并将其分配给onApplicationStart()
范围,从而创建了一个静态的单例。
然后,我不再将其扩展或重新包含在几乎所有的cfc中,这允许我使用扩展来实现更传统的继承类型,然后我将这些方法注入我构建的所有cfc的构造函数(init)中。我这样做是通过调用实用程序对象本身的方法来实现的:
Application
public/remote any function init() {
structAppend( Variables, Application.MyApp.Objects.oCore.injectCoreMethods() );
return this; // Return instance of this object
}
方法选择性地返回我想要虚拟扩展到所有对象的实用函数的结构。它不一定会注入所有实用程序方法。包括injectCoreMethods()
本身在内的不常使用的仍然需要通过完整的单例应用程序指针来解决injectCoreMethods()
。
通过注入受保护的Application.MyApp.Objects.oCore.infrequentMethod()
范围,这些方法实际上将是私有方法。因此,对象的任何转储都不会显示这些实用程序函数,但可以通过其所有直接方法在cfc中完全访问。
文件组织:
我一般都陷入了每个文件夹只有一个cfc的模式。在每个文件夹中,我有一个cfc文件用于组件和init。我打破cfm文件的所有其他方法,并在cfc中包含。我这样做:
所以包含4个crud方法的dao对象看起来像这样:
Variables
cfc只包含/code/dao/dao.cfc
/code/dao/_removeRecord.cfm
/code/dao/_addRecord.cfm
/code/dao/_viewRecord.cfm
/code/dao/_editRecord.cfm
和自我记录的注释,在伪构造函数区域中我包含了四种方法。这也让我可以通过它的文件夹抓取任何cfc并将其移动到某处。
实用程序cfc相同。它位于自己的文件夹中,在10个左右的cfm文件中有大约30个奇函数(我留在同一个文件中的一些简单函数,例如init()
实际上包含_string.cfm
,lpad()
,所有字符串相关。你明白了。)
模块和自定义标签
我不惜一切代价避免这些因为它们需要注册并妨碍轻松移动/部署。我不喜欢那些不仅仅是从一个环境拖放到另一个环境的自我配置的东西。 CF5-你必须以这种方式做更多的事情。但是由于CF6和在真实OOP模式中使用对象的能力,你为什么要这样做?如果有的话,你很少需要/需要。
其他
我曾经把'核心'函数放到base.cfc中,它会自动扩展到CF生成的所有cfc中(查找它,添加一个函数和瞧!有点像在js中添加东西到原型)。我曾经非常喜欢这个,但这是部署/维护的问题。
在某种程度上,我采取了工厂方法。我经常在应用程序中放入相当数量的静态cfc,就像核心一样。控制器读取一个通用控制表,并在app循环中设置循环中的所有对象以及应用程序变量上的一堆其他内容。但是根据需要实例化一些对象,显然重型对象和包含可操作的[半]持久性数据的对象属于该类别
从某些方面来说,自从CF7以来,我一直在这样做。使用CF9 +,它变得非常简单,成熟和光滑。