Сложные и запутанные участки кода 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Сложные и запутанные участки кода



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

Дублирование кода

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

Ошибкоопасные места

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

Лишние участки кода

Кажется, что эти участки кода никому не мешают, однако они затрудняют понимание кода, да и просто мешают читать код. Например, если появляются неиспользуемые методы или классы, то их нужно выкидывать (помечать как комментарии).

 

Проблемы с именованием

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

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

Преимущества рефакторинга:

1) Рефакторинг, ведет к более ясному пониманию кода

Это позволяет увидеть возможности дальнейшего улучшения и пути дальнейшего развития системы.

2) Рефакторинг улучшает архитектуру системы

В процессе рефакторинга происходит развитие системы. Система развивается естественным образом, выделяя новые части в процессе рефакторинга.

3) Рефакторинг позволяет избежать ошибок

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

4) Рефакторинг улучшает обмен знаниями в команде

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

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

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

Идею рефакторинга хорошо демонстрирует притча о двух дровосеках. Два дровосека поспорили о том, кто из них нарубит больше дров за день. Рано утром они усиленно начали рубить лес. Один из дровосеков вдруг услышал, что второй перестал рубить и с удвоенной силой продолжал рубить, чтобы обогнать первого. Но в результате первый дровосек нарубил намного больше. «Как тебе это удалось? Что ты делал, когда переставал рубить?» - спросил второй. Первый ответил: «Отдыхал и затачивал топор».

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


Модульные тесты

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

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

Однако этот способ тестирования обладает рядом недостатков:

1. Тестируется только «внешняя часть системы», близкая к пользовательскому интерфейсу. Ошибки во внутренних механизмах системы при таком способе тестирования отследить достаточно сложно.

2. Отследить все возможные ситуации практически невозможно. И чем сложнее система – тем серьезнее эта проблема. Поскольку система тестируется только целиком, то количество ситуаций – это количество всех возможных комбинаций ситуаций в разных частях системы.

3. Такое тестирование отнимает много времени, поскольку выполняется вручную.

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

Модульные тесты позволяют решить эти проблемы. Идея модульного тестирования заключается в создании автоматических тестов, которые тестируют внутренние части системы. Т.е. программа тестирует сама себя.

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

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

Тесты объединяются в набор тестов (Test Fixture), который можно запускать автоматически после каждого внесения изменений. Если один из тестов перестает срабатывать, легко разобраться в чем причина проблемы и устранить ошибку.

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

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


Использование модульных тестов дает множество преимуществ. Модульные тесты:

1. Позволяют на ранних стадиях разработки отлавливать ошибки во внутренних частях системы

2. Обеспечивают обратную связь, т.е. позволяют сразу же проверить работоспособность только что написанного кода, не дожидаясь готовности других частей.

3. Повышают уверенность в разрабатываемом коде, т.к. его работоспособность подтверждается автоматическими тестами.

4. Позволяют вносить изменения в код и выполнять рефакторинг, не опасаясь появления ошибок.

5. Документируют модуль, т.к. модульные тесты являются примерами использования данного модуля.

6. Уменьшают зависимость задач по разработке. При использовании модульных тестов, любой модуль системы можно начинать разрабатывать в любой момент, не дожидаясь готовности остальных модулей. Это дает большую гибкость при планировании разработки и распределении задач между разработчиками.

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

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

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


Разработка через тестирование

Использование модульных тестов позволяет по-новому взглянуть на процесс разработки и использовать подход, называемый разработкой через тестирование (test driven development, TDD). Суть этого подхода заключается в том, что модульные тесты пишутся до того, как написан код, которые они тестируют.

При этом разработка происходит по следующему алгоритму:

1. Написать тест

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

2. Заставить тест работать

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

3. Рефакторинг

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

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

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

Цель написания кода – чистый и качественный код, который работает. Но достигнуть этой цели можно по- разному.

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

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

 


Принцип ортогональности

Одна из основных проблем разработки программного обеспечения – это сложность создаваемых систем, а один из основных способов борьбы с этой борьбы – декомпозиция – разбиение сложной системы на более простые части. Однако просто разбить систему на части недостаточно. Например, вы можете разбить программу из 10000 строк кода на блоки по 100 строк, но это не уменьшит ее сложности. Важно, чтобы части системы были логически цельными и как можно более независимыми друг от друга. Авторы книги «Программист прагматик» назвали такую независимость частей системы друг от друга – ортогональностью.

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

Зависимость проявляется в том, что одна часть системы полагается на то, что какая-то другая часть системы устроена определенным образом или вообще существует в системе. Также зависимость проявляется в том, что одна часть системы слишком много «знает» о внутреннем устройстве другой части системы. Это приводит к следующим проблемам:

1. Сложность системы недостаточно уменьшается

2. Ошибка в одной части системы может привести к ошибке в другой части системы

3. Изменение одной части системы требует изменения всех зависящих от нее частей

4. Сложно повторно использовать какие-то части системы, т.к. они слишком связаны с другими частями

5. Сложно поделить задачи между программистами при командной разработке

6. Зависимости ведут к другим зависимостям, что еще больше усложняет систему

7. Зависимости не позволяют увидеть структуру системы и пути ее улучшения

 

Рассмотрим пример. Пусть есть класс, реализующий список. Для вывода на экран в этом модуле используется метод:

   public void PrintList()

   {

       XElement zElement = _first;

       while (zElement!= null)

       {

           Console.WriteLine(pElement.Data.ToString());

           zElement = zElement.Next;

       }

   }

 

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

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

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

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

   public delegate void ProcessElementDataDelegate(object data);

 

   public void ProcessList

(ProcessElementDataDelegate processElementDataDelegate)

   {

   XElement zElement = _first;

   while (zElement!= null)

       {

          processElementDataDelegate(zElement.Data);

       zElement = zElement.Next;

       }

   }

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

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

Вообще, отделение представления данных от самих данных – это типичная задача декомпозиции, для решения которой придумана архитектура Модель-Вид-Контроллер (MVC – Model, View, Controller). Идея этой архитектуры заключается в разделении системы на три части:

Модель – занимается хранением и обработкой данных и обеспечением доступа к ним. Не зависит от способа представления данных пользователю и способа ввода данных пользователем.

Вид – отвечает за представление данных пользователю.

Контроллер – реагирует на действия пользователя и, соответствующим образом изменяет модель.

Контроллер и Вид часто бывают слиты в один модуль, занимающийся взаимодействием с пользователем.

Метапрограммирование

Языки программирования можно использовать не только для написания непосредственно программ, которые решают определенную задачу, но и для поддержки написания этих программ и разных служебных целей. Такой подход называют метапрограммированием. Примером метопрограммирования являются, например модульные тесты. Они непосредственно не решают поставленную задачу (код тестов даже не включается в код финального продукта), но позволяют обеспечить тестирование основной программы. Другой пример – разного рода генераторы данных и заглушки, которые помогают в разработке.

Еще один интересный способ применения метапрограммирования – генераторы кода – программы, которые генерируют код для основной программы, например классы для взаимодействия с базой данных, разные классы по шаблонам и т.п. 

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

Язык XML

Возникновение XML

В настоящее время основным стандартом представления информации в сети интернет является язык HTML (Hypertext Markup Language – язык разметки гипертекста). Он был построен на основе метаязыка разметки текста - SGML (Standart Generalised Markup Language), созданного издателями еще в 80-е годы. Выбор HTML в качестве стандарта объяснялся его простотой и удобством по сравнению с SGML, и в эпоху зарождения интернета этот выбор был оправдан. По сути, HTML-документ представляет собой просто текст с описанием способов его отображения и связями между документами. Однако HTML ничего не говорит о смысле того, что на нем описано. Текст структурирован только с точки зрения отображения, а не семантики. 

С развитием интернета появилась необходимость отделить данные от способа их отображения. Частично эта задача была решена с помощью каскадных таблиц стилей (CSS). Появилась необходимость создания динамически изменяющихся и интерактивных страниц, манипуляции гипертекстом. Эта задача была решена с помощью создания объектной модели документа (DOM), скриптовых языков, языков веб-программирования и технологии Flash. Сам язык HTML тоже развивается. Однако дальнейшее развитие этих направлений упирается в саму структуру языка HTML.

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

Способом решения указанных проблем является использование нового языка представления данных, который мог бы прийти на смену HTML, стать стандартом обмена данными через интернет и использоваться в других областях. Таким решением является XML.

XML

XML расшифровывается как Extensible Markup Language – Расширяемый Язык Разметки. Он является некоторым возвратом к SGML, поскольку тоже является метаязыком - более простым, чем SGML, но более универсальным, чем HTML. HTML является подмножеством XML.

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

Рассмотрим пример. Пусть у нас есть таблица с ценами на комплектующие.

Категория Наименование Цена
Процессоры [BOX] AMD Athlon 64 3200+ Venice Socket 939 179.90
Процессоры [BOX] AMD Athlon 64 X2 4200+ Socket 939 440.80
Видеокарты 128Mb PCI-E PCX 6600 GT TV DVI [MSI NX6600GT-TD128E] OEM 156.00
Видеокарты 256Mb PCI-E X800GTO TV DVI [Sapphire part number: 1024-AC60-xx-xx] OEM 225.80
Жесткие диски 120 Gb 7200rpm Maxtor DiamondMax 6Y120L0 74.60
Жесткие диски 120 Gb 7200rpm 8Mb cache Seagate Barracuda 7200.9 ST3120813AS NCQ 93.50

Когда пользователь просматривает такую таблицу в браузере, он может понять, что это прайс-лист. Для компьютера же это просто таблица с тремя колонками, которая на HTML описывается так:


<TABLE CELLPADDING=2 BORDER=0>

<TR>

<TD FACE=verdana>Категория</FONT></TD>

<TD FACE=verdana>Название</FONT></TD>

<TD FACE=verdana>Цена</FONT></TD>

</TR>

<TR>

<TD><FONT FACE=verdana>Процессоры</FONT></TD>

<TD><FONT FACE=verdana>[BOX] AMD Athlon 64 3200+ Venice Socket 939</FONT></TD>

<TD><FONT FACE=verdana COLOR="FF0000">179.90</FONT></TD>

</TR>

</TABLE>

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

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

Теперь посмотрим, как те же данные описывается с помощью XML.

<?xml version="1.0" encoding="utf-16"?>

<PriceList>

<Item Category = "Процессоры">

<Product>[BOX] AMD Athlon 64 3200+ Venice Socket 939</Product>

<Price>179.90</Price>

</Item>

<Item Category = "Процессоры">

<Product>[BOX] AMD Athlon 64 X2 4200+ Socket 939</Product>

<Price>440.80</Price>

</Item>

<Item Category = "Видеокарты">

<Product>128Mb PCI-E PCX 6600 GT TV DVI [MSI NX6600GT-TD128E] OEM </Product>

<Price>156.00</Price>

</Item>

<Item Category = "Видеокарты">

<Product>256Mb PCI-E X800GTO TV DVI [Sapphire part number: 1024-AC60-xx-xx] OEM </Product>

<Price>225.80</Price>

</Item>

<Item Category = "Жесткие диски">

<Product>120 Gb 7200rpm Maxtor DiamondMax 6Y120L0</Product>

<Price>74.60</Price>

</Item>

<Item Category = "Жесткие диски">

<Product>120 Gb 7200rpm 8Mb cache Seagate Barracuda 7200.9 ST3120813AS NCQ </Product>

<Price>93.50</Price>

</Item>

</PriceList>

 

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

Обратите внимание, что категорию товара мы вынесли в атрибут тэга Item. Можно было бы все характеристики товара сделать атрибутами или наоборот, хранить их всех в тэгах. Обычно в атрибуты выносятся характеристики объекта, описываемого тэгом, а во внутренние тэги – данные объекта. Если проводить аналогию с русским языком, то атрибуты – это прилагательные, а внутренние тэги – существительные. Если товары разных категорий будут иметь разный набор характеристик, то, определив категорию в атрибуте, мы можем по ней узнать, сколько тегов должно быть внутри тэга Item и какие они.

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

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

Для связи документов XML между собой и создания ссылок на данные внутри XML-документа созданы технологии XLink, XPath и XPointer.

Поскольку в отличие от HTML, в XML можно использовать свои собственные тэги и атрибуты, то необходимы способы проверки соответствия XML-документа определенному формату. Для этого существуют схемы DTD, XDR и язык определния схем XSD.

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

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

Для обмена данными в современных распределенных системах, основанных на веб-службах (web-services) используется протокол SOAP, основанных на XML.

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

Описав прайс, с помощью XML мы описали структуру данных, но не описали, каким образом их отображать. Это можно сделать различными способами, одним из которых являются таблицы стилей XSL.

Таблицы стилей XSL

Для описания способа отображения данных в XML используются таблицы стилей XLS (Extensible Stylesheet Language – расширяемый язык таблиц стилей). XSL является гораздо более мощной технологией, чем каскадные таблицы стилей (CSS).

XSL состоит из двух языков – XSLT, предназначенного для определения преоблазований и XSL-FO, предназначеггого для описания способа отображения элементов.

XSL-FO во многом повторяет HTML, также описывая специальные теги для указания шрифтов, отображения таблиц и т.п.

XSLT является очень мощным языком, позволяющим, по сути, компилировать XML-текст в любой другой формат: текст, XML-текст другого формата, HTML и т.д. При этом можно группировать и фильтровать элементы и производить над ними различные преобразования. Например, при отображении можно легко выкинуть некоторые столбцы таблицы, выкинуть всю графику со страницы или поменять порядок элементов.

При этом сами данные, хранящиеся в XML, остаются теми же. Благодаря такому подходу можно легко сделать несколько версий страницы на сайте: версию для печати, версии на разных языках, WAP-версию и т.д. Способ отображения документа XML, описываемый XSL, может зависеть от самих данных. Например, можно отображать все отрицательные числовые значения красным цветом. Также имеются возможности использования в XSL каскадных таблиц стилей CSS.

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

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

Рассмотрим пример XSL таблицы стилей для XML-документа с прайсом из предыдущего раздела, которые позволяют отображать его в html:

<?xml version="1.0" encoding="utf-16"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

<xsl:template match="/">

<P><H3>Прайс-лист</H3></P>

<TABLE border="1" cellspacing="0">

<TR>

   <TD><B>Категория</B></TD>

   <TD><B>Продукт</B></TD>

   <TD><B>Цена</B></TD>

</TR>

<xsl:apply-templates select="PriceList/Item"/>

</TABLE>

<BR />

</xsl:template>

<xsl:template match="Item">

<TR>

<TD><xsl:value-of select="@Category" /></TD>

<TD><xsl:value-of select="Product" /></TD>

<TD><xsl:value-of select="Price" /></TD>

</TR>

</xsl:template>

</xsl:stylesheet>

Правила отображения задаются с помощью тэгов xsl:template. Атрибут match этих тэгов указывает шаблон (в формате XPath), который ищется в XML-документе, а содержимое – текст результирующего документа. Тэги xsl:value-of вытаскивают содержимое и атрибутов (которые обозначаются символом @) из тэгов XML-документа (также с помощью XPath).

Чтобы связать xml-документ со схемой, нужно указать в XML-документе тэг:

<?xml-stylesheet type="text/xsl" href="price.xsl"?>

При использовании такой таблицы стилей документ в браузере будет выглядеть следующим образом:

Прайс-лист

Категория Продукт Цена
Процессоры [BOX] AMD Athlon 64 3200+ Venice Socket 939 179.90
Процессоры [BOX] AMD Athlon 64 X2 4200+ Socket 939 440.80
Видеокарты 128Mb PCI-E PCX 6600 GT TV DVI [MSI NX6600GT-TD128E] OEM 156.00
Видеокарты 256Mb PCI-E X800GTO TV DVI [Sapphire part number: 1024-AC60-xx-xx] OEM 225.80
Жесткие диски 120 Gb 7200rpm Maxtor DiamondMax 6Y120L0 74.60
Жесткие диски 120 Gb 7200rpm 8Mb cache Seagate Barracuda 7200.9 ST3120813AS NCQ 93.50

 


Можно написать таблицу стилей, чтобы выводить этот прайс-лист по группам:

<?xml version="1.0" encoding="utf-16"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl">

<xsl:template match="/">

<P><H3>Прайс-лист</H3></P>

<BR><B>Процессоры</B></BR>

<TABLE border="1" cellspacing="0">

<TR>

<TD><B>Наименование</B></TD>

<TD><B>Цена</B></TD>

</TR>

<xsl:apply-templates select="PriceList/Item[@Category='Процессоры']" />

</TABLE>

<BR><B>Видеокарты</B></BR>

<TABLE border="1" cellspacing="0">

<TR>

<TD><B>Наименование</B></TD>

<TD><B>Цена</B></TD>

</TR>

<xsl:apply-templates select="PriceList/Item[@Category='Видеокарты']" />

</TABLE>

<BR><B>Жесткие диски</B></BR>

<TABLE border="1" cellspacing="0">

<TR>

<TD><B>Наименование</B></TD>

<TD><B>Цена</B></TD>

</TR>

<xsl:apply-templates select="PriceList/Item[@Category='Жесткие диски']" />

</TABLE>

<BR />

</xsl:template>

<xsl:template match="Item">

<TR>

<TD><xsl:value-of select="Product" /></TD>

<TD><xsl:value-of select="Price" /></TD>

</TR>

</xsl:template>

</xsl:stylesheet>

В этом случае прайс-лист будет выглядеть так:

Прайс-лист


Процессоры

Наименование Цена
[BOX] AMD Athlon 64 3200+ Venice Socket 939 179.90
[BOX] AMD Athlon 64 X2 4200+ Socket 939 440.80


Видеокарты

Наименование Цена
128Mb PCI-E PCX 6600 GT TV DVI [MSI NX6600GT-TD128E] OEM 156.00
256Mb PCI-E X800GTO TV DVI [Sapphire part number: 1024-AC60-xx-xx] OEM 225.80

 


Жесткие диски

Наименование Цена
120 Gb 7200rpm Maxtor DiamondMax 6Y120L0 74.60
120 Gb 7200rpm 8Mb cache Seagate Barracuda 7200.9 ST3120813AS NCQ 93.50

 

Это только небольшая часть того, что можно делать с помощью XSL.

Программная обработка XML в C#

Для обработки текста XML заранее известного формата совсем не сложно написать собственный обработчик (parser). Однако существуют стандартные средства работы с XML.

Способы работы с XML можно разделить на две больших группы:

1) Объектные модели документа

В этих обработчиках документ загружается в память в виде набора объектов, с помощью методов котоырх можно получать доступ к данным в XML-документе. Самой известной из таких моделей является DOM (Document Object Model), в которой XML-документ представляется в виде дерева.

Объектные модели удобны, когда нужно обеспечивать произвольный доступ к узлам в XML-документе, использовать сложные фильтры, создавать новые XML-документы в памяти.

2) Синтаксические анализаторы, последовательно обрабатывающие документы

Обработчики этой группы последовательно обходят тэги XML-текста, считывают данные и выполняют необходимые действия. Для такой обработки XML-файлов существует стандарт SAX (Simple API for XML).

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

В C# классы для работы с XML находятся в пространстве имен System.Xml

Последовательное чтение Xml файла удобно производить с помощью XmlTextReader. Рассмотрим пример чтения и записи файла этими способами.

public class XPriceListItem

{

   string _category;

 

   public string Category

   {

       get { return _category; }

       set { _category = value; }

   }

 

   string _name;

 

   public string Name

   {

       get { return _name; }

       set { _name = value; }

   }

 

   decimal _price;

 

   public decimal Price

   {

       get { return _price; }

       set { _price = value; }

   }

 

   public XPriceListItem(XmlReader r)

   {

       if (r.LocalName == "Item")

       {

           _category = r.GetAttribute("Category");

           // Читаем открывающий тэг Item

           r.ReadStartElement();

           // Читаем название товара как строку

           _name = r.ReadElementString();

           // А цену, как число

           _price = r.ReadElementContentAsDecimal();



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 31; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.17.150.163 (0.311 с.)