2009-03-08 23 views
14

Estoy reorganizando mis estructuras de directorios de ColdFusion y tengo curiosidad sobre cómo los desarrolladores experimentados de CF están organizando bibliotecas de funciones más pequeñas.¿Cómo se organizan sus pequeños cofres reutilizables?

No tengo tanta curiosidad sobre los componentes elaborados (objetos) como sobre las docenas de pequeñas funciones de utilidad que todos construimos a lo largo del tiempo.

  • ¿Utiliza un solo archivo grande con funciones y cfinclude?
  • ¿Utiliza un único archivo grande como un componente cf y llama a creatobject/cfinvoke?
  • ¿Pones cada función de función en su propio cfc y llamas createobject/cfinvoke?
  • ¿Utiliza la sintaxis de cflport taglib?
  • ¿Utiliza CustomTags o cfmodule?
  • ¿Tienes una mejor manera?

Como no me gusta la sintaxis detallada, he estado simplemente incluyendo un cf.cfm que tiene un montón de funciones comunes en él. Puedo refactorizarlos a cfcs agrupados en los que puedo crearobjeto solo para tener un mejor aislamiento en los ámbitos de variable.

¿Hay una mejor manera de hacerlo?

Respuesta

18

Esta es una reimpresión de blog post I did anterior al 13 de junio de 2007. ¡He estado utilizando este método durante bastante tiempo y funciona de maravilla! YMMV.

¿A quién no le gustan las funciones definidas por el usuario (UDF)? Si ha realizado alguna programación, es probable que la haya utilizado extensamente. El mayor problema que las personas tienen con ellos es cómo incluirlos y organizarlos en su aplicación.

Lo que he encontrado que la mayoría de la gente hace es crear un Utils.cfc o UDFs.cfc y cortar y pegar sus UDF que quieren utilizar en el componente como se demuestra a continuación:

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

Una vez que tener todas las UDF que su aplicación usará pegadas en su componente, deberá hacer que las UDF estén disponibles para su aplicación. Casi todos los que he visto lo cargan por el componente en el alcance de la aplicación. La siguiente línea se coloca en el onApplicationStart() si está usando Application.cfc o con sólo añadir que en el Application.cfm si se está utilizando lo siguiente:

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

Cualquiera que usted está usando, Application.cfc o Application.cfm, los resultados son los mismos; todas sus UDF están disponibles para su aplicación y puede usarlas libremente en todas partes. La única diferencia es qué nombre de variable usa. Uso las funciones de aplicación, algunas usan application.utils o application.udfs; no importa, una vez más, los resultados son los mismos.

Hay un problema que tengo con este enfoque, sin embargo, es engorroso y el componente UDFs se volverá enorme. El problema de tener un archivo de componentes tan grande es la edición, se convierte en una pesadilla ya que desplazarse por miles de líneas de código no es muy divertido y también me he dado cuenta de que CFEclipse se atasca en archivos de gran tamaño. El colapso del código seguro proporciona cierto alivio, pero tiene que haber una mejor manera.

Lo que quería era tener solo un archivo para cada UDF que estaba usando y una forma de que mi aplicación los cargara automáticamente. La razón detrás de esto era que si necesitaba editar myUDF1, podía simplemente abrir el archivo myUDF1.cfm y editar lo que necesitaba. También quería poder tomar UDF del CFLib.org y simplemente colocarlas en mi aplicación sin tener que editar nada. Si alguna vez necesitaba eliminar una UDF de mi aplicación, sería tan fácil como borrar el archivo UDF y reiniciar mi aplicación.

para lograr lo que quería, he modificado mi UDFs.cfc a 11 líneas de código:

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

Entonces, ¿qué está pasando exactamente?

En pocas palabras, esto es lo que está sucediendo: Tengo un directorio llamado udfs en el mismo directorio que tengo mi UDFs.cfc. Este es el directorio donde pongo todos mis archivos UDF CFM. Lo que UDFs.cfc hace es escanear este directorio cuando se llama y automáticamente incluye cada archivo CFM que encuentra. Por lo tanto, carga automáticamente todas las UDF en la carpeta UDFs (comúnmente llamada "mixin").

¡Así que se ha alcanzado mi objetivo! Tengo cada UDF en su propio archivo, así que no tengo que desplazarme a través de un enorme archivo de componente para encontrarla. Ahora puedo abrirlo y editarlo fácilmente. Con solo mirar el directorio, sé qué UDF está usando mi aplicación. Puedo agregar automáticamente un UDF desde CFLib.org simplemente guardando el texto del navegador en un archivo en el directorio. Además, si ya no necesito usar el UDF en mi aplicación, simplemente elimino el archivo del directorio y se elimina de mi aplicación durante el próximo reinicio. Todo esto se hace sin tener que tocar el archivo principal UDFs.cfc.

A continuación se muestra un ejemplo de cómo se ve uno de los archivos UDF CFM. El archivo se llama fullLeft.cfm y reside en el directorio 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

Muy buena idea, tendré que experimentar con su enfoque. – kevink

+0

He estado haciendo esto durante mucho tiempo con buenos resultados, también. –

+1

Implementé esto ayer y tengo que decir que funcionó muy bien. También utilicé el enfoque para simplificar las pruebas de mi unidad recorriendo el directorio y asegurándome de que no se pueden perder funciones. Muy resbaladizo – kevink

1

Creo que esto depende de su estilo de programación, vaya con el estilo que le resulte más cómodo. Me parece la forma más fácil es en Application.cfm, establece una variable en el ámbito de aplicación a un cfcomponent con todas mis funciones de utilidad:

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

Ahora puede llamar a métodos en application.utitlies desde cualquier lugar. Tenga en cuenta que si realiza cambios en su componente cf, debe actualizar su variable de aplicación con una nueva instancia de Utilidades.

+0

Quizás esto podría escribirse como 'param application.utilities = createObject (" component "," Utilities ");' –

1

si está utilizando Application.cfc (si no está Me gustaría sugerir fuertemente la migración a la misma desde Application.cfm - es muy fácil de hacer) se puede construir un baseComponent.cfc con todos sus métodos y UDF hacer que Application.cfc herede de baseComponent. luego, en el método onRequestStart establece una variable llamada request.app = this;

para la solicitud de entiure, puede utilizar request.app.methodname() para acceder a la UDF. su manera muy agradable y simple de manejar UDFs

también, si lo desea, puede hacer que todos sus cfcs hereden del mismo baseComponent para que todos sus cfcs tengan esas funciones de utilidad como métodos nativos. hace que la prueba unitaria de los cfcs sea muy fácil porque los cfcs no necesitan responder en una referencia pasada (inyectada) al componente UDf, ¡son descendientes de este!

Un desafío con este enfoque es que el atributo extends de un cfc no puede ser una expresión ... así que dependiendo de cómo empaquete sus componentes, esto puede ser difícil de implementar. la forma más fácil de manejarlo es con un mapeo de fusión fría.

hth Jon

1

Utilizamos archivos .cfm de bibliotecas de funciones y llamar al archivo correspondiente con cfinclude. Algunos de los archivos .cfm se han descargado de cflib.org y otros los hemos escrito nosotros. Los archivos están en un directorio llamado UDF que es un subdirectorio de otro directorio que está asignado al carácter de barra diagonal. La instrucción cfinclude es simple:

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

Este enfoque hace que las funciones estén disponibles para todas las aplicaciones en el servidor.

También preferimos el enfoque de varias bibliotecas pequeñas. Cada biblioteca es específica de un tema (matemática, cadena, matriz de lista, etc.)

+0

¿Cuántos archivos pequeños acaba teniendo al final del día? –

0

Opción: ¿Utiliza un solo archivo grande con funciones y cfinclude?

A: He hecho eso pero lo hago cada vez menos. Me gusta aprovechar la herencia y cfcexplorer

Opción: ¿Utiliza un único archivo grande como un componente cf y llama a creatobject/cfinvoke?

R: Si lo hago a menudo esta opción

: ¿Usted pone cada cffunction utilidad en su propia CFC y llamar createobject/cfinvoke?

R: Yo podría hacer esto si espero funciones adicionales que se añadirán más tarde

Opción: ¿Utiliza la sintaxis taglib cfimport?

R: hacer las cosas de esa manera i18n

Opción: ¿Utiliza los CustomTags

A: No en mucho tiempo. Los cfc son mejores en este

Opción: ¿o cfmodule?

A: No en mucho tiempo. cfc's son mejores en esto. caller. * scope puede dificultar la depuración

0

Me doy cuenta de que esta es una vieja pregunta, pero utilizo un enfoque un poco diferente para estos problemas.

Utilidad Función/Enfoque Singleton con 'inyección'

creo un 'núcleo' o CFC 'utilidad'. En ella Embalo todos mis funciones de tipo de utilidad que sean:

  • utiliza con frecuencia todo el tiempo en todas partes (tales como genérico viewRecord() DAO y una función básica checkSecurity(), et al.)
  • son funciones de base que en mi humilde opinión deberían ser núcleo en CF (tales como lpad(), capitalize(), et al)
  • son las envolturas de algunas etiquetas que me permite utilizar cfscript forma ubicua (tales como exit() que envuelve <cfexit>)

En onApplicationStart(), creo una instancia de este objeto y la asigno al ámbito Application creando así un singleton estático de clases.

Luego, en lugar de extender o volver a incluir esto en casi todos mis cfc, lo que me permite usar la extensión para un tipo de herencia más tradicional, inyecte estos métodos en el constructor (el init) de todos mis cfc's build . Lo hago por llamar a un método en el propio objeto de la utilidad de tal manera que:

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

injectCoreMethods() El método devuelve una estructura selectiva de las funciones de utilidad que quiero prácticamente extendido a todos mis objetos. No inyecta necesariamente todos los los métodos de utilidad. Los menos utilizados, incluido el propio injectCoreMethods(), aún tendrían que ser direccionados a través del puntero de aplicación singleton completo, tal que Application.MyApp.Objects.oCore.infrequentMethod().

Al inyectar en el alcance Variables, que está protegido, estos métodos serán efectivamente métodos privados. Por lo tanto, los volcados de los objetos no mostrarán estas funciones de utilidad, pero son perfectamente accesibles dentro del cfc por todos sus métodos directos.

organización del archivo:

que han caído en el patrón general de tener una CFC por carpeta. En cada carpeta, tengo un archivo cfc para el componente y el init. Todos los demás métodos los divido en archivos cfm y los incluyo en ese cfc. Lo hago para:

  1. archivos CFC Evitar gigantes de más de 1000 líneas que pueden ralentizar mi IDE (utilizo aptana/cfeclipse)
  2. permitir que los cambios pueden grabar/seguimiento más discreta en un archivo por base del archivo y así grabada en mi SCM/software de control de versiones.
  3. Permita que más de una persona trabaje en un objeto dado sin que el código salte entre sí.

lo tanto un objeto DAO que contiene 4 métodos CRUD sería algo como esto:

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

La CFC solo contiene los comentarios init() y auto-documentación y en la zona de pseudo-constructor que incluya los cuatro métodos. Esto también me permite tomar cualquier cfc por su carpeta y moverlo a alguna parte.

Lo mismo para la utilidad cfc. Se encuentra en su propia carpeta y tiene unas 30 funciones extrañas entre 10 o más archivos cfm (algunas funciones simples que dejo en el mismo archivo como _string.cfm que en realidad contiene lpad(), rpad(), etc. todas relacionadas con cadenas. Ya entendió la idea.)

módulos y etiquetas personalizadas

puedo evitar estos a toda costa, ya que necesitan estar registrado y obstaculizar fácil mover/despliegues. No me gustan las cosas que no se configuran solo al arrastrar y soltar de un entorno a otro. CF5: tenías que hacer las cosas de esta manera mucho más. Pero desde CF6 y la capacidad de usar objetos en un patrón de OOP real, ¿por qué querrías? Hay muy pocos casos que desee/necesite si los tiene.

Otros

solía poner las funciones 'básicas' en el base.cfc que se extiende de forma automática en todos los CFC generada por CF (buscar, agregar una función y listo! Un poco como añadir cosas a prototipo en js). Realmente me gustaba mucho, pero era un problema para la implementación/mantenimiento.

Hasta cierto punto, tomo un enfoque de fábrica. A menudo pongo una cantidad considerable de cfc estáticos en la aplicación como el núcleo. Un controlador lee una tabla de control genérica y configura todos los objetos en un bucle junto con un montón de otras cosas en el inicio de la aplicación como variables de la aplicación. Pero algunos objetos se instancian cuando es necesario, obviamente los objetos pesados ​​y los objetos que contienen datos manipulables [semi] persistentes entran en esa categoría

He estado haciendo esto de alguna manera desde CF7. Con CF9 + se está volviendo bastante fácil, maduro y resbaladizo.

Cuestiones relacionadas