15

我正在重新组织我的 ColdFusion 目录结构,并对经验丰富的 CF 开发人员如何组织较小的 cffunctions 库感到好奇。

我对复杂的组件(对象)并不像对随着时间的推移而构建的几十个小实用函数那样好奇。

  • 您是否使用带有 cffunctions 和 cfinclude 的大型单个文件?
  • 您是否使用大型单个文件作为 cfcomponent 并调用 creatobject/cfinvoke?
  • 您是否将每个实用程序 cffunction 放在其自己的 cfc 中并调用 createobject/cfinvoke?
  • 你使用 cfimport taglib 语法吗?
  • 您使用 CustomTags 还是 cfmodule?
  • 你有更好的方法吗?

因为我不喜欢冗长的语法,所以我只是 cfinclude 一个 lib.cfm,其中有一堆常见的 cffunctions。我可以将它们重构为可以在其上创建对象的分组 cfcs,以便更好地隔离变量范围。

有一个更好的方法吗?

4

6 回答 6

20

这是我在 2007 年 6 月 13 日发表的一篇博文的重印。我使用这种方法已经有一段时间了,效果很好!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,则将以下行放入,onApplicationStart()或者如果您正在使用它,只需将其添加到 Application.cfm 中:

<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>

那么究竟发生了什么?

简而言之,这就是正在发生的事情:我有一个名为udfsUDFs.cfc 的目录中的目录。这是我放置所有 UDF CFM 文件的目录。UDFs.cfc 所做的是在调用它时扫描此目录并自动包含它找到的每个 CFM 文件。因此,它会自动将 UDFs 文件夹中的所有 UDF 加载到自身中(通常称为“mixin”)。

所以我的目标达到了!我在自己的文件中拥有每个 UDF,因此我不必滚动浏览一个巨大的组件文件来找到它。我现在可以轻松地打开和编辑它。通过查看目录,我知道我的应用程序正在使用哪些 UDF。我只需将浏览器中的文本保存到目录中的文件中,就可以自动从 CFLib.org 添加 UDF。另外,如果我不再需要在我的应用程序中使用 UDF,我只需从目录中删除该文件,并在下一次重新初始化期间将其从我的应用程序中删除。所有这些都无需接触主 UDFs.cfc 文件即可完成。

下面是一个 UDF CFM 文件的外观示例。该文件被调用fullLeft.cfm并位于 UDFs 目录中。

<!--- 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>
于 2009-03-09T02:02:01.680 回答
1

我认为这取决于你的编程风格,选择你最喜欢的风格。我发现最简单的方法是在 application.cfm 中,将应用程序范围内的变量设置为具有我所有实用程序功能的 cfcomponent:

<cfif not isDefined("application.utilities")>
    <cfset application.utilities = createObject("component", "Utilities")>
</cfif>

现在您可以从任何地方调用 application.utils 中的方法。请注意,如果您对 cfcomponent 进行更改,则必须使用新的 Utilities 实例刷新应用程序变量。

于 2009-03-09T01:17:35.843 回答
1

如果您使用的是 Application.cfc(如果不是,我强烈建议您从 Application.cfm 迁移到它 - 这很容易做到)您可以使用您的所有 UDF 方法构建一个 baseComponent.cfc 并让 Application.cfc 继承自基础组件。然后在 onRequestStart 方法中设置一个名为 request.app=this 的变量;

对于整个请求,您可以使用 request.app.methodname() 访问 UDF。它处理UDF的非常好和简单的方法

此外,如果您愿意,可以让所有 cfcs 从同一个 baseComponent 继承,以便所有 cfcs 都将这些 util 函数作为本机方法。使单元测试 cfcs 变得非常容易,因为 cfcs 不需要回复对 UDf 组件的传递(注入)引用,它们是它的后代!

这种方法的一个挑战是 cfc 的 extends 属性不能是表达式……因此,根据您打包组件的方式,这可能很难实现。处理它的最简单方法是使用冷融合映射。

乔恩

于 2009-03-27T21:50:10.053 回答
1

我们将 .cfm 文件用于函数库,并使用 cfinclude 调用相应的文件。一些 .cfm 文件是从 cflib.org 下载的,其他文件是我们编写的。这些文件位于名为 UDF 的目录中,该目录是映射到正斜杠字符的另一个目录的子目录。cfinclude 语句很简单:

<cfinclude template="/UDF/filename.cfm">

这种方法使服务器上的所有应用程序都可以使用这些功能。

我们也更喜欢几个小型库的方法。每个库都是特定于主题的(数学、字符串、列表数组等)

于 2012-11-18T14:55:33.167 回答
0

选项:您是否使用带有 cffunctions 和 cfinclude 的大型单个文件?

A:我做过,但我做的越来越少。我喜欢利用继承和 cfcexplorer

选项:您是否使用大型单个文件作为 cfcomponent 并调用 creatobject/cfinvoke?

A:是的,我经常这样做

选项:您是否将每个实用程序 cffunction 放在其自己的 cfc 中并调用 createobject/cfinvoke?

A:如果我希望以后添加其他功能,我可能会这样做

选项:您使用 cfimport taglib 语法吗?

A: 我就是这样做 i18n 的

选项:您是否使用自定义标签

答:很久没有了。cfc 在这方面做得更好

选项:或cfmodule?

答:很久没有了。cfc 在这方面做得更好。caller.* 范围会使调试变得困难

于 2012-11-27T04:21:25.287 回答
0

我意识到这是一个老问题,但我对这些问题使用了一些不同的方法。

具有“注入”的实用函数/单例方法

我创建了一个“核心”或“实用程序”cfc。在其中我打包了我所有的实用程序类型函数,它们是:

  • 在任何地方都经常使用(例如通用viewRecord()dao 和核心checkSecurity()函数等)
  • 恕我直言应该是 CF 核心的基本功能(例如lpad(), capitalize(), et al)
  • 是一些允许我cfscript普遍使用的标签的包装器(例如exit()which wraps <cfexit>

在 上onApplicationStart(),我创建了这个对象的一个​​实例并将其分配给Application范围,从而创建了一个静态单例。

然后,我没有将它扩展或重新包含到我几乎所有的 cfc 中,这允许我将扩展用于更传统的继承类型,然后我将这些方法注入到我构建的所有 cfc 的构造函数(init)中。我通过调用实用程序对象本身的方法来做到这一点,这样:

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().

通过注入Variables受保护的作用域,这些方法将有效地成为私有方法。因此,任何对象转储都不会显示这些实用程序功能,但可以通过其所有直接方法在 cfc 中完全访问。

文件组织:

我通常陷入每个文件夹一个 cfc 的模式。在每个文件夹中,我都有一个用于组件和 init 的 cfc 文件。我将所有其他方法分解为 cfm 文件并包含在该 cfc 中。我这样做是为了:

  1. 避免超过 1000 行的巨型 cfc 文件,这会降低我的 IDE 速度(我使用 aptana/cfeclipse)
  2. 允许在每个文件的基础上更离散地记录/跟踪更改,从而记录在我的 SCM/版本控制软件中。
  3. 允许多人在给定对象上工作,而不会相互碰撞代码。

所以一个包含 4 个 crud 方法的 dao 对象看起来像这样:

/code/dao/dao.cfc
/code/dao/_removeRecord.cfm
/code/dao/_addRecord.cfm
/code/dao/_viewRecord.cfm
/code/dao/_editRecord.cfm

cfc 只包含init()和自文档注释,在伪构造器区域中,我包含了四种方法。这也让我可以通过文件夹抓取任何 cfc 并将其移动到某处。

实用程序 cfc 也是如此。它位于自己的文件夹中,在 10 个左右的 cfm 文件中具有大约 30 个奇怪的函数(我将一些简单的函数留在同一个文件中,例如_string.cfm实际上包含lpad(),rpad()等所有字符串相关的。你明白了。)

模块和自定义标签

我不惜一切代价避免这些,因为它们需要注册并妨碍轻松移动/部署。我不喜欢不只是在从一个环境拖放到另一个环境时自行配置的东西。CF5-您必须以这种方式做更多的事情。但是既然 CF6 和在真正的 OOP 模式中使用对象的能力,你为什么要这样做呢?如果有的话,您想要/需要的情况很少。

其他

我曾经将“核心”函数放入 base.cfc 中,该函数会自动扩展到 CF 生成的所有 cfc 中(查找它,添加一个函数,瞧!有点像在 js 中向原型添加东西)。我曾经非常喜欢这个,但这是部署/维护的问题。

在某种程度上,我采用工厂方法。我经常在应用程序中放置相当数量的静态cfc,就像核心一样。控制器读取通用控制表并在循环中设置所有对象以及应用程序启动时的一堆其他内容,例如应用程序变量。但是有些对象是根据需要实例化的,显然重的对象和包含可操作[半]持久数据的对象属于该类别

在某些方面,我从 CF7 开始就一直在这样做。使用 CF9+,它变得非常简单、成熟和流畅。

于 2013-06-06T01:43:39.680 回答