1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 69
Текст из файла (страница 69)
Основное в нем — часть с приоритетом, хотя реальный класс P a c k a g e , несомненно, должен содержать массу других членов. Все, чтотребуется классу P a c k a g e для участия в данном пакете — это член-данные для хранения приоритета, конструктор для создания пакета с определенным приоритетом иметод (реализованный здесь как свойство только для чтения) для возврата значенияприоритета.Требуют пояснения два аспекта класса P a c k a g e : тип приоритета и интерфейсI P r i o r i t i z a b l e , реализуемый данным классом.О п р е д е л е н и е возможных приоритетовПриоритеты представляют собой перечислимый тип (enum) под названием P r i o r i t y .
Он выглядит следующим образом:352Часть V. За базовыми классами//Priority - в м е с т о числовых п р и о р и т е т о в н а п о д о б и е// 3, . . . и с п о л ь з у е м п р и о р и т е т ы с и м е н а м иe n u mP r i o r i t y1,2,{Low,Medium,High{Реализация интерфейса IPrioritizableЛюбой объект, поступающий в P r i o r i t y Q u e u e , должен знать собственный приоритет (общий принцип объектно-ориентированного программирования гласит, что кажцый объект отвечает сам за себя).Можно просто неформально убедиться, что класс P a c k a g e имеет член дляполучения его приоритета, но лучше заставить компилятор проверять этотребование, т.е. то, что у любого объекта, помещаемого в P r i o r i t y Q u e u e ,имеется этот член.Один из способов обеспечить это состоит в требовании, чтобы все объекты реализовывали интерфейс I P r i o r i t i z a b l e :// I P r i o r i t i z a b l e - о п р е д е л я е м п о л ь з о в а т е л ь с к и й и н т е р ф е й с :// классы, к о т о р ы е м о г у т быть д о б а в л е н ы в P r i o r i t y Q u e u e ,I I должны р е а л и з о в ы в а т ь э т о т и н т е р ф е й сinterfaceIPrioritizablePriorityPriority{get;}Запись { g e t ; } определяет, как должно быть описано свойство в объявленииинтерфейса.
Обратите внимание, что тело функции доступа g e t отсутствует,но интерфейс указывает, что свойство P r i o r i t y — только для чтения и возвращает значение перечислимого типа P r i o r i t y .Класс P a c k a g e реализует интерфейс путем предоставления реализации свойстваPriority:publicgetPriority{Priorityreturn priority;}Функция Main()Перед тем как приступить к исследованию класса P r i o r i t y Q u e u e , стоит посмотреть, как он применяется на практике.
Вот исходный текст функции M a i n ( ) ://Main - з а п о л н я е м о ч е р е д ь с п р и о р и т е т а м и п а к е т а м и ,// затем и з в л е к а е м из очереди их случайное к о л и ч е с т в оstatic v o i d Main ( s t r i n g [] a r g s ){Console.WriteLine("Создание очереди с приоритетами:");P r i o r i t y Q u e u e < P a c k a g e > pq =new P r i o r i t y Q u e u e < P a c k a g e > ( ) ;Console.WriteLine("Добавляем случайное количество" +Глава 15. Обобщенное программирование353" (0 - 2 0 ) с л у ч а й н ы х п а к е т о в "*"вочередь:");Package pack;P a c k a g e F a c t o r y f a c t = new P a c k a g e F a c t o r y ( ) ;/ / Нам н у ж н о с л у ч а й н о е ч и с л о , м е н ь ш е е 2 0R a n d o m r a n d = n e w R a n d o m () ;// Случайное число в диапазоне 0 - 2 0i n t numToCreate = r a n d .
N e x t ( 2 0 ) ;C o n s o l e . W r i t e L i n e ( " ^ С о з д а н и е {0} п а к е т о в :",numToCreate);f o r ( i n t i = 0; i < n u m T o C r e a t e ; i + + )+{Console.Write("\t\tTeHepauHH и добавление ""случайного пакета { о } " , i ) ;pack = f a c t . C r e a t e P a c k a g e ( ) ;C o n s o l e . W r i t e L i n e ( " с приоритетом { о } " ,pack.Priority);pq.Enqueue(pack);+}C o n s o l e . W r i t e L i n e ( " Ч т о п о л у ч и л о с ь : ") ;int nTotal = pq.Count;Console.WriteLine("Получено пакетов: { о } " , nTotal);C o n s o l e .
W r i t e L i n e ( " И з в л е к а е м случайное количество" +" п а к е т о в : 0-2 0 : " ) ;i n t numToRemove = r a n d . N e x t ( 2 0 ) ;C o n s o l e . W r i t e L i n e ( " ^ И з в л е к а е м {0} п а к е т о в " ,numToRemove);f o r ( i n t i = 0; i < numToRemove; i + + ){pack = pq.Dequeue();if(pack != n u l l ){Console.WriteLi n e ( " \ t \ t Д о с т а в к а пакета"с приоритетом {О}",pack.Priority);}}// Сколько пакетов " д о с т а в л е н о "Console.WriteLine("Доставлено {о} пакетов",nTotal - pq.Count);/ / Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яConsole.WriteLine("Нажмите <Enter> для " +"завершения п р о г р а м м ы . . .
" ) ;Console.Read();Итак, что же происходит в функции M a i n () ?1 . Инстанцируется объект P r i o r i t y Q u e u e для типа P a c k a g e .2. Создается объект P a c k a g e F a c t o r y , работа которого состоит в формированииновых пакетов со случайно выбранными приоритетами. ( Ф а б р и к а — это классили метод, который создает для вас объекты.)354Часть V. За базовыми классами3. Для генерации случайного числа используется класс R a n d o m из библиотеки.NET, а затем вызывается P a c k a g e F a c t o r y для создания соответствующегоколичества новых объектов P a c k a g e со случайными приоритетами.4. Выполняется добавление созданных пакетов в P r i o r i t y Q u e u e с помощью вызова p g .
E n q u e u e ( p a c k ) .5. Выводится число созданных пакетов, после чего некоторое случайное их количество извлекается из P r i o r i t y Q u e u e .6. Функция завершается выводом количества извлеченных из P r i o r i t y Q u e u e пакетов.Написание обобщенного кодаКак же написать собственный обобщенный класс со всеми этими <Т>? Выглядит это,конечно, устрашающе, но все не так уж и страшно.Простейший путь написания обобщенного класса состоит в создании сначалаего необобщенной версии, а затем расстановки в ней всех этих <Т>. Так, например, вы можете написать класс P r i o r i t y Q u e u e для объектов P a c k a g e ,протестировать его, а затем "обобщить".Вот небольшая часть необобщенного класса P r i o r i t y Q u e u e для иллюстрации сказанного:publicclassPriorityQueue{//Queues - три в н у т р е н н и е (обобщенные!) о ч е р е д и1private Queue<Package> queueHigh= new Q u e u e < P a c k a g e > ( ) ;p r i v a t e Q u e u e < P a c k a g e > q u e u e M e d i u m = new Q u e u e < P a c k a g e > ( ) ;p r i v a t e Queue<Package> queueLow= new Q u e u e < P a c k a g e > ( ) ;//Enqueue - на основании приоритета Package добавляем е г о// в с о о т в е т с т в у ю щ у ю о ч е р е д ьpublic void Enqueue(Package item){switch(item.Priority)//Packageимеетэтосвойство{casePriority.High:queueHigh.Enqueue(item);break;casePriority.Low:queueLow.Enqueue(item);break;case Priority.Medium:queueMedium.Enqueue(item);break;}}// и так далее .
. .Написание необобщенного класса упрощает тестирование его логики. Затем, после тестирования и исправления всех ошибок, вы можете сделать контекстную замену P a c k a g e на<Т> (конечно, все не так прямолинейно, но и не очень отличается от сказанного).355Обобщенная очередь с приоритетамиТеперь пришло время разобраться с основным классом, из-за которого все и затевалось — с обобщенным классом P r i o r i t y Q u e u e .Внутренние очередиКласс P r i o r i t y Q u e u e — оболочка, за которой скрываются три обычных объектаQ u e u e < T > , по одному для каждого уровня приоритета.
Вот первая часть исходного текста P r i o r i t y Q u e u e , в которой показаны эти три внутренние очереди:/ / P r i o r i t y Q u e u e - обобщенный к л а с с о ч е р е д и с п р и о р и т е т а м и ;// типы данных, д о б а в л я е м ы х в о ч е р е д ь , о б я з а н ы// реализовывать интерфейс I P r i o r i t i z a b l eclassPriorityQueue<T>where T : I P r i o r i t i z a b l e{//Queues - три внутренние (обобщенные!) очередиp r i v a t e Queue<T> q u e u e H i g h= new Q u e u e < T > ( ) ;p r i v a t e Queue<T> q u e u e M e d i u m = new Q u e u e < T > ( ) ;p r i v a t e Queue<T> queueLow= new Q u e u e < T > ( ) ;/ / Все о с т а л ь н о е м ы в о т - в о т р а с с м о т р и м . . .В данных строках объявляются три закрытых члена-данных типа Q u e u e < Т > , инициализируемые путем создания соответствующих объектов Q u e u e < T > .М е т о д Enqueue()E n q u e u e О добавляет элемент типа Т в P r i o r i t y Q u e u e .
Работа состоит в том,чтобы выяснить приоритет элемента и поместить его в соответствующую приоритетуочередь. В первой строке метод получает приоритет элемента и использует конструкциюs w i t c h для определения целевой очереди исходя из полученного значения. -Например,получив элемент с приоритетом P r i o r i t y . H i g h , метод E n q u e u e () помещает егов очередь q u e u e H i g h .
Вот исходный текст метода P r i o r i t y Q u e u e . E n q u e u e ( ) :////Добавляет элементприоритетаpublicvoidEnqueue(ТТвочередьнаоснованиизначенияегоitem){switch (item.Priority)// Требует реализации{//IPrioritizablecasePriority.High:queueHigh.Enqueue(item);break;casePriority.Low:queueLow.Enqueue(item);break;casePriority.Medium:queueMedium.Enqueue(item);break;default:t h r o w newArgumentOutOfRangeException(item.Priority.ToString(),356Часть V.
За базовыми классам"Неверный приоритетвPriorityQueue.Enqueue");Метод Dequeue()Работа метода Dequeue () немного более хитрая. Он должен найти непустую очередь элементов с наивысшим приоритетом и выбрать из нее первый элемент. Первуючасть своей работы — поиск непустой очереди элементов с наивысшим приоритетом —Dequeue () делегирует закрытому методу T o p Q u e u e ( ) , который будет описан ниже.Затем метод D e q u e u e () вызывает метод D e q u e u e () найденной очереди для извлечения из нее объекта, который и возвращает.