Как вы организуете свои маленькие многоразовые функции?

Я реорганизую свои структуры каталогов ColdFusion, и мне интересно, как опытные разработчики CF организовывают библиотеки небольших функций.

Меня не так интересуют сложные компоненты (объекты), как десятки маленьких вспомогательных функций, которые мы все со временем создаем.

  • Используете ли вы один большой файл с cffunctions и cfinclude его?
  • Используете ли вы один большой файл в качестве cfcomponent и вызываете creatobject/cfinvoke?
  • Вы помещаете каждую функцию утилиты в свой собственный cfc и вызываете createobject/cfinvoke?
  • Используете ли вы синтаксис cfimport taglib?
  • Вы используете CustomTags или cfmodule?
  • У тебя есть способ получше?

Поскольку я не люблю подробный синтаксис, я просто включил lib.cfm, в котором есть куча общих функций. Я могу реорганизовать их в сгруппированные cfcs, для которых я могу создать объект, просто чтобы лучше изолировать переменные области.

Есть лучший способ сделать это?

6 ответов

Решение

Это перепечатка поста в блоге, который я сделал 13 июня 2007 года. Я уже давно пользуюсь этим методом, и он отлично работает! YMMV.

Кому не нравятся пользовательские функции (UDF)? Если вы занимались программированием, скорее всего, вы широко их использовали. Самая большая проблема, с которой люди сталкиваются, заключается в том, как включить и организовать их в вашем приложении.

Я обнаружил, что большинство людей создают Utils.cfc или UDFs.cfc и вырезают и вставляют свои UDF, которые они хотят использовать, в компонент, как показано ниже:

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

После того, как у вас есть все UDF, которые ваше приложение будет использовать, вставленные в ваш компонент, вам нужно будет сделать эти UDF доступными для вашего приложения. Почти все, кого я видел, делают эту загрузку компонентом в область приложения. Следующая строка помещается в onApplicationStart() если вы используете Application.cfc или просто добавляете его в Application.cfm, если вы используете это:

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

Какой бы из них вы ни использовали, Application.cfc или Application.cfm, результаты будут одинаковыми; все ваши пользовательские функции доступны для вашего приложения, и вы можете свободно использовать их повсюду. Разница лишь в том, какое имя переменной вы используете. Я использую application.functions, некоторые используют application.utils или application.udfs; опять же, не имеет значения, результаты такие же.

Однако у меня есть одна проблема, связанная с этим подходом: он громоздкий и компонент UDF станет огромным. Проблема, связанная с редактированием такого огромного файла компонента, становится кошмаром, поскольку прокрутка тысяч строк кода не очень интересна, а также я заметил, что CFEclipse заваливает огромные файлы. Конечно, свертывание кода действительно дает некоторое облегчение, но должен быть лучший путь.

Я хотел, чтобы для каждого используемого UDF был только один файл, и чтобы приложение автоматически загружало их. Причиной этого было то, что если бы мне нужно было отредактировать myUDF1 Я мог бы просто открыть файл myUDF1.cfm и отредактируйте то, что мне было нужно. Я также хотел иметь возможность получать UDF- файлы с CFLib.org и просто помещать их в мое приложение без необходимости что-либо редактировать. Если мне когда-нибудь понадобится удалить UDF из моего приложения, это будет так же просто, как удалить файл UDF и повторно инициализировать мое приложение.

Чтобы выполнить то, что я хотел, я изменил свой UDFs.cfc до 11 строк кода:

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

Так что именно происходит?

Вкратце, вот что происходит: у меня есть каталог с именем udfs в том же каталоге, что у меня есть мой UDFs.cfc. Это каталог, в который я положил все свои файлы UDF CFM. UDFs.cfc выполняет сканирование этого каталога при его вызове и автоматически включает каждый найденный CFM-файл. Таким образом, он автоматически загружает любые UDF из папки UDF в себя (обычно это называется "mixin").

Так что моя цель достигнута! У меня есть каждый UDF в отдельном файле, поэтому мне не нужно пролистывать огромный файл компонента, чтобы найти его. Теперь я могу открыть и редактировать его легко. Глядя на каталог, я знаю, какие UDF использует мое приложение. Я могу автоматически добавить UDF из CFLib.org, просто сохранив текст из браузера в файл в каталоге. Плюс, если мне больше не нужно использовать UDF в моем приложении, я просто удаляю файл из каталога, и он удаляется из моего приложения во время следующей повторной инициализации. Все это делается без прикосновения к основному файлу UDFs.cfc.

Ниже приведен пример того, как выглядит один из файлов UDF CFM. Файл называется fullLeft.cfm и находится в каталоге 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>

Мы используем файлы.cfm для библиотек функций и вызываем соответствующий файл с помощью cfinclude. Некоторые файлы.cfm были загружены с cflib.org, а другие написаны нами. Файлы находятся в каталоге с именем UDF, который является подкаталогом другого каталога, который сопоставлен с символом прямой косой черты. Утверждение cfinclude просто:

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

Такой подход делает функции доступными для всех приложений на сервере.

Мы также предпочитаем подход с несколькими небольшими библиотеками. Каждая библиотека относится к определенной теме (математика, строка, список-массив и т. Д.)

Я думаю, что это зависит от вашего стиля программирования, выбирайте тот, который вам наиболее удобен. Я считаю, что самый простой способ в application.cfm, установить переменную в области приложения для cfcomponent со всеми моими вспомогательными функциями:

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

Теперь вы можете вызывать методы в application.utitlies из любого места. Обратите внимание, что если вы вносите изменения в свой cfcomponent, вы должны обновить переменную вашего приложения новым экземпляром Utilities.

Если вы используете Application.cfc (если нет, я бы настоятельно рекомендовал перейти на него из Application.cfm - это очень легко сделать), вы можете создать baseComponent.cfc со всеми вашими методами UDF и иметь наследование Application.cfc от baseComponent. затем в методе onRequestStart установите переменную с именем request.app=this;

для запроса entiure вы можете использовать request.app.methodname() для доступа к UDF. это очень хороший и простой способ обработки UDF

Кроме того, если вам нравится, вы можете иметь все ваши cfcs наследовать от одного и того же baseComponent, так что все ваши cfcs имеют эти функции утилит как родные методы. делает модульное тестирование cfcs очень простым, потому что cfcs не нужно отвечать на переданную (вставленную) ссылку на компонент UDf, они являются его потомками!

Одна из проблем этого подхода заключается в том, что атрибут extends для cfc не может быть выражением... поэтому в зависимости от того, как вы упаковываете свои компоненты, это может быть сложно реализовать. самый простой способ справиться с этим с помощью картографирования холодного синтеза.

Йон Джон

Я понимаю, что это старый вопрос, но я использую немного другой подход к этим вопросам.

Функция полезности / синглтонный подход с "инъекцией"

Я создаю "ядро" или "утилиту" CFC. В него я упаковываю все свои функции типа утилит:

  • Часто используется везде и всегда (например, общий viewRecord() дао и ядро checkSecurity() функция и др.)
  • Являются ли базовые функции, которые imho должны быть основными в CF (такие как lpad(), capitalize(), и другие)
  • Являются ли обертки некоторых тегов, которые позволяют мне использовать cfscript повсеместно (например, exit() который оборачивает <cfexit>)

На onApplicationStart()Я создаю экземпляр этого объекта и назначаю его Application область, таким образом, создавая статический синглтон сортов.

Затем вместо расширения или повторного включения этого почти во все мои cfc, что позволяет мне использовать расширение для более традиционного типа наследования, я затем внедряю эти методы в конструктор (init) всех моих cfc, которые я строю. Я делаю это, вызывая метод на самом объекте утилиты так, чтобы:

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

injectCoreMethods() Метод выборочно возвращает структуру полезных функций, которые я хочу, фактически распространить на все мои объекты. Это не обязательно вводить все служебные методы. Менее часто используемые, в том числе injectCoreMethods() сам по себе, все еще должен быть адресован через полный указатель одноэлементного приложения, так что Application.MyApp.Objects.oCore.infrequentMethod(),

Вводя в Variables Область, которая защищена, эти методы будут эффективно частными методами. Таким образом, любые дампы объектов не будут показывать эти служебные функции, но будут полностью доступны в cfc всеми его прямыми методами.

Файловая организация:

Я обычно впадаю в шаблон наличия одного cfc на папку. В каждой папке у меня есть один файл cfc для компонента и init. Все остальные методы я разбил на файлы cfm и включил в этот cfc. Я делаю это, чтобы:

  1. Избегайте гигантских файлов cfc из 1000+ строк, которые могут замедлить мою IDE (я использую aptana/cfeclipse)
  2. Разрешить запись / отслеживание изменений более дискретно для каждого файла и, таким образом, для записи в моем программном обеспечении SCM/ контроля версий.
  3. Разрешить нескольким людям работать над данным объектом, не сталкиваясь друг с другом.

Таким образом, объект dao, который содержит 4 метода crud, будет выглядеть примерно так:

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

CFC просто содержит init() и самодокументированные комментарии, и в области псевдоконструктора я включаю четыре метода. Это также позволяет мне взять любой cfc за его папку и переместить его куда-нибудь.

То же самое для утилиты CFC. Он находится в своей собственной папке и имеет около 30 нечетных функций среди 10 или около того файлов cfm (некоторые простые функции, которые я оставляю в том же файле, такие как _string.cfm который на самом деле содержит lpad(), rpad()и т. д. все строки связаны. Вы поняли.)

Модули и пользовательские теги

Я избегаю их любой ценой, потому что они должны быть зарегистрированы и затруднять легкое перемещение / развертывание. Мне не нравятся вещи, которые не просто настраиваются при перетаскивании из одной среды в другую. CF5- ты должен был делать это намного больше. Но с CF6 и возможностью использовать объекты в реальном ООП-шаблоне, зачем вам это? Есть очень мало случаев, которые вы хотели бы / нужно, если таковые имеются.

Другой

Раньше я помещал "базовые" функции в base.cfc, который автоматически распространяется на все cfc, сгенерированные CF (найдите его, добавьте функцию и вуаля! Вроде как добавление вещей в прототип в js). Раньше мне действительно нравилось это, но это было проблемой для развертывания / обслуживания.

В какой-то степени я использую Фабричный подход. Я часто помещаю достаточное количество статических CFC в приложение, как основной. Контроллер читает общую контрольную таблицу и устанавливает все объекты в цикле вместе с кучей других вещей при запуске приложения, таких как переменные приложения. Но некоторые объекты создаются по мере необходимости, в эту категорию попадают, очевидно, тяжелые объекты и объекты, содержащие манипулируемые [полупостоянные] данные.

Я, в некотором смысле, делал это с CF7. С CF9+ он становится довольно легким, зрелым и гладким.

Вариант: Вы используете большой одиночный файл с cffunctions и cfinclude его?

A: Я сделал это, но делаю это все реже и реже. Мне нравится использовать преимущества наследования и cfcexplorer

Вариант: Используете ли вы один большой файл в качестве cfcomponent и вызываете creatobject / cfinvoke?

A: Да, я часто делаю это

Опция: Вы помещаете каждую функцию утилиты в свой собственный cfc и вызываете createobject / cfinvoke?

A: Я мог бы сделать это, если я ожидаю, что дополнительные функции будут добавлены позже

Опция: Используете ли вы синтаксис cfimport taglib?

A: Я делаю вещи таким способом

Вариант: Используете ли вы CustomTags

A: Не в течение длительного времени. CFC лучше в этом

Вариант: или cfmodule?

A: Не в течение длительного времени. CFC лучше в этом. вызывающий.* область может затруднить отладку

Другие вопросы по тегам