2009-03-08 40 views
14

Tôi đang tổ chức lại cấu trúc thư mục ColdFusion của mình và tò mò về cách các nhà phát triển CF có kinh nghiệm tổ chức các thư viện của các cffunctions nhỏ hơn.Làm thế nào để bạn tổ chức các cffunctions tái sử dụng nhỏ của bạn?

Tôi không tò mò về các thành phần phức tạp (đối tượng) như tôi về hàng chục chức năng tiện ích nhỏ mà tất cả chúng ta xây dựng theo thời gian.

  • Bạn có sử dụng tệp lớn duy nhất có cffunctions và cfinclude không?
  • Bạn có sử dụng một tệp đơn lẻ làm thành phần cfcomponent và gọi creatobject/cfinvoke không?
  • Bạn có đặt từng chức năng tiện ích trong cfc của riêng mình và gọi createobject/cfinvoke không?
  • Bạn có sử dụng cú pháp tagimib cfimport không?
  • Bạn có sử dụng CustomTags hoặc cfmodule không?
  • Bạn có cách nào tốt hơn không?

Vì tôi không thích cú pháp chi tiết Tôi đã chỉ cần chụp một lib.cfm có một loạt các cffunctions phổ biến trong đó. Tôi có thể cấu trúc lại chúng để nhóm cfcs tôi có thể createobject trên chỉ để có sự cô lập tốt hơn trên phạm vi biến.

Có cách nào tốt hơn để thực hiện việc này không?

Trả lời

18

Đây là bản in lại của blog post I did vào ngày 13 tháng 6 năm 2007. Tôi đã sử dụng phương pháp này trong một thời gian và nó hoạt động rất tốt! YMMV.

Ai không thích các hàm do người dùng định nghĩa (UDF)? Nếu bạn đã thực hiện bất kỳ chương trình nào, rất có thể là bạn đã sử dụng chúng rộng rãi. Vấn đề lớn nhất mà mọi người gặp phải là cách bao gồm và sắp xếp chúng trong ứng dụng của bạn.

Những gì tôi đã phát hiện ra rằng hầu hết mọi người làm là tạo một Utils.cfc hoặc UDFs.cfc và cắt và dán UDFs của họ mà họ muốn sử dụng vào các thành phần như chứng minh dưới đây:

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

Khi bạn có tất cả các UDF mà ứng dụng của bạn sẽ sử dụng được dán vào thành phần của bạn, bạn sẽ cần phải làm cho các UDF có sẵn cho ứng dụng của bạn. Hầu như tất cả mọi người tôi đã nhìn thấy điều này tải bởi các thành phần vào phạm vi ứng dụng. Dòng sau được đặt vào onApplicationStart() nếu bạn đang sử dụng Application.cfc hoặc bởi chỉ cần thêm nó vào Application.cfm nếu bạn đang sử dụng rằng:

<cfset application.functions = CreateObject("component", "udfs").init()> 

Cho dù bạn đang sử dụng, Application.cfc hoặc Application.cfm, kết quả là như nhau; tất cả UDF của bạn có sẵn cho ứng dụng của bạn và bạn có thể sử dụng chúng một cách tự do trong suốt. Sự khác biệt duy nhất là tên biến mà bạn sử dụng. Tôi sử dụng application.functions, một số application.utils sử dụng hoặc application.udfs; không quan trọng, một lần nữa, kết quả là như nhau.

Có một vấn đề mà tôi có với cách tiếp cận này mặc dù, nó là cồng kềnh và thành phần UDFs sẽ nhận được rất lớn. Vấn đề với việc có một tập tin thành phần lớn như vậy là chỉnh sửa nó trở thành một cơn ác mộng vì việc cuộn qua hàng ngàn dòng mã không phải là rất thú vị và tôi cũng nhận thấy rằng CFEclipse bogs xuống trên các tệp lớn. Sự sụp đổ mã chắc chắn không cung cấp một số cứu trợ nhưng phải có một cách tốt hơn.

Điều tôi muốn là chỉ có một tệp cho mỗi UDF mà tôi đang sử dụng và một cách để ứng dụng của tôi tải chúng tự động. Lý do đằng sau việc này là để nếu tôi cần chỉnh sửa myUDF1, tôi chỉ có thể mở tệp myUDF1.cfm và chỉnh sửa những gì tôi cần. Tôi cũng muốn có thể lấy UDF từ CFLib.org và chỉ cần thả chúng vào ứng dụng của tôi mà không cần phải chỉnh sửa bất kỳ thứ gì. Nếu tôi cần xóa UDF khỏi ứng dụng của mình, nó sẽ dễ dàng như việc xóa tệp UDF và khởi tạo lại ứng dụng của tôi.

Để thực hiện những gì tôi muốn, tôi sửa đổi UDFs.cfc tôi đến 11 dòng mã:

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

Vì vậy, chính xác những gì đang xảy ra?

Tóm lại, đây là những gì đang xảy ra: Tôi có một thư mục có tên là udfs trong cùng thư mục mà tôi có UDFs.cfc của mình. Đây là thư mục mà tôi đặt tất cả các tệp UDF CFM của tôi. Những gì UDFs.cfc làm là quét thư mục này khi nó được gọi và tự động bao gồm mỗi tệp CFM mà nó tìm thấy. Do đó, nó tự động tải bất kỳ UDF nào trong thư mục UDF vào chính nó (thường được gọi là "mixin").

Vì vậy, mục tiêu của tôi đã đạt được! Tôi có mỗi UDF trong tập tin riêng của nó vì vậy tôi không phải di chuyển qua một tập tin thành phần lớn để tìm nó. Bây giờ tôi có thể mở và chỉnh sửa nó một cách dễ dàng. Chỉ cần nhìn vào thư mục, tôi biết UDF đang sử dụng ứng dụng nào. Tôi có thể tự động thêm UDF từ CFLib.org bằng cách chỉ lưu văn bản từ trình duyệt vào một tệp trong thư mục. Plus nếu tôi không còn cần phải sử dụng UDF trong ứng dụng của tôi, tôi chỉ cần xóa các tập tin từ thư mục và nó bị xóa khỏi ứng dụng của tôi trong lần khởi động lại tiếp theo. Tất cả điều này được thực hiện mà không cần phải chạm vào tập tin UDFs.cfc chính.

Dưới đây là ví dụ về một trong những tệp CFF CFM trông như thế nào. Tệp này được gọi là fullLeft.cfm và nằm trong thư mục 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> 
+0

Ý tưởng rất hay, tôi sẽ phải thử nghiệm với cách tiếp cận của bạn. – kevink

+0

Tôi đã làm điều này trong một thời gian dài với kết quả tốt, là tốt. –

+1

Tôi đã thực hiện điều này ngày hôm qua và phải nói rằng nó hoạt động rất tốt. Tôi cũng sử dụng cách tiếp cận để đơn giản hóa các bài kiểm tra đơn vị của mình bằng cách lặp qua thư mục để đảm bảo không có chức năng nào có thể bỏ qua. Rất trơn. – kevink

1

Tôi nghĩ điều này phụ thuộc vào phong cách lập trình của bạn, đi với bất kỳ kiểu nào bạn cảm thấy thoải mái nhất. Tôi thấy cách dễ nhất là trong application.cfm, thiết lập một biến trong phạm vi ứng dụng để một cfcomponent với tất cả các chức năng tiện ích của tôi:

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

Bây giờ bạn có thể gọi các phương thức trong application.utitlies từ bất cứ nơi nào. Lưu ý rằng nếu bạn thực hiện thay đổi đối với cfcomponent của mình, bạn phải làm mới biến ứng dụng của mình bằng một phiên bản Tiện ích mới.

+0

Có lẽ điều này có thể được viết là 'param application.utilities = createObject (" component "," Utilities ");' –

1

nếu bạn đang sử dụng Application.cfc (nếu bạn không phải là tôi sẽ đề nghị chuyển sang nó từ Application.cfm - nó rất dễ dàng để làm), bạn có thể xây dựng một baseComponent.cfc với tất cả các phương pháp UDF của bạn và có Application.cfc kế thừa từ baseComponent. sau đó trong phương thức onRequestStart thiết lập một biến gọi là request.app = this;

cho yêu cầu entiure, sau đó bạn có thể sử dụng request.app.methodname() để truy cập UDF. cách rất đơn giản và đơn giản của nó để xử lý UDFs

cũng có thể, nếu bạn thích bạn có thể có tất cả cfcs của bạn kế thừa từ cùng một baseComponent sao cho tất cả cfcs của bạn có những hàm util như phương thức gốc. làm cho đơn vị kiểm tra cfcs rất dễ dàng bởi vì cfcs không cần phải trả lời trên một tham chiếu (được tiêm) tham chiếu đến thành phần UDf, chúng là những phần tử của nó!

một thách thức với cách tiếp cận này là thuộc tính mở rộng của cfc không thể là biểu thức ... do đó tùy thuộc vào cách bạn gói các thành phần của bạn, điều này có thể khó triển khai. cách dễ nhất để xử lý nó là với ánh xạ coldfusion.

hth Jon

1

Chúng tôi sử dụng file .cfm cho các thư viện chức năng và gọi tập tin phù hợp với cfinclude. Một số tệp .cfm đã được tải xuống từ cflib.org và những tệp khác được viết bởi chúng tôi. Các tệp nằm trong một thư mục có tên UDF là thư mục con của một thư mục khác được ánh xạ tới ký tự dấu gạch chéo chuyển tiếp. Câu lệnh cfinclude chỉ đơn giản là:

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

Cách tiếp cận này làm cho các chức năng có sẵn cho tất cả các ứng dụng trên máy chủ.

Chúng tôi cũng thích phương pháp tiếp cận thư viện nhỏ hơn. Mỗi thư viện là chủ đề cụ thể (toán, chuỗi, mảng danh sách, v.v.)

+0

Bạn có bao nhiêu tệp nhỏ vào cuối ngày? –

0

Tùy chọn: Bạn có sử dụng một tệp đơn lớn với cffunctions và cfinclude không?

A: Tôi đã làm điều đó nhưng tôi làm điều đó ít hơn và ít hơn. Tôi thích tận dụng lợi thế của kế thừa và cfcexplorer

Tùy chọn: Bạn có sử dụng một tệp đơn lẻ làm thành phần cfcomponent và gọi hàm creatobject/cfinvoke không?

A: Có, tôi thường làm điều này

Tùy chọn: Bạn đưa từng cffunction tiện ích trong CFC riêng của mình và gọi CreateObject/cfinvoke?

A: Tôi có thể làm điều này nếu tôi mong đợi các chức năng bổ sung để được thêm vào sau

Tùy chọn: Bạn có sử dụng cú pháp taglib cfimport?

A: Tôi làm i18n thứ mà cách

Tùy chọn: Bạn có sử dụng các CustomTags

A: Không có trong một thời gian dài. cfc's better at this

Tùy chọn: hoặc cfmodule?

A: Không lâu. cfc là tốt hơn lúc này. người gọi. * phạm vi có thể gây khó khăn cho việc gỡ lỗi

0

Tôi nhận thấy đây là một câu hỏi cũ, nhưng tôi sử dụng một cách tiếp cận khác cho những vấn đề này.

Utility Chức năng/Singleton tiếp cận với 'tiêm'

tôi tạo ra một 'lõi' hoặc 'tiện ích' CFC. Trong đó tôi đóng gói tất cả các chức năng loại tiện ích của tôi đó là:

  • Thường được sử dụng tất cả các thời gian ở khắp mọi nơi (chẳng hạn như một viewRecord() dao chung và một chức năng cốt lõi checkSecurity(), et al.)
  • Are chức năng cơ bản mà IMHO nên cốt lõi trong CF (như lpad(), capitalize(), et al)
  • Are giấy gói của một số thẻ cho phép tôi sử dụng cfscript ubiquitously (như exit() mà kết thúc tốt đẹp <cfexit>)

Trên onApplicationStart(), tôi tạo một thể hiện của đối tượng này và gán nó vào phạm vi Application do đó tạo ra một loại đơn tĩnh. Sau đó thay vì mở rộng hoặc bao gồm điều này vào gần như tất cả cfc của tôi, cho phép tôi sử dụng phần mở rộng cho kiểu thừa kế truyền thống hơn, sau đó tôi tiêm các phương thức này vào hàm khởi tạo (của init) của tất cả cfc của tôi. . Tôi làm điều này bằng cách gọi một phương pháp trên đối tượng tiện ích riêng của mình mà:

public/remote any function init() { 
    structAppend(Variables, Application.MyApp.Objects.oCore.injectCoreMethods()); 
    return this; // Return instance of this object 
} 

Phương pháp injectCoreMethods() chọn lọc trả về một cấu trúc của các chức năng tiện ích tôi muốn hầu như mở rộng vào tất cả các đối tượng của tôi. Nó không nhất thiết phải tiêm tất cả các phương thức tiện ích. Những cái ít được sử dụng hơn, bao gồm cả injectCoreMethods() chính nó, vẫn sẽ cần được giải quyết thông qua con trỏ ứng dụng đơn đầy đủ như vậy Application.MyApp.Objects.oCore.infrequentMethod().

Bằng cách tiêm vào phạm vi Variables, được bảo vệ, các phương pháp này sẽ là phương pháp riêng tư hiệu quả. Vì vậy, bất kỳ bãi của các đối tượng sẽ không hiển thị các chức năng tiện ích nhưng hoàn toàn có thể truy cập trong cfc bởi tất cả các phương pháp trực tiếp của nó.

tổ chức File:

Tôi đã thường rơi vào mô hình của việc có một CFC cho mỗi thư mục. Trong mỗi thư mục, tôi có một tệp cfc cho thành phần và init. Tất cả các phương pháp khác tôi chia ra thành các tập tin cfm và incude trong cfc đó. Tôi làm điều này để:

  1. file CFC Tránh khổng lồ của 1000 dòng mà có thể làm chậm IDE của tôi (tôi sử dụng Aptana/cfeclipse)
  2. Cho phép thay đổi được ghi/theo dõi kín đáo hơn trên một tập tin cơ sở cho mỗi tập tin và do đó được ghi lại trong phần mềm điều khiển SCM/Phiên bản của tôi.
  3. Cho phép nhiều người làm việc trên một đối tượng nhất định có mã chạm vào nhau.

Vì vậy, một đối tượng dao, trong đó có 4 phương pháp crud sẽ giống như thế này:

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

CFC chỉ chứa init() và ý kiến ​​tự lập hồ sơ và trong khu vực pseudo-constructor tôi bao gồm bốn phương pháp. Điều này cũng cho phép tôi lấy bất kỳ cfc nào bằng thư mục của nó và di chuyển nó ở đâu đó.

Tương tự cho tiện ích cfc. Nó nằm trong thư mục riêng của nó và có khoảng 30 hàm lẻ trong số 10 tệp cfm (một số hàm đơn giản mà tôi để trong cùng một tệp chẳng hạn như _string.cfm thực sự chứa lpad(), rpad(), v.v ... liên quan đến chuỗi.)

Modules và Custom Thẻ

Tôi tránh những bằng mọi giá bởi vì họ cần phải được đăng ký và cản trở di chuyển dễ dàng/triển khai. Tôi không thích những thứ không chỉ tự cấu hình khi kéo và thả từ môi trường này sang môi trường khác. CF5- bạn phải làm mọi thứ theo cách này nhiều hơn. Nhưng kể từ CF6 và khả năng sử dụng các đối tượng trong một mô hình OOP thực, tại sao bạn muốn? Có rất ít trường hợp bạn muốn/cần nếu có.

khác

tôi sử dụng để đưa chức năng 'lõi' vào base.cfc được tự động gia hạn vào tất cả của CFC được tạo ra bởi CF (tìm kiếm nó, thêm một chức năng và thì đấy! Kinda như thêm điều cần nguyên mẫu trong js). Tôi từng thực sự thích điều này nhưng nó là một vấn đề cho việc triển khai/bảo trì.

Ở một mức độ nào đó, tôi có phương pháp tiếp cận Nhà máy. Tôi thường đặt một số tiền hợp lý của tĩnh cfc lên trong các ứng dụng như một trong những lõi. Một bộ điều khiển đọc một bảng điều khiển chung và thiết lập tất cả các đối tượng trong một vòng lặp cùng với một loạt các thứ khác trên ứng dụng bắt đầu như biến ứng dụng. Nhưng một số đối tượng được khởi tạo khi cần thiết, các đối tượng và vật thể rõ ràng nặng có chứa dữ liệu cố định [bán] có thể thao tác được vào danh mục đó

Tôi đã thực hiện điều này từ CF7. Với CF9 + nó trở nên khá dễ dàng, trưởng thành và khéo léo.

Các vấn đề liên quan