Как вы организуете свои маленькие многоразовые функции? - PullRequest
14 голосов
/ 09 марта 2009

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

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

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

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

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

Ответы [ 6 ]

19 голосов
/ 09 марта 2009

Это перепечатка поста в блоге, который я сделал 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>

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

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

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

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

Я хотел, чтобы для каждого используемого UDF был только один файл, и чтобы приложение автоматически загружало их. Причиной этого было то, что если мне нужно было отредактировать myUDF1, я мог бы просто открыть файл myUDF1.cfm и отредактировать то, что мне нужно. Я также хотел иметь возможность получать пользовательские функции из 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>
1 голос
/ 18 ноября 2012

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

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

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

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

1 голос
/ 28 марта 2009

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

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

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

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

НТН Jon

1 голос
/ 09 марта 2009

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

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

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

0 голосов
/ 06 июня 2013

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

Функция полезности / синглтонный подход с «впрыском»

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

  • Часто используется везде и всегда (например, общая viewRecord() dao и базовая 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 + он становится довольно легким, зрелым и гладким.

0 голосов
/ 27 ноября 2012

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

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

Опция: Вы используете один большой файл в качестве компонента cf и вызываете creatobject / cfinvoke?

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

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

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

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

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

Опция: Используете ли вы CustomTags

A: Не надолго. это лучше, чем у cfc

Опция: или cfmodule?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...