(Ре) разработка для Мобильности

При портировании приложений на OS X Вы могли бы полагать, что изменения в архитектуре создания сделали Ваш порт (и будущие порты) более удобными в сопровождении.

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

Что такое Мобильность?

Существует много различных определений для мобильности в контексте проекта кода. Одно определение мобильности ограничивает себя функциями, указанными в обычно принимаемом стандарте, такими как Single UNIX Specification (SUS) или Интерфейс Переносимой информационной системы (POSIX). Это полезно для программного обеспечения не-GUI, особенно при перемещении среди основанных на UNIX и подобных UNIX систем, но оно не адресует графические интерфейсы пользователя и не позволяет Вам использовать в своих интересах определенные для операционной системы возможности.

Альтернатива таким ограничениям должна разработать Ваш код модульным способом так, чтобы дополнительные функции, определенные для данного OS, могли быть “включены в” Ваше приложение. Делаете ли Вы это со сменным интерфейсом, иерархией классов или простой абстракцией, это - самый эффективный способ позволить Вашему программному обеспечению быть легко портированным на многократные платформы, не жертвуя функциональностью.

Существует много различных способов достигнуть этой степени мобильности, все из которых имеют допустимое использование. Цель этой главы состоит в том, чтобы описать некоторые из них и объяснить, когда это является надлежащим (или несоответствующим) использовать каждого. Это должно помочь Вам избежать некоторых распространенных ошибок при изменении программного обеспечения для поддержки многократных платформ.

Конечно, надлежащее время для принятия таких решений - прежде чем Вы начнете писать первую строку кода. Так как это не всегда возможно, эта глава также опишет способы переоснастить эти понятия в существующее приложение самым удобным в сопровождении возможным способом.

Используя уровни абстракции

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

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

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

Что необходимо обычно избегать делать, тем не менее, абстрагирует открытое, читайте, и вызовы записи индивидуально, затем с помощью них в больших циклах. Это приводит к напрасно нечитабельному коду с большим количеством очень небольших функций уровня абстракции. Необходимо вместо этого представить интерфейс, который целесообразен в контексте приложения.

Например, если Ваше приложение выполняет потоковую передачу, у Вас могла бы быть следующая полная структура:

Ваша открытая функция могла бы возвратить непрозрачный дескриптор, который может быть роздан в ядре приложения, но чья структура неизвестна вне функций файла.

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

Избегите кода Conditionalizing

Не попадайте в прерывание conditionalizing сотен битов кода с #ifdef директивы. (Случайное #ifdef в порядке.) Это быстро приводит к неуправляемому коду, особенно при поддержке OS X, многократных основанных на UNIX и подобных UNIX систем, Классического Mac OS и Windows.

При изучении спотов в коде, в котором Вы нуждаетесь к особому случаю, как правило, Вы найдете, что их проблемы тесно связаны. Например, они могли бы все быть графическими подпрограммами. Если так, Вы могли бы вытянуть все функции, вызывающие графические подпрограммы в их собственный файл и conditionalize включение всего файла. Например, Вы могли бы использовать использование одного файла для подпрограмм X11, один для подпрограмм Win32, и один для подпрограмм Углерода или Какао. Это проще к коду conditionalize в нескольких базовых функциях, чем к conditionalize это в ста случайных местах в коде. Точно так же проще заменить весь файл, чем заменить биты файла.

Проблемы абстракции GUI

Большинство уровней абстракции не-GUI должно быть максимально тонким, так как толстый уровень абстракции имеет тенденцию приводить к значительному расхождению платформы. Однако с GUI, расхождение платформы желательно и является таким образом исключением к этому правилу.

Если Вы хотите, чтобы Ваше приложение выглядело идентичным на всех платформах, то необходимо сделать уровень абстракции тонким. Однако это имеет тенденцию приводить к пользователям OS X, бросающим Ваше приложение в мусорный контейнер, потому что не похоже на приложение Mac. OS X имеет общие правила стиля GUI, за которыми не следует большинство приложений X11.

Например, приложения X11 часто имеют на меню окна, в то время как OS X имеет на меню приложения (с возможностью добавить или удалить меню, когда различное окно находится на переднем плане). В то время как приложения Mac имеют тенденцию округлиться, большой, проще к кнопкам считывания, приложения X11 имеют тенденцию иметь очень квадратные функции и маленькие, эффективные пространством кнопки.

Точно так же другие операционные системы, такие как Windows и Классический Mac OS, имеют различные правила проектирования. Необходимо понять правила проектирования GUI для каждой вычислительной платформы, или заявление не будет принято легко на тех платформах.

Поэтому является самым простым разделить Ваш весь пользовательский интерфейс на отдельный файл или каталог для X11, затем клонировать это и переписать его для Углерода или Какао при портировании на OS X. Тем путем можно в большой степени перепроектировать интерфейс OS X для соответствия, какие пользователи Mac ожидают без раздражения пользователей UNIX.

Хороший способ реализовать это состоит в том, чтобы записать Ваш GUI для работы в отдельном потоке (или многократных потоках, при желании). Это может тогда связаться с ядром приложения с помощью каналов или других специфичных для платформы решений.

Другой способ реализовать это состоит в том, чтобы просто иметь GUI, вызывают функции в ядре кода. Этот проект является эффективным, когда ядро приложения является полностью управляемым GUI, но имеет тенденцию приводить к GUI, кажущемуся втиснуть, если ядро приложения может занять много времени для завершения работы и особенно твердо реализовать, если базовый код должен сделать что-то постоянно в фоновом режиме во время нормального функционирования. Необходимо полагать, что эти проблемы в модернизации подают заявку, более приемлемую в конечных пользователей.

Другие общие правила

Не попадайте в прерывание по абстракции Вашего кода. Если блок кода не будет использоваться в многократных местах, не абстрагируйте его, если это не специфично для платформы. Абстракция кодирует, который появляется в двух или трех местах, по причинам удобочитаемости, которые обычно не стоит разделить на отдельную функцию.

Действительно попытайтесь ограничить свои функции разумной длиной. Абстракция для одной только удобочитаемости является, вероятно, не хорошей идеей, поскольку разделение функции излишне может закончить тем, что делало его тяжелее для чтения. Если цель функции не делится очевидным способом на многократные подзадачи, вероятно, лучше не разделить его. Во многих случаях, тем не менее, можно найти, что внутренний цикл в той функции можно считать отличной работой к себе. Если так, тот цикл может быть разделен на отдельную функцию, если выполнение так улучшает удобочитаемость.

Не делайте уровни абстракции больше чем пятью или шестью уровнями глубоко, как измерено от верхнего уровня главного функционального блока. Чем глубже вложение, тем тяжелее это должно следовать за кодом при отладке.

Не делайте функции короче, чем приблизительно десять строк кода. Улучшение удобочитаемости такой мелочи во внешней функции является редко значительным. Существуют, конечно, исключения, такие как встроенный ассемблерный код (который должен всегда абстрагироваться). Эти исключения являются хорошими кандидатами на подставляемую функцию (или даже макрос, если Вы так склонны).

Помните, что это инструкции, не правила. Необходимо обычно следовать инстинктами. Если Вы чувствуете, что что-то могло бы быть допускающим повторное использование, разрешение и абстрагировать его, даже если оно используется только в одном месте. Если Вы чувствуете, что разделение внутреннего цикла в его собственную функцию бессмысленно, не разделяйте его. Это применяется ко всем инструкциям, не только тем в этой главе.

Используя плагины и библиотеки эффективно

Плагины могут быть эффективным способом минимизировать специфичный для платформы код в ядре приложения. В этом разделе описываются места, где плагины являются надлежащими. Специфические особенности записи плагинов для OS X описаны подробно в Динамических Библиотеках и Плагинах.

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

Если у Вас есть только несколько специфичных для платформы битов, вероятно, достаточно иметь единственный плагин на платформу. Хорошее место для такого проекта находится в приложении, которое должно, например, поддерживать единственную функцию и в Mac OS 9 и в OS X. Путем изоляции этого блока кода во внешний модуль надлежащая часть может быть загружена согласно платформе, в которой запускается приложение.

Другой подход должен иметь отдельный плагин для каждой отличной службы от операционной системы. Если число служб является значительным, это особенно эффективно. Разделение служб в отдельные модули делает отладку каждой службы проще с точки зрения управления версиями.

Разработка сменной архитектуры

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

Обеспечение, что архитектура устойчива, является ответственностью конструкторов и выходит за рамки любого документа проекта. Необходимо рассмотреть расширяемость и простоту рано и часто во время процесса проектирования.

Простота

В целом, чем более простой сменный API, тем более вероятные люди будут использовать его вместо того, чтобы привить взломы в другие части кода. Конечно, это становится проблемой, если Вам не удается представить функции, которые необходимы для данного плагина. Для решения этой проблемы разработайте API, базируемый вокруг понятий передачи сообщений, а не вызовов функции. Тем путем можно легко добавить дополнительные типы сообщений к API, не изменяя API самостоятельно.

Конечно, если Ваш сменный API только должен обработать один определенный тип коммуникации, и Вы относительно уверены, что это не изменится, сменную архитектуру на основе функций несколько проще использовать. Как со всеми проектами, существуют компромиссы.

Расширяемость

Расширяемость в проекте API является щекотливым вопросом. При создании сменной архитектуры в целом можно редко предполагать виды плагинов, которые могли бы в конечном счете быть полезными. Кто-то мог бы хотеть, чтобы плагины добавили дополнительную функциональность, о которой никто даже не думал, намного меньше изобретенный, когда Вы создали архитектуру

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

Первый шаг в разработке сменного API для абстракции службы должен выбрать уровень абстракции, при которой концы приложения и плагин начинается. Если Вы выберете уровень, который слишком близок к приложению, то плагины будут содержать избыточный код. Если Вы выберете уровень, слишком далеко удаленный, то необходимо будет в конечном счете соединить интерфейсом приложением со средой, не оправдывающей начальные надежды. В результате интерфейсный код для того модуля станет чрезмерно сложным. В любом случае перезапись, вероятно, в порядке, который напрасно тратит время и ресурсы, которые, возможно, были сохранены, имел Вас выбранный правильный уровень абстракции для начала.

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

  • Никакие предположения не сделаны о структурах данных базовой службы.

  • Возвращаемые значения стандартизированы в пути, удовлетворяющем потребности Вашего приложения.

  • Соглашение о вызовах может быть реализовано в любой системе, предоставляющей поддержку, необходимую для Вашего приложения для функционирования — т.е. никакие специальные функции базовой службы не должны подразумеваться на сменном уровне API или в приложении.

  • Типы данных передали, и от сменного API один из двух видов: или базовые типы в предпочтительном языке программирования, или составляют композит тех базовых типов, структурно относящихся к приложению в целом, а не к базовой службе.

Разработка сменного API этим способом может, однако, привести к существенному дублированию кода. В таких средах вложенный сменный подход может быть более практичным.

Вложенные модули для совместно используемой функциональности

Понятие вложенных плагинов является прямым. Необходимо рассмотреть использование вложенных плагинов при нахождении многочисленных возможностей для деления приложения от его плагинов — т.е. при контакте с общим классом базовых служб, разделенных на многие подклассы, содержащие многократные определенные варианты с общими характеристиками.

Вложенные модули удобны для:

  • Аутентификация — универсальный уровень аутентификации с универсальным плагином для основанных на UNIX систем и определенными плагинами ниже для обработки специфичных для платформы изменений

  • Базы данных — на верхнем уровне, у Вас могли бы быть ODBC, JDBC, STET и SQL, с подмодулями для другого, более определенных реализаций SQL

  • Распечатывая — например, у Вас мог бы быть плагин, обрабатывающий принтеры PostScript с более узкими плагинами, переопределяющими универсальные предположения для определенной модели принтера

Короче говоря, каждый раз, когда у Вас есть группа базовых служб, которые существенно подобны в поведении, но где Вы хотите быть в состоянии к также услугам по поддержке с существенно различным поведением, вложенный сменный подход является эффективным проектом (если различия в поведении на самом низком уровне не очень минимальны).

Пример: поддержка БД

Хорошим примером сменного проекта API является доступ к базе данных. Как пример, рассмотрите проект, использующий базу данных для получения информации о списке воспроизведения песни.

Такая программа требует различных определенных данных от базы данных. Для данной песни этому, возможно, понадобились бы заголовок, художник, общая длина, введение (разговор-) время, время, в которое следующая песня должна начать играть (триггерное время) и время, в которое песня должна постепенно исчезнуться, если применимо. Кроме того, поле комментариев могло быть включено для композитора или другой специфичной для жанра информации. Это упоминается как внутренний уровень данных.

Для упрощения поддержки произвольных баз данных Вы добавляете первый уровень абстракции на внутреннем уровне данных. Базовый код вызывает функции с именами как getsongname и getsongtrigger. Любая произвольная база данных, независимо от используемого языка запросов, может легко возвратить строку или целое число (или в худшем случае, быть принуждена в выполнение так). Это рассматривают, минимальный уровень функциональности должен был поддерживать это приложение, и таким образом формирует первое сменное разделение.

Однако удивительное число баз данных использует общий синтаксис, SQL, для подачи с запросами. Несмотря на то, что синтаксис является тем же, определенные подробные данные (типы данных) отличаются, и библиотеки, пользовавшиеся для доступа к ним, также отличаются. По этим причинам поддержка многократных SQL-серверов является желательной целью, потому что можно использовать наиболее распространенные базы данных в качестве хранения.

Поскольку сами инструкции SQL так подобны между базами данных, второе разделение прибывает несколько естественно. Код для фактического выполнения запроса может быть помещен в специфичный для реализации модуль, и код для генерации запроса — для имени песни, например — может быть помещен в универсальный модуль языка SQL. API для специфичного для реализации модуля мог, например, включать:

  • getdbc— Возвращает указатель на объект соединения с базой данных. Код ядра SQL обрабатывает это как непрозрачный тип, но это необходимо, чтобы позволить многим реализациям базы данных использоваться в многопоточной среде. Эта информация не должна оставлять ядро SQL, если больший проект не требует соединений с многократными базами данных одновременно.

  • opendb— Открывает соединение с базой данных.

  • closedb— Закрывает соединение с базой данных.

  • dbquerystring— Выполняет SQL-запрос и возвращает первый результат как строку.

  • dblistsongs— Возвращает массив песни IDs, соответствующий SQL-запрос.

Это могло бы казаться Вам нечетный, что этот проект не указывает универсальный вызов SQL-запроса. Это - в основном пространственно-временной компромисс. Реализация универсального запроса позволила бы GUI, например, запрашивать всю информацию о вызове в единственном запросе. Однако выполнение так добавило бы много кода для относительно маленького преимущества времени. Так как GUI не должен часто обновляться, и так как число запросов в секунду, поэтому, имеет тенденцию быть относительно маленьким, оно целесообразно разрабатывать API, чтобы быть максимально простым. Если низкая задержка и высокая пропускная способность требуются для определенного приложения, универсальная подпрограмма желательна.

Если Вы знакомы с SQL, тем не менее, Вы, вероятно, знаете, что общие черты среди реализаций SQL заканчиваются при вставке новых таблиц в базу данных. Поскольку это - такая маленькая часть общей картины для этого типа приложения (одна или две строки общего количества кода), это могло легко быть «специально в корпусе» в базовом коде или (предпочтительно) в универсальном коде SQL. Это - один из тех случаев, где необходимо решить, стоит ли дополнительный 1% функциональной абстракции дополнительного включенного усилия. Во многих случаях это не.

Если составление таблиц является более значительным, Вы могли бы создать их одним из двух способов: путем добавления функции в специфичном для реализации плагине, или путем добавления функции в универсальном плагине SQL, использования таблицы для типов данных (который мог при желании находиться в специфичном для реализации плагине).

Для демонстрации эффективности этого проекта поддержка БД MySQL была добавлена к приложению, использовавшему этот точный проект за приблизительно час. За исключением табличного создания, не требовались никакие модификации к универсальным подпрограммам поддержки SQL.

Таким образом, надлежащий сменный проект во многом как надлежащая абстракция. Код, который может быть снова использован, должен быть снова использован, но API к плагинам не должен принимать знание базовой системы.

Мобильность инструмента командной строки

Если Ваше программное обеспечение зависит от инструментов командной строки, поставляющих как часть операционной системы, необходимо знать о различиях во флагах командной строки и поведении. Поведение некоторых инструментов командной строки варьируется не только через платформы, но также и через различные версии OS X.

Для обширного объяснения этих различий см. Сценарии Разработки для Межплатформенного Развертывания.

Архитектурная мобильность

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

При обнаружении с кодом, определенным для OS X с архитектурными зависимостями от определенной архитектуры процессора существует много способов обработать ситуацию. Вот некоторые подсказки, которые должны помочь:

Для более подробной информации посмотрите Компиляцию для Многократных архитектур ЦП и документа Универсальные Двоичные Инструкции по Программированию, Второй Выпуск.