Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

GrandM-Patterns_in_Java

.pdf
Скачиваний:
98
Добавлен:
14.03.2016
Размер:
8.88 Mб
Скачать

428Глава 8. Поведенческие шаблоны проектироваНИR

* @param authToken

* Этот объект возвращает метод аутентификации .

* / abstract

protected void notifyAuthentication (Object authToken)

// class Abs t ractLogon

Подклассы класса A b s t ractLogon должны замещать абстрактные методы это­ го класса примерно таким образом:

public class Logon extends AЬstractLogon

protected Object authenticate (String userID , String password)

throws Exception

if (userID . equals ("abc") && password . equals ("123"» return userID ;

throw new Exception ("bad userID") ;

/ / authent icate

protected void notifyAuthentication (Object authToken) (

// noti fy (Obj ect)

// class Logon

ШАБЛОН ПРОЕКТИРОВАНИЯ, СВЯЗАННЫЙ С ШАБЛОНОМ TEMPLATE METHOD

Strategy. Шаблон Strategy изменяет логику отдельных объектов на стадии вы­

полнения. Шаблон Template Method изменяет логику всего класса на стадии

компиляции.

Этот шаблон ранее был описан в работе [GoF95].

СИНОПСИС

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

КОНТЕКСТ

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

щью диалогового окна пользователь может определить стили заголовков, кото­ рые будут вынесены в оглавление.

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

таблица будет называться внутренней таблицей оглавления (internal ТоС tabIe). Каждая строка этой таблицы содержит номер уровня, соответствующего главе,

разделу, подразделу или любой другой иерархической структуре, которая зада­

етсяи пользователем. Кроме того, строки таблицы будут включать стиль абзаца

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

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

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

таблицы оглавления, необходимо обеспечить следующие, связанные с оглавле­

нием, характеристики:

возможность создавать и вставлять таблицу оглавления в документ, кото­

рый представлен одним файлом документа; и

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

его в виде

заголовков

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

430 Глава 8. Поведенческие шаблоны проектирования

Поскольку такие операции предполагают манипулирование документом тек­

стового процессора, любой проект, реализующий характеристики оглавления,

должен будет содержать классы, используемые текстовым процессором для

представления документов (рис. 8.31).

 

 

 

 

 

 

l'

О..*

 

 

 

 

 

 

 

«interface»

 

 

 

 

 

 

 

 

DocumentElementIF

 

 

 

 

 

 

 

 

 

 

getStyle. ..

 

I!&

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

_-------- - - - - - - - - - - -- - - - - ----------- - - - - ,

1 1

1

 

 

I

 

 

 

 

 

 

 

I

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DocChar

 

 

 

 

 

 

 

CompositeOocumentElement

 

 

 

 

 

 

 

 

 

 

getChild(index:int) : DocumentElement

 

 

 

 

 

 

 

 

 

addChild(child:DocumentElement)

 

 

 

 

 

 

 

 

 

 

removeChild(child:DocumentElement)

 

 

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Document

 

 

 

 

Paragraph

 

 

 

LineOfТext

 

 

 

 

Рис. 8.31.

Классы документа

 

 

 

 

С точки зрения механизма оглавления интерес представляют следующие клас­ сы: Document, Paragraph и LineOfText. Документ содержит абзацы, которые включают строки текста. Любой проект, предполагающий создание оглавле­ ния, должен учитывать то, что объекты Document могуг содержать объекты, не являющиеся объектами Paragraph. Кроме того, необходимо принимать во

внимание, что содержать объекты Paragraph могуг не только объекты Docu­ ment, но И другие виды объектов. И наконец, проект не должен усложнять

классы, представляющие документы.

При реализации характеристик оглавления нужно рассмотреть два основныХ

подхода. Один предусматривает размещение необходимой логики в различных

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

в разделе « Синопсис», это не лучшее решение.

Другой подход предполагает размещение всей логики, связанной с какой-тО

операцией, в одном классе. По завершении связанной с оглавлением операциИ

объект, отвечающий за операцию, просматривает объект Document И содержа­

щиеся в нем объекты. Он ищет объекты Paragraph, которые содержатся не­

посредственно в объекте Document. Когда он находит объекты Paragraph со

стилем , который хранится во внутренней таблице оглавления , управляющей

связанной с оглавлением операцией, он предпринимает необходимые действИЯ

(рис. 8.32). Структура, показанная на рис. 8.32, использует также классы, изо­

браженные на рис. 8.31.

 

 

 

 

 

 

 

 

 

1. о. .*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Visitor

-

431

 

 

I

 

«interface»

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

*'

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DocumentELementIFJ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Н

1

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1- - - - - - - _ .1J- --- -

 

 

 

--- -

 

--

 

 

-1

 

 

'

 

 

 

 

 

l'L

0

 

 

 

 

1

 

1

 

 

-- --

 

- --

 

-

-

 

 

 

 

 

 

 

 

• • *

 

 

 

1

 

 

 

 

 

 

ComposlteDocumentElement

 

 

 

I

I

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

DocChar

0

• • * J

 

 

 

 

 

1

 

 

 

I

 

 

 

 

 

I

 

1

I

 

1

 

 

 

 

 

 

 

 

 

 

 

 

Paragraph

 

 

 

 

 

 

 

 

 

 

Document

 

 

 

 

*

 

LineOfТext

 

 

 

 

 

 

 

 

 

 

 

0

 

 

 

0• • *

 

 

 

0• •

 

0• •

*

 

 

 

.....Редактирует

СоздаеТ.....• • *

 

 

 

 

 

 

 

.....Использует

 

 

 

 

 

 

 

 

 

 

исполЬзуеТ.....

 

 

 

 

 

 

.....спольИ зует

1

1

 

 

1

WordProcessor

 

1

1

1

 

 

 

 

 

 

 

 

 

 

Получает

 

 

1

1

1

 

 

ReorgVisitor

 

 

roCVisitor

 

 

Ij

1

 

 

 

1

1

 

 

 

 

 

 

Получает

 

 

 

17

 

 

 

исполЬзует.....

...

 

DocumentVisitor

 

1

 

getNexParagraphO:Paragraph

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 8.32. Классы оглавления

Рассмотрим следуюшие классы.

WordProcessor. Класс WordProces sor отвечает за создание и редактирование объектов, представляюших документ. Для редактирования документа он ис­

пользует другие клаССЫ1 указанные на диаграмме.

DocumentVisitor. Это абстрактный класс. Его подклассы исследуют объекты, Образуюшие документ, с целью создания оглавления или реорганизации доку­ мsiента для представления его в виде нескольких файлов. Класс DocumentVi ­ tor обеспечивает логику, используемую его подклассами для навигации по

этим объектам.

Идея такова, что экземпляры подклассов класса DocumentVis i tor «(посеша­ !от» (обрашаются к) объекты, входящие в состав документа, собирают и обраба­ Тывают информацию от каждого объекта.

TOCVisitor. Этот подкласс класса DocumentVisi tor отвечает за создание ог­

лавления. Он просматривает каждый объект Parag raph , которым непосредст­

венно владеет объект Document80 . Когда он находит объект Paragrap h , который имеет стиль, храняшийся внутренней таблице оглавления, он создает соот-

432 Глава 8. Поведенческие шаблоны проектирования

ветствующий элемент оглавления. Элемент оглавления использует содеРЖИМое

первогО объекта LineOfText, принадлежащего объекту Paragraph.

ReorgVisitor. Этот подкласс класса DocumentVi s i tor отвечает за автомаТИче­

ское разделение документа на несколько файлов. Сначала ему сообщают, что­ бы он осуществил поиск абзацев, соответствующих определенному УРОВню

структуры документа. Он находит этот уровень структуры во внутренней табли­

це оглавления и считывает из нее стиль, связанный с этим уровнем структуры.

Затем он просматривает все объекты Paragraph, принадлежащие объекту

Document. Он ищет те объекты Paragraph, которые имеют стиль, прочитан­

ный им из таблицы. Когда ReorgVis i tor находит объект Paragraph с нужным

стилем, он создает новый объект Document, перемещает найденный объект

Paragraph вместе со всеми объектами Paragraph, непосредственно следую­

щими за ним и находящимися на более низком уровне структуры, во вновь соз­ данный объект Document. Класс ReorgVi s i tor записывает в файл новый объ­ ект Document И все объекты абзацев, связанные теперь с ним. Он заменяет

объекты Paragraph, которые он переместил из исходного объекта Document, новым объектом, содержащим имя файла, в котором теперь хранятся переме­ щенные абзацы.

МОТИВЫ

©Имеются различные операции, которые должны выполняться для некото­ рой структуры объектов.

©Структуру образуют объекты, принадлежащие разным классам.

©Типы объектов структуры меняются не часто, а их объединения являются

согласованными и предсказуемыми.

РЕШЕНИЕ

в этом разделе рассматриваются две версии шаблона Visitor. Первая представ­

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

фективно. Вторая версия шаблона Visitor применима для множества разных

ситуаций, но связана с издержками из-за ввода дополнительных зависимоСтей

между классами.

На рис. 8.33 представлена диаграмма классов, демонстрирующая роли, испоЛ­

няемые классами в идеальной версии шаблона Visitor.

Опишем роли, исполняемые такими классами.

Client. Экземпляры классов в этой роли предназначены для манипулированиЯ всей структурой объектов и объектами, входяшими в состав этой структурЫ.

С целью выполнения вычислений для структур объектов, за которые они отве­

чают, они используют объекты ConcreteVis itor.

Visitor 433

visit(:Elementl)

 

 

 

 

Использует

 

 

 

 

Visitor

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

vis it(:Elementz)

 

1

 

 

 

 

 

1

. . .

 

 

 

 

 

 

 

 

 

I

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

СопсгеtеЕlеmепt1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

accept(:AbstractVisitor)

 

 

 

 

 

 

 

 

 

 

 

. . .

 

 

 

 

 

 

I

 

 

 

 

 

 

L

 

 

 

I

 

 

 

 

 

 

 

 

 

ConcreteVisitor1

 

ConcreteVisitor2

 

.

. .

visit(:Element1)

 

 

visit(:Element1)

 

 

 

visit(:Element2)

 

 

visit(:Element2)

 

 

 

...

 

 

 

 

 

. ..

 

 

 

 

 

 

1

 

....Использует

 

 

 

1

1

 

 

 

1

 

Client 1

 

 

Использует

 

 

 

 

 

1

I

1

 

 

 

 

Использует....

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

AbstractElement

0. . *

 

accept(:AbstractVisitor). ..

 

 

 

Содержит....

ConcreteElement2

 

. l .

 

 

 

 

 

 

accept(. . .

:AbstractVisitor)

 

1

( )

 

 

 

 

 

 

 

 

 

ObjectStructureI

 

 

 

0• . *

 

Использует....

Рис. 8.33. Идеальная версия шаблона Visitor

ObjectStructure. Экземпляр класса в этой роли служит корневым объектом структуры объектов. « Посещая» объекты некоторой структуры, объект Vis i tor начинает с экземпляра класса Obj ectStructure, а затем перемещается по дру­ гим объектам этой структуры.

Некоторый класс может участвовать в шаблоне Visitor в роли Obj ectStruc­ ture, а также исполнять роль ConcreteElement (в качестве элемента структуры

объектов).

AbstractElement. Класс в этой роли представляет собой абстрактный супер­ еткласс для объектов, которые входят в состав структуры объектов. Он определя­ абстрактный метод, который на рис. 8.33 указан под названием accept. В ка­ честве apryмeHTa он принимает объект AbstractVisitor. Подклассы класса

AbstractElement обеспечивают реализацию метода accept, который вызыва­

ет метод объекта Abs tractVi s i tor, а затем передает объект Abs tractVisi tor

методу accept других объектов AbstractEи lement.

ConcreteElementl, ConcreteElement2 т.д. Экземпляры классов в этой роли представляют собой элементы структуры объектов. При выполнении вычислений дЛя объектов структуры объект Abs tractVi s i tor передается методу accept

объекта ConcreteElement. Метод accept передает объект ConcreteElement

одному из методов объекта AbstractVi s i tor, поэтому он может включить

434 Глава 8. Поведенческие шаблоны проектирования

всвои вычисления объект ConcreteElement. По окончании операции объект

ConcreteEl ement передает объект AbstractVi s itor методу accept ДРУГОго

объекта ConcreteElement.

Visitor. Выстудля пающий в этой роли класс представляет собой абстрактный су­ перкласс классов, выплняющихдля x вычисления над элементами структуры

объектов. Он определяет метод каждого класса, который будут «посещатЪ» его подклассы, поэтому их экземпляры могут передавать сами себя объектам

Vis i tor и принимать участие в вычисленияхи Т.д. .

ConcreteVisitorl, ConcreteVisitor2 Экземпляры классов, выступающих

в этой роли, образуют структуру объектов.

На рис. 8.34 представлена диаграмма взаимодействия, более очевИдНО демонст­

рирующая то, как объекты Vis i tor сотрудничают с объектными структурами.

На этом рисунке показано взаимодействие между объектом Vi s itor и элемен­

тамиj структуры объектов. После того как объект Vi s itor представлен объекту

Ob ectS tructure, объект Obj ectStructure передает объект Visi tor методу accept объекта ConcreteE lement. Объект ConcreteElement передает сам себя методу v i s i t объекта Visitor, что позволяет этому объекту включить объект Vi s i tor в свои вычисления. Затем объект ConcreteElement передает объект Vi s itor другому объекту ConcreteElement , поэтому объект Vis itor может «посетить» его. При передаче объекта Vi s i tor другим объектам Соп­ creteElement цикл продолжается. Объект ConcreteElement может быть свя­ зан с любым количеством других объектов ConcreteElement. Он может пере­ давать объект Vis i tor нескольким, всем или ни одному связанному с ним объекту ConcreteElement.

I jObjectStructure I

 

1:accePt(v)

 

 

.2:-.

 

 

 

 

1

 

 

 

 

 

I

 

 

 

 

accept(v)

 

 

 

еl:СопсгеtеЕlemепt1

:1-------------------1:J

 

 

 

 

 

е2:СопсгеtеElеmепtZ

 

 

1.1: visit(e1)

L.._____

------

1.2.1: visщеl)1

 

1

 

 

Ir-

...Г'L --,--------J

+

 

 

 

 

 

 

 

 

 

 

Рис. 8.34.

Взаимодействие в идеальной версии шаблона Visitor

 

В этой версии шаблона Visitor объекты AbstractElement определяют, какие элементы структуры объектов «посещают» объект Vis itor и каков порЯдок

этих «посещений». Данная версия хорошо работает в тех случаях, когда объепКо­

ты Visi tor любого вида, «посещая» элементы структуры объектов, следуют

одному и тому же маршруту. Преимуществоот здесь состоит в том, что класСЫ

Visi tor поддерживаются независимо способа организации структуры объ-

Visitoг 435

ектов. Однако в некоторых ситуациях эта версия не работает. Это следующие ситуации.

Появляются посетители, изменяющие структуру объектов.

Описанный

в разделе «КонтексТ» пример объекта Vis itor, разделяющего документ на

несколько файлов, - это именно такая ситуация.

 

Структуры объектов слишком велики, и, чтобы <mосетить»

все объекты,

 

объекту Vi s i tor потребуется недопустимо много времени,

тогда как он

должен <mосетить» только некоторые из объектовдля структуры.

На рис. 8.35 представлена диаграмма классов другой версии шаблона Visitor. В этой версии шаблона Visitor классы Vis i tor отвечают за навигацию по структуре объектов в соответствии со своим собственным маршрутом. Орга­ низованные таким образом посетители способны изменять структуру объектов или выборочно перемещаться по ней. Недостатком подобной организации яв­

ляется то, что классы Visi tor не могут быть многократно используемыми, по­ скольку для перемещения по структуре объектов они должны делать некоторые

предположения, касающиеся этой структуры.

 

 

Cllent

1

 

 

спользует

 

 

 

О. .* ..

ObjectStructure

 

I

 

 

 

 

 

 

 

 

 

 

 

 

1

 

1

 

 

 

 

 

 

 

 

'1

 

 

 

 

1 1

Использует

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

исП лЬ3ует

 

1 1

 

 

,

 

содержит.....

 

 

 

 

 

 

О

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.....

 

 

I I

ConcreteVisitor2

I

. . .

 

 

 

 

 

 

 

 

 

 

ConcreteVisitor1

 

 

 

 

 

 

 

 

 

 

1

 

 

 

1

 

 

I

 

 

 

 

 

0. .*

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Перемещает

 

 

 

I

 

 

 

 

 

 

 

 

Visitor

 

 

 

"1 AbstroctElement

 

 

 

#navigationOperation1( ):AbstractElement

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

t

 

 

 

 

#navigationOperation2( ):AbstractElement

 

 

 

1

 

 

 

 

 

. ! .

 

 

 

 

 

 

 

 

 

 

 

I

ConcreteElement1

I I concreteElement2

1

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 8.35. Неидеальная версия шаблона Visitor

Класс AbstractElement в этой версии шаблона не содержит каких-либо методов, ЛЯетСп циально связанных с объектами Visitor. Вместо этого класс Vis i tor опреде­ методы, используемые его подклассами для навигации по структуре объектов.

РЕАЛИЗАЦИЯ

При реализации шаблона Visitor прежде всего нужно решить, можно ли ис­ Пользовать идеальную версию шаблона. Если да, то следует использовать имен­

Но эту версию шаблона Visitor, так как ее реализация и сопровождение требует

436 Глава 8. Поведенческие шаблоны проектирования

меньших усилий. Если использование идеальной версии шаблона Visitor невоз_

можно, помещают в класс Vi s i tor (а не в его подклассы) как можно большую

часть логики, предназначенную ДЛЯ навигации по структуре объектов. Тогда число зависимостей таких объектов ConcreteVis i tor от структуры объектов будет минимальным, и сопровождение станет более простым.

СЛЕДСТВИЯ

© Шаблон Visitor позволяет легко добавлять в структуру объектов новые опе­ рации. Классы ConcreteElement не зависят от классов Vi s i tor, поэтомуАВ­ добавление нового класса Vis i tor не требует изменения класса

s t ractElement или любого из его подклассов.

©Шаблон Visitor помещает логику, необходимую ДЛЯ выполнения некоторой операции, в один сцепленный класс - ConcreteVi s i tor. Такая схема со­ провождается легче по сравнению с операциями, распределенными по мно­

 

гим классам Сопсrе tеЕ lеmепt.

©

Единственный объект Vis i tor сохраняет состояние, необходимое для

 

выполнения операции над структурами объектов. Такая схема легче сопро­

 

вождается и более эффективна по сравнению со схемой, при которой ин­

 

Формация о состоянии должна передаваться в виде дискретных значений

®

от одного объекта другому.

Другое следствие использования шаблона Visitor состоит в том, что нужны

 

дополнительные усилия для добавления новых классов ConcreteEl ement.

 

Идеальная версия шаблона Visitor требует от программиста добавления но­

 

вого метода vis i t в каждый класс ConcreteVis i tor ДЛЯ каждого добав

 

ляемого класса ConcreteEl ement. Для другой версии шаблона Visitor мо­

 

жет понадобиться изменение логики, используемой классами Vis i tor для

®

навигации по структуре объектов.

Прямым следствием шаблона Visitor является то, что классы ConcreteEle­

 

men t должны обеспечивать предоставление о себе столько информации,

 

сколько должно быть достаточно для проведения вычислений объектамИ

 

Vis itor. Это может означать, что программист открывает информацию.

 

которая в противном случае была бы скрыта инкапсуляцией класса.

ПРИМЕР КОДА

Приведем коды ДЛЯ некоторых классов, представленных в проекте по созданиЮ

оглавления, описанном в разделе « Контекст». Сначала код для класОНса WordProcess or, содержащего общую логику ДЛЯ текстового процессора. отвечает за инициирование операций, манипулирующих документами.

public class WordProcessor {

/ / Редактируемый в дaHHЬ момент документ .

reorg ( int)

Visitor 437

private Document activeDocument;

/* *

*

*

* /

Реорганизуем документ, представляя его в виде несколь ких

файлов .

private void reorg (int level) {

new ReorgVisitor (activeDocument, level) ; } //

/

* *

*

*

/Строим оглавление .

private ТОС buildTOC () {

 

return new TOCVisitor (activeDocument) . buildTOC () ;

}

// bui ldTOC ( )

/ /

class WordProcessor

Следуюший листинг - класс DocumentVis i tor. Класс DocumentVis itor

предстаW1яет собой абстрактный суперкласс ДЛЯ классов, реализуюших опера­ ции, в ходе которых «посешаются» многие объекты, входяшие в состав доку­ мента.

abstract class DocumentVisitor

{

private

Document document;

 

private

int docIndex = О ; / /

Индекс , исполь зуемый

 

//

для навигации потомков документа .

DocumentVisitor (Document document) this . document = document;

// constructor ( Document)

protected Document getDocument () { return documenti }

/ * *

*

*

Возвращает следующий абзац, который

является

непосредственной частью документа .

 

* /

 

 

protected Paragraph getNextParagraph () Document myDocument = document i

while (docIndex < myDocument . getChildCount (» DocumentElementIF docElement;

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]