Question

Je suis en train de réorganiser mes structures de répertoires ColdFusion et je suis curieux de savoir comment les développeurs expérimentés organisent des bibliothèques de fonctions plus petites.

Je ne suis pas aussi curieux des composants élaborés (objets) que des dizaines de petites fonctions utilitaires que nous avons tous créées au fil du temps.

  • Utilisez-vous un fichier unique volumineux avec des fonctions et le finalisez-vous?
  • Utilisez-vous un seul fichier volumineux en tant que cfcomponent et appelez creatobject / cfinvoke?
  • Placez-vous chaque fonction d’utilitaire dans son propre cfc et appelez createobject / cfinvoke?
  • Utilisez-vous la syntaxe cfimport taglib?
  • Utilisez-vous les CustomTags ou cfmodule?
  • Avez-vous un meilleur moyen?

Comme je n’aime pas la syntaxe verbeuse, j’ai simplement ajouté un fichier lib.cfm contenant de nombreuses fonctions courantes. Je peux les refactoriser en cfcs groupés sur lesquels je peux créer un objet simplement pour avoir une meilleure isolation sur des portées variables.

Y a-t-il une meilleure façon de faire cela?

Était-ce utile?

La solution

Ceci est une réimpression d'un article de blog que j'ai publié le 13 juin 2007. J'utilise cette méthode depuis un moment et cela fonctionne très bien! YMMV.

Qui n'aime pas les fonctions définies par l'utilisateur (UDF)? Si vous avez déjà programmé, il est fort probable que vous les ayez beaucoup utilisées. Le principal problème des utilisateurs est de savoir comment les inclure et les organiser dans votre application.

Ce que j'ai constaté chez la plupart des gens, c’est de créer un fichier Utils.cfc ou UDFs.cfc et de copier / coller leurs fichiers UDF qu’ils souhaitent utiliser dans le composant, comme illustré ci-dessous:

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

Une fois que vous avez collé tous les fichiers UDF utilisés par votre application dans votre composant, vous devez les mettre à la disposition de votre application. Presque toutes les personnes que j'ai vues effectuent ce chargement par composant dans le domaine d'application. La ligne suivante est placée dans onApplicationStart() si vous utilisez Application.cfc ou simplement en l'ajoutant dans Application.cfm si vous l'utilisez:

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

Peu importe celui que vous utilisez, Application.cfc ou Application.cfm, les résultats sont les mêmes. tous vos fichiers UDF sont disponibles pour votre application et vous pouvez les utiliser librement tout au long. La seule différence est le nom de la variable que vous utilisez. J'utilise application.functions, certains utilisent application.utils ou application.udfs; ça ne compte & # 8217; peu importe, encore une fois, les résultats sont les mêmes.

Il y a cependant un problème avec cette approche: elle est lourde et le composant UDF deviendra énorme. Le problème de l'édition d'un fichier de composant aussi volumineux devient un cauchemar, car parcourir des milliers de lignes de code n'est pas très amusant et j'ai également remarqué que CFEclipse s'enlise dans d'énormes fichiers. Bien sûr, l’effondrement du code procure un certain soulagement, mais il doit exister un meilleur moyen.

Ce que je voulais, c’était avoir un fichier par UDF que j’utilisais et un moyen de charger automatiquement mon application. La raison derrière cela était que si je devais éditer myUDF1, je pouvais simplement ouvrir le fichier myUDF1.cfm et éditer ce dont j'avais besoin. Je souhaitais également pouvoir récupérer des fichiers UDF sur CFLib.org et les déposer simplement dans mon application sans avoir à éditer n'importe quoi. Si jamais je devais supprimer une fonction utilisateur de mon application, ce serait aussi simple que de supprimer le fichier UDF et de réinitialiser mon application.

Pour accomplir ce que je voulais, j'ai modifié mon fichier UDFs.cfc en 11 lignes de code:

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

Alors, que se passe-t-il exactement?

En résumé, voici ce que & # 8217; a lieu: J'ai un répertoire nommé udfs dans le même répertoire que mon fichier UDFs.cfc. C'est le répertoire dans lequel j'ai mis tous mes fichiers UDF CFM. Le fichier UDFs.cfc numérise ce répertoire lorsqu'il est appelé et inclut automatiquement chaque fichier CFM trouvé. Ainsi, il charge automatiquement tous les fichiers UDF du dossier UDF (généralement appelé & "Mixin &";).

Donc mon objectif est atteint! J'ai chaque fichier UDF dans son propre fichier, je n'ai donc pas à faire défiler un énorme fichier de composant pour le trouver. Je peux maintenant l'ouvrir et l'éditer facilement. En regardant simplement le répertoire, je sais quels UDF sont utilisés par mon application. Je peux ajouter automatiquement un fichier UDF de CFLib.org en enregistrant simplement le texte du navigateur dans un fichier du répertoire. De plus, si je n'ai plus besoin d'utiliser l'UDF dans mon application, je supprime simplement le fichier du répertoire et il est supprimé de mon application lors de la prochaine réinitialisation. Tout cela est fait sans avoir à toucher le fichier UDFs.cfc principal.

Vous trouverez ci-dessous un exemple de l’un des fichiers UDF CFM. Le fichier s'appelle fullLeft.cfm et réside dans le répertoire 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>

Autres conseils

Je pense que cela dépend de votre style de programmation. Choisissez le style qui vous convient le mieux. Je trouve que le moyen le plus simple est dans application.cfm. Définissez une variable dans le champ d'application avec un cfcomponent avec toutes mes fonctions utilitaires:

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

Vous pouvez maintenant appeler des méthodes dans application.utitlies de n’importe où. Notez que si vous apportez des modifications à votre cfcomponent, vous devez actualiser votre variable d’application avec une nouvelle instance d’utilitaires.

Si vous utilisez Application.cfc (si ce n’est pas le cas, je vous suggère fortement de migrer d’application.cfm - c’est très facile à faire), vous pouvez créer un fichier baseComponent.cfc avec toutes vos méthodes UDF et disposer de l’application. cfc hériter de baseComponent. puis dans la méthode onRequestStart, définissez une variable appelée request.app = this;

pour la requête entiure, vous pouvez ensuite utiliser request.app.methodname () pour accéder à la fonction définie par l'utilisateur. sa manière très simple et agréable de manipuler des FDU

De plus, si vous le souhaitez, tous vos cfcs peuvent hériter du même composant de base, de sorte que tous vos cfcs disposent de ces fonctions util en tant que méthodes natives. rend les tests unitaires cfcs très simples car les cfcs n’ont pas besoin de répondre sur une référence (injectée) réussie au composant UDf, ils en sont décédés!

L’un des défis de cette approche est que l’attribut extend d’un cfc ne peut pas être une expression ... il peut donc être difficile à implémenter, selon la manière dont vous empaquetez vos composants. le moyen le plus simple de le gérer consiste à utiliser un mappage coldfusion.

hth Jon

Nous utilisons des fichiers .cfm pour les bibliothèques de fonctions et appelons le fichier approprié avec cfinclude. Certains des fichiers .cfm ont été téléchargés à partir de cflib.org et d’autres sont écrits par nous. Les fichiers se trouvent dans un répertoire nommé UDF, qui est un sous-répertoire d’un autre répertoire mappé sur la barre oblique. L'instruction cfinclude est simplement:

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

Cette approche rend les fonctions disponibles pour toutes les applications du serveur.

Nous préférons également l’approche de plusieurs petites bibliothèques. Chaque bibliothèque est spécifique à un sujet (math, chaîne, tableau de liste, etc.)

Option: utilisez-vous un seul fichier volumineux avec cffunctions et cfinclude?

R: Je l’ai fait mais je le fais de moins en moins. J'aime profiter de l'héritage et de cfcexplorer

Option: utilisez-vous un seul fichier volumineux en tant que cfcomponent et appelez creatobject / cfinvoke?

A: Oui, je le fais souvent

Option: placez-vous chaque fonction utilitaire dans son propre cfc et appelez createobject / cfinvoke?

R: Je pourrais le faire si je m'attends à ce que des fonctions supplémentaires soient ajoutées plus tard

Option: utilisez-vous la syntaxe cfimport taglib?

R: Je fais ce que je fais de cette façon

Option: utilisez-vous les CustomTags

R: Pas dans longtemps. les cfc sont meilleurs à cette

Option: ou cfmodule?

R: Pas dans longtemps. les cfc sont meilleurs à cela. caller. * scope peut rendre le débogage difficile

Je réalise que c’est une vieille question, mais j’utilise une approche légèrement différente pour ces problèmes.

Fonction utilitaire / Approche singleton avec "Injection"

Je crée un cfc 'core' ou 'utility'. J'emballe toutes mes fonctions de type utilitaire qui sont:

  • Souvent utilisé partout (par exemple, un générique viewRecord() dao et une fonction checkSecurity() principale, etc.)
  • Sont des fonctions de base qui devraient être à la base de la FC (telles que lpad(), capitalize() et autres)
  • Sont des wrappers de certaines balises qui me permettent d’utiliser cfscript une omniprésence (comme exit() qui enveloppe <cfexit>)

Sur onApplicationStart(), je crée une instance de cet objet et l’assigne à la portée Application, créant ainsi un singleton statique.

Ensuite, au lieu d’étendre ou de ré-inclure ceci dans presque tous mes cfc, ce qui me permet d’utiliser l’extension pour un type d’héritage plus traditionnel, j’injecte ensuite ces méthodes dans le constructeur (init) de tous mes cfc que je construis. Je le fais en appelant une méthode sur l'objet utilitaire lui-même tel que:

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

La méthode injectCoreMethods() retourne de manière sélective une structure des fonctions utilitaires que je souhaite pratiquement étendre à tous mes objets. Cela n'injecte pas nécessairement toutes les méthodes utilitaires. Les applications moins fréquemment utilisées, y compris Application.MyApp.Objects.oCore.infrequentMethod() lui-même, devront toujours être adressées via le pointeur d'application unique unique tel que Variables.

En injectant dans la portée init() protégée, ces méthodes seront en réalité des méthodes privées. Ainsi, toutes les sauvegardes des objets ne montreront pas ces fonctions utilitaires mais seront parfaitement accessibles dans le cfc par toutes ses méthodes directes.

Organisation du fichier:

Je suis généralement tombé dans le schéma d'avoir un cfc par dossier. Dans chaque dossier, j'ai un fichier cfc pour le composant et l'init. Toutes les autres méthodes que je décompose dans des fichiers cfm et que j'inclus dans ce cfc. Je fais ceci pour:

  1. Évitez les fichiers cfc géants de plus de 1000 lignes qui pourraient ralentir mon IDE (j'utilise aptana / cfeclipse)
  2. Autoriser l'enregistrement / le suivi des modifications de manière plus discrète, fichier par fichier, et ainsi être enregistré dans mon logiciel de contrôle SCM / Version.
  3. Autoriser plus d'une personne à travailler sur un objet donné sans contourner le code.

Ainsi, un objet dao contenant 4 méthodes crud ressemblerait à ceci:

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

La cfc contient uniquement les commentaires _string.cfm et auto-documentés. Dans la zone de pseudo-constructeur, j'inclus les quatre méthodes. Cela me permet également de prendre n'importe quel cfc par son dossier et de le déplacer quelque part.

Idem pour l'utilitaire cfc. Il se trouve dans son propre dossier et contient une trentaine de fonctions parmi une dizaine de fichiers cfm (certaines fonctions simples que je laisse dans le même fichier, comme rpad() qui contient en fait <=>, <=>, etc., toutes liées à une chaîne. Vous avoir l'idée.)

Modules et balises personnalisées

Je les évite à tout prix car ils doivent être enregistrés et entraver les déplacements / déploiements faciles. Je n'aime pas les choses qui ne se configurent pas simplement par glisser-déposer d'un environnement à un autre. CF5 - il fallait faire les choses de cette façon beaucoup plus. Mais depuis que CF6 et la possibilité d’utiliser des objets dans un modèle de POO réel, pourquoi voudriez-vous le faire? Il y a très peu de cas que vous voudriez / auriez besoin de faire, le cas échéant.

Autre

J'avais l'habitude de mettre des fonctions "de base" dans le fichier base.cfc, qui est automatiquement étendu à tous les cfc générés par CF (recherchez-la, ajoutez une fonction et le tour est joué! Un peu comme ajouter des éléments au prototype dans js). J'aimais beaucoup cela, mais c'était un problème de déploiement / maintenance.

Dans une certaine mesure, je prends l’approche Factory. Je mets souvent une quantité non négligeable de cfc statiques dans l'application, comme celle de base. Un contrôleur lit une table de contrôle générique et configure tous les objets d'une boucle avec un tas d'autres choses sur l'application qui démarrent comme des variables d'application. Mais certains objets sont instanciés au besoin, évidemment des objets lourds et des objets contenant des objets manipulés.les données [semi] persistantes susceptibles entrent dans cette catégorie

Je le fais à certains égards depuis CF7. Avec CF9 +, il devient assez facile, mature et lisse.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top