2009-03-08 13 views
14

Sto riorganizzando le strutture della mia directory ColdFusion e sono curioso di sapere come gli esperti sviluppatori di CF stiano organizzando librerie di piccoli cffunctions.Come organizzi le tue piccole funzioni riutilizzabili?

Non sono così curioso di elaborare componenti (oggetti) quanto le dozzine di piccole funzioni di utilità che tutti noi costruiamo nel tempo.

  • Si utilizza un singolo file di grandi dimensioni con funzioni di cifratura e cfinclude?
  • Si utilizza un singolo file di grandi dimensioni come cfcomponent e si chiama creatobject/cfinvoke?
  • Metti ogni cffunction di utilità nel proprio cfc e chiama createobject/cfinvoke?
  • Usi la sintassi di tagli di cfimport?
  • Usi CustomTags o cfmodule?
  • Hai un modo migliore?

Dato che non mi piace la sintassi verbosa, ho incluso solo un lib.cfm che contiene un sacco di funzioni comuni in esso. Posso rifattorici su cfcs raggruppati. Posso creare oggetti solo per avere un migliore isolamento sugli ambiti variabili.

C'è un modo migliore per farlo?

risposta

18

Questa è una ristampa di un blog post I did indietro il 13 giugno 2007. Ho utilizzato questo metodo per un bel po 'e funziona benissimo! YMMV.

A chi non piacciono le funzioni definite dall'utente (UDF)? Se hai fatto qualche programmazione, è probabile che tu li abbia usati ampiamente. Il problema più grande che le persone hanno con loro è come includerli e organizzarli nella tua applicazione.

Quello che ho trovato che la maggior parte della gente è creare un Utils.cfc o UDFs.cfc e tagliare e incollare le loro UDF che vogliono utilizzare nel componente come illustrato di seguito:

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

volta avere tutte le UDF che l'applicazione utilizzerà incollata nel componente, sarà necessario rendere disponibili le UDF alla propria applicazione. Quasi tutti quelli che ho visto eseguono questo caricamento dal componente nello scope dell'applicazione. La riga seguente è inserito nel onApplicationStart() se si sta utilizzando Application.cfc o semplicemente aggiungendolo nella Application.cfm se si sta utilizzando che:

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

Qualunque sia uno che si sta utilizzando, Application.cfc o Application.cfm, i risultati sono gli stessi; tutte le tue UDF sono disponibili per la tua applicazione e puoi utilizzarle liberamente. L'unica differenza è quale nome di variabile usi. Uso le funzioni di applicazione, alcuni usano application.utils o application.udfs; non importa, di nuovo, i risultati sono gli stessi.

C'è un problema che ho con questo approccio, però, è ingombrante e il componente UDF diventerà enorme. Il problema di avere un file componente così grande è la modifica diventa un incubo poiché scorrere migliaia di righe di codice non è molto divertente e ho anche notato che CFEclipse si blocca su file enormi. Il collasso del codice sicuro fornisce un certo sollievo, ma deve esserci un modo migliore.

Quello che volevo era avere un solo file per ogni UDF che stavo usando e un modo per la mia applicazione di caricarli automaticamente. La ragione di questo era che se avessi avuto bisogno di modificare myUDF1, avrei potuto semplicemente aprire il file myUDF1.cfm e modificare quello che mi serviva. Volevo anche essere in grado di catturare UDF da CFLib.org e semplicemente rilasciarli nella mia applicazione senza dover modificare nulla. Se dovessi rimuovere una UDF dalla mia applicazione, sarebbe facile come cancellare il file UDF e reinizializzare la mia applicazione.

per realizzare quello che volevo, ho modificato il mio UDFs.cfc a 11 righe di codice:

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

Così che cosa esattamente sta succedendo?

In breve, ecco cosa sta succedendo: ho una directory denominata udfs nella stessa directory in cui ho il mio UDFs.cfc. Questa è la directory in cui ho inserito tutti i miei file UDF CFM. Ciò che fa UDFs.cfc è la scansione di questa directory quando viene chiamata e include automaticamente ogni file CFM che trova. In questo modo carica automaticamente tutte le UDF nella cartella UDF in sé (comunemente chiamata "mixin").

Quindi il mio obiettivo è raggiunto! Ho ciascuna UDF nel proprio file, quindi non devo scorrere un enorme file componente per trovarlo. Ora posso aprirlo e modificarlo facilmente. Guardando la directory, so quali sono le UDF utilizzate dalla mia applicazione. Posso aggiungere automaticamente una UDF da CFLib.org semplicemente salvando il testo dal browser in un file nella directory. Inoltre, se non ho più bisogno di usare l'UDF nella mia applicazione, semplicemente cancello il file dalla directory e viene rimosso dalla mia applicazione durante il prossimo re-init. Tutto ciò viene fatto senza dover toccare il file UDFs.cfc principale.

Di seguito è riportato un esempio di come si presenta uno dei file CFM UDF. Il file si chiama fullLeft.cfm e risiede nella directory UDF.

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

Idea molto interessante, dovrò sperimentare il tuo approccio. – kevink

+0

Lo sto facendo da molto tempo con buoni risultati. –

+1

L'ho implementato ieri e devo dire che ha funzionato benissimo. Ho anche utilizzato l'approccio per semplificare i miei test unitari eseguendo il looping della directory, assicurando così che nessuna funzione possa essere ignorata. Molto lucido – kevink

1

Penso che questo dipenda dal tuo stile di programmazione, scegli lo stile che preferisci. Trovo il modo più semplice è in Application.cfm, impostare una variabile nel campo di applicazione ad un cfcomponent con tutte le mie funzioni di utilità:

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

Ora è possibile chiamare i metodi in application.utitlies da qualsiasi luogo. Si noti che se si apportano modifiche al proprio componente cfC, è necessario aggiornare la variabile dell'applicazione con una nuova istanza di utilità.

+0

Forse questo potrebbe essere scritto come 'param application.utilities = createObject (" component "," Utilities ");' –

1

se si utilizza Application.cfc (se non siete Vorrei suggerire la migrazione ad esso da Application.cfm - è molto facile da fare) si può costruire un baseComponent.cfc con tutti i vostri metodi e UDF avere ereditato Application.cfc da baseComponent. quindi nel metodo onRequestStart impostare una variabile chiamata request.app = this;

per la richiesta intera, è quindi possibile utilizzare request.app.methodname() per accedere all'UDF. è un modo molto bello e semplice di gestire le UDF

Inoltre, se lo desideri puoi avere tutti i tuoi cfcs ereditati dallo stesso baseComponente in modo che tutti i tuoi cfc abbiano quelle funzioni di utilizzo come metodi nativi. rende l'unità di prova di cfcs molto facile perché i cfcs non hanno bisogno di rispondere su un riferimento passato (iniettato) al componente UDf, sono dei decedenti!

una sfida con questo approccio è che l'attributo extends di un cfc non può essere un'espressione ... quindi a seconda di come impacchettate i componenti, questo può essere difficile da implementare. il modo più semplice per gestirlo è con una mappatura coldfusion.

hth Jon

1

Usiamo file .cfm per librerie di funzioni e chiamare il file appropriato con cfinclude. Alcuni dei file .cfm sono stati scaricati da cflib.org e altri sono stati scritti da noi. I file si trovano in una directory chiamata UDF, che è una sottodirectory di un'altra directory che è mappata al carattere della barra diretta. La dichiarazione cfinclude è semplicemente:

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

Questo approccio rende le funzioni a disposizione di tutte le applicazioni sul server.

Preferiamo anche l'approccio alla libreria di piccole dimensioni. Ogni libreria è specifica per argomento (matematica, stringa, elenco-array, ecc.)

+0

Quanti piccoli file finisci per avere alla fine della giornata? –

0

Opzione: si utilizza un singolo file di grandi dimensioni con funzioni di cifratura e cfinclude?

A: L'ho fatto ma lo faccio sempre meno. Mi piace sfruttare l'ereditarietà e cfcexplorer

Opzione: si utilizza un singolo file di grandi dimensioni come cfcomponent e si chiama creatobject/cfinvoke?

A: Sì faccio spesso questo

Opzione: si fa a mettere ogni cffunction utilità nel proprio cfc e chiamare CreateObject/cfinvoke?

A: Potrei farlo se mi aspetto ulteriori funzioni da aggiungere in seguito

Opzione: Usi la sintassi taglib cfimport?

A: faccio cose i18n in questo modo

Opzione: Usi i CustomTags

A: Non in un lungo periodo di tempo. cfc sono migliori in questo

Opzione: o cfmodule?

A: Non molto tempo. cfc sono migliori in questo. chiamante. * scope può rendere difficile il debug

0

Mi rendo conto che questa è una vecchia domanda, ma io uso un po 'un approccio diverso per questi problemi.

Utility Funzione/Singleton Approccio con 'iniezione'

creo un 'core' o CFC 'utility'. In esso I pack tutte le mie funzioni di tipo di utilità che sono:

  • usato frequentemente per tutto il tempo in tutto il mondo (ad esempio una generica viewRecord() dao e una funzione fondamentale checkSecurity(), et al.)
  • sono funzioni di base che imho dovrebbero essere di base in FC (come lpad(), capitalize(), et al)
  • sono wrapper di alcuni tag che mi permettono di usare cfscript ubiquitously (come exit() che avvolge <cfexit>)

Su onApplicationStart(), creare un'istanza di questo oggetto e assegnarlo all'ambito Application creando così un singleton statico di ordinamento.

Quindi invece di estenderlo o ri-includerlo in quasi tutti i miei cfc, che mi consente di usare l'estensione per un tipo di ereditarietà più tradizionale, li inietto quindi nel costruttore (l'init) di tutti i miei cfc che costruisco . Faccio questo chiamando un metodo sull'oggetto di utilità per sé tale che:

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

Il metodo restituisce injectCoreMethods() selettivamente una struttura delle funzioni di utilità che voglio virtualmente esteso in tutti i miei oggetti. Non necessariamente iniettare tutti i metodi di utilità. Quelli meno frequentemente usati, incluso lo injectCoreMethods() stesso, dovrebbero comunque essere indirizzati tramite il puntatore dell'applicazione Singleton completo tale che Application.MyApp.Objects.oCore.infrequentMethod().

Iniettando nell'ambito Variables, che è protetto, questi metodi saranno effettivamente metodi privati. Quindi, qualsiasi dump degli oggetti non mostrerà queste funzioni di utilità, ma sono perfettamente accessibili all'interno di cfc con tutti i suoi metodi diretti.

organizzazione File:

ho generalmente caduto nel modello di avere uno CFC per cartella. In ogni cartella, ho un file cfc per il componente e l'init. Tutti gli altri metodi sono inclusi in file cfm e sono inclusi in quel cfc. Lo faccio per:

  1. file CFC Evitare giganti di 1000+ linee che possono rallentare il mio IDE (io uso Aptana/cfeclipse)
  2. Consenti modifiche da registrare/monitorati più discretamente su un file per file di base e quindi registrato nel mio software di controllo SCM/Version.
  3. Consentire a più di una persona di lavorare su un determinato oggetto senza eseguire il bump del codice l'una nell'altra.

Quindi un oggetto dao che contiene 4 metodi CRUD sarebbe simile a questa:

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

CFC contiene solo i commenti init() e auto-documentazione e nella zona pseudo-costruttore ho includere i quattro metodi. Questo mi permette anche di prendere qualsiasi cfc dalla sua cartella e spostarlo da qualche parte.

Uguale per l'utilità cfc. Si trova nella sua cartella e ha circa 30 funzioni dispari tra 10 o più file cfm (alcune semplici funzioni lascio nello stesso file come _string.cfm che contiene effettivamente lpad(), rpad(), ecc. Tutte le stringhe correlate.)

Moduli e tag personalizzati

ho evitare questi a tutti i costi perché hanno bisogno di essere registrato e ostacolare facile spostare/distribuzioni. Non mi piacciono le cose che non si auto configurano semplicemente trascinandole da un ambiente all'altro. CF5- hai dovuto fare le cose in questo modo molto di più. Ma dal momento che CF6 e la possibilità di utilizzare oggetti in un modello OOP reale, perché vorresti? Ci sono pochissimi casi che vorresti/se necessario.

Altro

ho usato per mettere funzioni 'core' nel base.cfc che si estende automaticamente in tutti i CFC generato dal CF (cercarlo, aggiungere una funzione e voilà! Un po 'come l'aggiunta di cose da prototipo in js). Mi piaceva molto questo, ma era un problema per la distribuzione/manutenzione.

In una certa misura, prendo un approccio di fabbrica. Spesso applico una buona quantità di cfc statici nell'Applicazione come quella principale. Un controller legge una tabella di controllo generica e imposta tutti gli oggetti in un ciclo insieme ad altre cose all'avvio dell'app come le variabili dell'app. Ma alcuni oggetti sono istanziati secondo necessità, ovviamente oggetti pesanti e oggetti che contengono dati [semi] persistenti manipolabili rientrano in quella categoria

Ho fatto, in qualche modo, questo da quando CF7. Con CF9 + sta diventando piuttosto facile, maturo e fluido.

Problemi correlati