可读性、维护性和对经过验证的面向对象范式的遵守将是使用真正的 CFC / 对象服务层构建 ColdFusion 应用程序的最重要方面,而不是大量的 cfincludes,这充其量是业余的,并且可能导致垃圾最糟糕的收藏噩梦。
可读性
假设您有一个名为 _queries.cfm 的 cfinclude,其中包含对您的应用程序的所有调用。然后,在您的员工页面顶部,就在您输出所有员工之前,您执行以下操作:
<cfinclude template="_queries.cfm" />
<cfoutput query="employeeQry">
employeeQry 是从哪里来的?它是该模板中的查询之一吗?它有什么作用?当我只需要员工时,是否需要包含该模板?如果它有网站中的所有查询...是否都需要每次都包含在内?
为什么不写一些更具可读性的东西,比如:
<cfset employeeQry = request.model.queries.getEmployees() />
<cfoutput query="employeeQry">
啊,我们走了。一目了然,在不了解您系统的细微差别的情况下,我可以立即识别:
- employeeQry 变量的来源
- 我从哪个缓存的 CFC 调用查询
- 我正在调用一个且只有一个查询,而不是大量包括一系列查询,页面不需要这些查询。
将业务逻辑封装在服务层 (CFC) 中可提高代码的可读性,这将在您进入下一个主题时产生影响。
维护
您获得了一个您负责的新 CF 应用程序,并打开员工页面以找到<cfinclude template="_queries.cfm">
上面的模板。
在其中,原始开发人员留下了一条评论,大意是:“我们不要运行所有查询,我们只运行基于参数的特定查询”,然后您会看到如下内容:
<cfswitch case="#param#">
<cfcase value="employee">
<cfinclude template="_employeeQry.cfm">
</cfcase>
<cfcase value="employees">
<cfinclude template="_employeesQry.cfm">
</cfcase>
<cfcase value="employeesByDept">
<cfinclude template="_employeesByDept.cfm">
</cfcase>
</cfswitch>
...所以你看着这个并想,嗯...我需要修改 employeesByDept 查询,所以你打开那个模板并找到:
<!--- employees by department --->
<cfif args.order_by is "ASC">
<cfinclude template="_employeeQryByDeptOnASCOrder.cfm">
<cfelse>
<cfinclude template="_employeeQryByDeptOnDESCOrder.cfm">
</cfif>
......到了这一点,你想朝自己的脸开枪。
这是一个夸张的例子,但在 ColdFusion 世界中再熟悉不过了;构建企业级应用程序时的业余爱好者心态。这种“包含在包含中”的噩梦是 CF 开发人员比您想象的更频繁地处理的事情。
解决方法很简单!
一个单一的 CFC,它封装了为您的员工生成查询的业务逻辑。
<cfcomponent>
<cffunction name="getEmployees" returntype="query">
<cfquery name="tmp">
select employeeID, name, age
from employees
</cfquery>
<cfreturn tmp />
</cffunction>
<cffunction name="getEmployeesByDept" returntype="query">
<cfargument name="deptID">
<cfargument name="order_by" required="false" default="ASC">
<cfquery name="tmp">
select employeeID, name, age
from employees e
inner join empToDept etd on (e.employeeID = etd.employeeID)
where etd.deptID = #arguments.deptID#
order by name #iif(arguments.order_by is 'asc',de('asc'),de('desc'))#
</cfquery>
<cfreturn tmp />
</cffunction>
</cfcomponent>
现在,在查询员工数据库时,您对希望生成的所有信息都有一个单一的参考点,并且可以一次对其进行参数化/调整,而无需在包含中的包含中挖掘大量包含...很麻烦,而且很难保持直截了当(即使有足够的源代码控制)。
它优雅地允许您编写一行:
<cfset empQry = request.model.queries.getEmployees() />
或者
<cfset empQry = request.model.queries.getEmployeesByDept(14,'DESC') />
并使您的代码维护工作变得更加容易。
遵守经过验证的面向对象范式
您的老板宣布有一位 Java 摇滚明星加入了团队。你非常渴望和兴奋地和他坐下来,因为过去几年你主要被困在 CF 中,并希望有机会向他展示你的一些东西,并可能向他学习。
“那么,应用程序如何访问数据?” 他问你。
“哦,我们有一系列的查询,我们在各个页面上调用,根据参数,我们会拉取不同类型的信息。”
“很好”,他说,“所以……你有一个数据对象模型的服务层,这很棒。”
不是真的,你认为。它只是包含在包含中......但他继续前进,
“这太好了,因为我们要添加的新东西之一是 Contractor 对象,它基本上是 Employee 的一个子集,他将拥有一些不同的功能,但总体上会非常像 Employee。我们'将继续并继承 Employee 类,并覆盖其中一些查询......"
...现在你迷路了。因为没有子类包含。包含中没有继承。包含不了解域或业务对象,也不了解它应该如何与其他对象交互。
cfinclude 可以方便地重用常见元素,例如页眉或页脚。它们不是反映业务对象复杂性的机制。
当您将 CFC 设计/构造/实现为反映应用程序实体的对象时,您说的是一种通用语言:OO。这意味着它没有为您提供基于经过验证的结构设计系统的能力,而是将“OO-ness”的语言扩展到其他技术的程序员。Java 程序员、C++/C# 程序员等...任何对面向对象开发有一定了解的人都会自动使用您的语言并能够与您和您的系统一起工作。
请注意最后一点:并非每个应用程序都需要面向对象。如果您的老板希望您快速创建一个员工表的 XML 转储文件并将其放到网站上——是的,您可能会为此放弃整个 oo 模型。但是,如果您要从头开始构建应用程序,并且它将包含员工、用户、部门、查询、角色、规则、票证……简而言之:域中的实体,是时候搁置 cfincludes作为您重用代码的主要工具。
哦,还有 PS:我在顶部留下的关于垃圾收集的小便条——不是开玩笑。我看到 CF 应用程序构建不正确,因此 Application.cfc本身调用 cfincludes,并且在将 CF 连接到可以监控 GC 中对象的实时创建/销毁的 JVM之后,我看到内存看起来像 EKG 监视器.
不好。