My FAQ,最新最全的IT技术FAQ
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 未整理篇 | 技术讨论
  当前位置: > 程序开发 > 编程语言 > Java > 数据库
ColdFusion Components and Data Abstraction @ JDJ
作者:未知 时间:2005-08-10 22:53 出处:Java频道 责编:My FAQ
              摘要:ColdFusion Components and Data Abstraction @ JDJ

I've been discussing ColdFusion Components on and off since we first introduced them - which was in ColdFusion MX - and even dedicated my entire keynote time slot at our 2002 conference to CFCs, as well as covering them extensively in prior CFDJ columns. But, apparently, many users have yet to take advantage of these important application building blocks, or have failed to fully appreciate their necessity. So I am going to dedicate my next two columns to explaining the most important aspect of ColdFusion Components - data abstraction.

The Separation of Presentation from Content
The best way to understand data abstraction and the resulting need for CFCs is to look at an example:

<!--- Get employees --->
<CFQUERY NAME="employees"
    DATASOURCE="CompanyInfo">
SELECT Emp_ID, LastName, FirstName
FROM Employee
ORDER BY LastName, FirstName
</CFQUERY>

<!--- Display list --->
<UL>
  <CFOUTPUT QUERY="employees">
   <LI>#LastName#, #FirstName#</LI>
  </CFOUTPUT>
</UL>

This is really simple code. You've seen code like this, and have likely written something much like it many times. It uses <CFQUERY> to retrieve an employee list from a database table, and then displays the names in an HTML list. Simple enough.

So, what is wrong with this code? It is commented, uses case consistently, is indented, doesn't have extraneous # characters, and, best of all, it works! So, is there really anything wrong with it?

Well, maybe not. After all, if the code works, then there's nothing wrong with it. The fact of the matter is that applications are written to do a specific job, and if that job gets done, then the application is doing exactly what it was intended to do. That's not wrong. If it works, there is no wrong per se.

But having said that, there is no right either. There are lots of ways to write applications, with pros and cons to each (usually). And the code above has one big con going against it. Consider what would happen to your code if the table column names were changed to NameLast and NameFirst. Or if the table schema changed. Or of the user list was to be pulled from Active Directory instead of a database.

These are not far-fetched scenarios, back ends do change all the time. And when this happens, what will happen to your code? Obviously the above SQL statement will break, and will need to be changed to reflect the new table information. That will likely be a tedious but not impossible task. You'll need to find all <CFQUERY> tags (a simple site-wide search will find those) and make the code changes. A pain, but doable.

But that won't be enough. Look at the code again. The <CFOUTPUT> loop is referring to column names explicitly, and so those will break too. Now the change has gotten a bit more complicated.

What if your database query was returning columns used in <CFIF> or <CFSET> statements, values used in calculations, or flags that somehow affected data presentation? Think about it.

How many <CFQUERY> tags are used throughout your code? And how many columns are referred to within CFML code outside of <CFQUERY> tags? How much of your code will break when back-end changes occur?

It's a scary thought, but it's an important one. The truth is that database changes should never be able to break HTML formatting, that makes no sense at all. And if table schemas do change, that should not be able to throw errors in client-generated output. Even if a database is replaced with directory services, employee lists should still be rendered the same way.

I have explained this problem in detail previously (see CFDJ, Vol. 4, issue 10), so I'm not going to dwell on it here. Rather, I'll concentrate on how CFCs help overcome this issue.

CFCs to the Rescue
The problem described above is one that ColdFusion Components (CFCs) were designed to solve. CFCs are special files (they have a .cfc extension, instead of .cfm) designed to provide basic object functionality to ColdFusion developers.

Don't let the word "object" scare you; CFCs are as easy to use as the rest of ColdFusion. They are plain text files made up of tags, and they are called (invoked, to be precise) from other .cfm pages.

Here's a modified version of the previously seen code:

<!--- Get employees --->
<CFINVOKE COMPONENT="emps"
     METHOD="list"
     RETURNVARIABLE="employees">

<!--- Display list --->
<UL>
  <CFOUTPUT QUERY="employees">
   <LI>#LastName#, #FirstName#</LI>
  </CFOUTPUT>
</UL>

A tag named <CFINVOKE> invokes a component named emps (the actual file name would be emps.cfc, in the current directory), and calls a method (function) named list that returns a query named employees, which is used just like the query in the first example.

So where is the actual <CFQUERY> tag? It is in the emps component.

Basic CFC Abstraction
Now you obviously want to see what component emps.cfc looks like. Here's the code:

<CFCOMPONENT>

<!--- List employees --->
<CFFUNCTION NAME="List"
     RETURNTYPE="query"
     OUTPUT="false">
  <!--- Get data --->
  <CFQUERY NAME="employees"
     DATASOURCE="CompanyInfo">
  SELECT Emp_ID, LastName, FirstName
  FROM Employee
  ORDER BY LastName, FirstName
  </CFQUERY>
  <!--- Return data --->
  <CFRETURN employees>
</CFFUNCTION>

</CFCOMPONENT>

Everything in a .cfc file is enclosed within <CFCOMPONENT> and </CFCOMPONENT> tags. These define the component itself. The component contains one or more methods (functions), each defined with a set of <CFFUNCTION> and </CFFUNCTION> tags.

The method contains the exact same <CFQUERY> as used before. But this time a <CFRETURN> tag returns that query to the calling page.

So, in order:

  1. Code in a .cfm page uses a <CFINVOKE> call to run the list method in a component named emps.cfc.
  2. The list method contains a <CFQUERY> that obtains data from a database.
  3. list returns the query to the calling .cfm page.
  4. A <CFOUTPUT> loop in the .cfm page displays the query contents as it would any other query.
It's that simple.

The Value of Data Abstraction
Was it worth it? All we have done is add an extra file and more lines of code. Have we gained anything?

Let's go back to the scenario where something changed in the table layout. Perhaps the table columns LastName and FirstName are now named NameLast and NameFirst, respectively. You'd obviously need to change your SQL; there is no real way around that. Your updated .cfc code might look like this:

<CFCOMPONENT>

<!--- List employees --->
<CFFUNCTION NAME="List"
     RETURNTYPE="query"
     OUTPUT="false">
  <!--- Get data --->
  <CFQUERY NAME="employees"
     DATASOURCE="CompanyInfo">
  SELECT Emp_ID, NameLast AS LastName,
     NameFirst AS FirstName
  FROM Employee
  ORDER BY LastName, FirstName
  </CFQUERY>
  <!--- Return data --->
<CFRETURN employees>
</CFFUNCTION>

</CFCOMPONENT>

Notice that the SELECT statement contains aliases (defined using the SQL AS keyword) that rename NameLast to LastName and NameFirst to FirstName. The database changed, and so the SQL had to change accordingly. And what about the presentation code? It can stay the same, as far as it is concerned nothing changed; it'll continue to work as it did before.

Even in an example as simple as this one, the benefits start to become apparent very quickly indeed.

Not Just for Data Retrieval
Most articles and tutorials on using CFCs use examples involving data retrieval, but that isn't all CFCs are for. All data access should be encapsulated within CFCs, including inserts, updates, and deletes. Here's a simple update method, used to update the name of an employee. This would be placed inside of the same emps.cfc file:

<!--- Update an employee --->
<CFFUNCTION NAME="Update"
     RETURNTYPE="boolean"
     OUTPUT="false">
  <!--- Arguments --->
<CFARGUMENT NAME="Emp_ID"
     TYPE="numeric"
     REQUIRED="yes">
  <CFARGUMENT NAME="LastName"
     TYPE="string"
     REQUIRED="yes">
  <CFARGUMENT NAME="FirstName"
     TYPE="string"
     REQUIRED="yes">

<!--- The update --->
<CFQUERY DATASOURCE="CompanyInfo">
UPDATE Employee
SET LastName = '#ARGUMENTS.LastName#',
   FirstName = '#ARGUMENTS.FirstName#'
WHERE Emp_ID = #ARGUMENTS.Emp_ID#
</CFQUERY>
<CFRETURN true>
</CFFUNCTION>

Now, to update an employee, .cfm code would simply need to invoke this new method and pass the required arguments (parameters) to it. One way to do this is by adding name=value pairs to the <CFINVOKE>, like this:

<CFINVOKE COMPONENT="emps"
   METHOD="update"
   EMP_ID="22"
   LASTNAME="Jones"
   FIRSTNAME="Bobby">

Another option is to use <CFINVOKEARGUMENT> tags, like this:

<CFINVOKE COMPONENT="emps"
   METHOD="update">
<CFINVOKEARGUMENT NAME="Emp_ID"
   VALUE="22">
<CFINVOKEARGUMENT NAME="LastName"
   VALUE="Jones">
<CFINVOKEARGUMENT NAME="FirstName"
   VALUE="Bobby">
</CFINVOKE>

Either way, .cfm code need simply pass values, and not care about what the update operation does internally.

It turns out that there is a right way to do things after all.

Conclusion
ColdFusion Components, first introduced in ColdFusion MX, provide the fundamental building blocks used to design applications the right way. In this column we looked at basic data abstraction, separating data (and data integration) from presentation. We'll continue this discussion next month when we look at encapsulating more than just data processing. Stay tuned!

 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 myfaq.com.cn All rights reserved. www.myfaq.com.cn 版权所有