所以我工作的公司在我们的网站上有一个相当无组织的方法。我们所有的脚本都是带有结尾的程序。我一直想把它组织成一个内部API,其他web开发人员会用它来做任何事情(因为做一个改变让我经历和定位每一个需要更新的其他实例)。
我终于有了一个实例并展示给老板看。它遵循我认为是正常的方法(从我的谷歌搜索)。服务层>网关DAO> bean,带有一些帮助对象创建的工厂。它工作得很好,完全符合我的要求。他对此印象深刻,并同意我们需要修饰我们的代码并更好地组织它,但他没有看到使用这种面向对象的API调用大列表的方法来完成相同的事情的优势。实际上,从他解释结论的方式来看,它的工作方式与方法调用相同。
他问我的方法与这个结论相比有什么优势,对于我来说,除了在一个对象中分组类似的数据之外,我真的找不到任何明显的优势。还有其他的吗,或者更确切地说,采用结论方法会更有利吗?
可读性、可维护性以及对已证明的面向对象范例的遵从性这是使用cfc/对象的真正服务层构建ColdFusion应用程序的最重要的方面,而不是使用大量的include,这充其量是业余的,最坏的情况下可能会导致垃圾收集的噩梦。
假设你有一个名为_queries的include。Cfm,它包含应用程序的所有调用。然后,在员工页面的顶部,在输出所有员工之前,执行以下操作:
<cfinclude template="_queries.cfm" />
<cfoutput query="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,封装为Employees生成查询的业务逻辑。
<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,并希望有机会向他展示你的一些东西,并可能向他学习。
他问你:"那么,应用程序如何访问数据呢?">
"哦,我们在不同的页面上调用了一系列查询,根据参数,我们将提取不同类型的信息。">
"很好",他说,"所以……你有一个数据对象模型的服务层,这很好。"
不是真的,你认为。它只是包含在包含…但他一直在说,
"这太棒了,因为我们将添加的新事物之一是承包者对象,它基本上是雇员的一个子集,他将有一些不同的功能,但总体上将非常像雇员。"我们会继续创建Employee的子类,并重写那些查询。"
…现在你迷路了。因为include中没有子类。include中没有继承。include不知道域或业务对象,也不知道它应该如何与其他对象交互。
include便于重用通用元素,如页眉或页脚。它们不是反映业务对象复杂性的机制。
当您设计/构造/实现cfc作为反映应用程序实体的对象时,您使用的是一种通用语言:OO。这意味着它不是为您提供基于经过验证的结构设计系统的能力,而是将这种"面向对象"的语言扩展到其他技术中的程序员。Java程序员、c++/c#程序员等等……任何有一定面向对象开发知识的人都会自动地说你的语言,并且能够与你和你的系统一起工作。
注意最后一点:不是每个应用程序都需要是面向对象的。如果您的老板希望您快速生成员工表的XML转储并将其放到网站上——是的,您可能会为此放弃整个oo模型。但如果你从头开始构建一个应用程序,它将包含员工、用户、部门、查询、角色、规则、票据……简而言之:在一个领域中的实体,是时候把include作为你重用代码的主要工具了。
哦,还有:我在顶部留下的关于垃圾收集的小纸条——不是开玩笑的。我见过CF应用程序构建不正确,因此应用程序。cfc本身调用cfinincles,并且在将CF连接到可以监视GC中对象的实时创建/销毁的JVM之后,我已经看到内存看起来像一个心电图监视器。
不好。
Shawn的回答绝对涵盖了要点。把所有这些总结成要点,你的老板就会明白……CFC方法将为他在维护方面节省大量资金,并将使他的开发人员更快乐,因为他们的工作将变得更加容易,并且熟练/积极的开发人员的保留率将大大高于他将意大利面条代码作为标准。
我完全同意Shawn的回答…如果你想让你的代码更上一层楼,那就使用框架吧!这样就可以为您和其他开发人员节省大量时间,因为每个人都将遵循自己的标准。
我的个人偏好是Coldbox,但是任何流行的MVC/OO框架都可以——Coldbox、Mach-II、Model-Glue、FW/1。我也听说过CFWheels的优点,但还没有使用过。