Рабочая программа по дисциплине Цели и задачи освоения дисциплины Дисциплина Объектноориентированный анализ и программирование
Скачать 339.98 Kb.
|
private Finder SlowFinder; private int.. data; FastFinder(int size, Finder SlowFinder) { data = new int[size;; this.s_owFinder = SlowFinder; public int getData(int key) { if(key >= 0 && key < data.length) return data[key]; else return SlowFinder.getData(key); } public void add(int key, int data) { if (key >= 0 && key < this . data . .length) this.data.key. = data; else SlowFinder.add(key, data); public class Client { public static void main(String args) { Finder ff new FastFinder(1000, new SiowFinder()); ff.add(10, 1); ff.add(100, 2); ff.adddOOO, 3); ff.adddOOOO, 4) ; for(int i = 10; i <= 10000; i = i * 10) System.out.printin(ff.getData(i) ) ; System.out.flush() ; В данном примере классы SiowFinder и FastFinder реализуют интерфейс Finder. Первый класс использует для хранения данных коллекцию HashMap, что позволяет хранить пары ключ-значение для широкого диапазона ключей. Класс FastFinder использует для хранения пар линейный массив и является «точкой входа» в цепочку. Операция доступа к элементам HashMap более трудоемкая, чем операция обращения к элементу массива, поэтому при условии, что большая часть запросов будет обрабатываться экземпляром класса FastFinder, предлагаемая цепочка позволит значительно сократить время, затрачиваемое в среднем на поиск одного значения. Важными преимуществами «цепочки ответственности» являются, во-первых, ослабление связности (клиентскому объекту нет нужды знать что-либо об объектах, обслуживающих его запрос; достаточно только иметь ссылку на точку входа), а во-вторых, дополнительная гибкость при распределении обязанностей (цепочка классов-обработчиков может быть модифицирована с минимальными затратами). Однако необходимо помнить, что само по себе наличие цепочки не гарантирует обработку запроса (запрос может пройти через всю цепочку объектов и при этом не быть обработанным). Итератор Практически все современные реализации коллекций на большинстве языков программирования позволяют работать с итераторами. Итератор — это способ последовательного (в общем случае) перебора элементов. Например, стандартная реализация итератора для вектора элементов позволяет обращаться к первому, последнему, следующему и предыдущему элементам. Осуществляется это благодаря использованию одноименного шаблона. «Итератор» — поведенческий шаблон, предоставляющий доступ ко всем элементам составного объекта, не раскрывая его внутреннего представления. Чаще всего «итератор» применяется, когда необходимо организовать несколько активных обходов одного и того же объекта или требуется поддержка единообразного интерфейса с целью взаимодействия с составными структурами разной природы. Пример использования шаблона представлен ниже. import java.util.Vector; public class Vertex implements Comparable private int key; private int data; Vertex(int key, int data) { this.key = key; this.setData(data) ; public int getDataO { return data; public void setData(int data) { this.data = data; public int compareTo(Vertex argO) { if(this.key < argO.key) return -1; if(this.key > argO.key) return 1; return 0; public abstract class Enumerator { protected int position = 0; protected Heap heap; Enumerator(Heap heap) { this.heap = heap; public Vertex current() { return heap.getVertex(position); public void first() { position = 0; public void last() { position - heap.size() - 1; public boolean isFirstO { return position == 0 ? true : false; public boolean isEnd() { return position < heap.size() ? false : true; } } public class Heapiterator extends Enumerator { Heapiterator(Heap heap) { super(heap); } public void leftchild() { position = ((position + 1) << 1) - 1; } public void rightchild() { position = (position + 1) << 1; } } public interface Iteration { public Enumerator createlterator() ; } public class Heap implements Iteration { private Vector public int size() { return data.size(); data new Vector } public void add(Vertex argO) { //В этом методе выполняется добавление узла к куче } public void remove(int key) { //В этом методе выполняется удаление узла из кучи } public void clear() { //В этом методе выполняется очистка кучи } private void upheap(int position) { //В этом методе осуществляется подъем элемента вверх } private void downheap(int position) { //В этом методе осущетсвпяется спуск элемента вниз protected Vertex getVertex(int position) { if (position < data.sizeO) return data.elementAt(position); else return null; public Enumerator createlterator() { return new Heapiterator(this); } В предлагаемом примере частично реализован итератор для структуры данных «куча». Отличительной особенностью этой разновидности бинарного дерева является то, что значение элемента-предка всегда не меньше, чем значение его элементов-потомков. Класс Heap является абстракцией «кучи». Для хранения элементов используется вектор. Heap реализует интерфейс Iteration и, соответственно, декларируемый в нем метод getlteratorQ. Класс Hcapltcrator расширяет абстрактный класс Enumerator, в котором описаны методы перемещения по вершинам дерева. Хранитель Простым, но чрезвычайно полезным поведенческим шаблоном является «хранитель». Назначение этой структуры — фиксация и вынесение за пределы объекта его внутреннего состояния с целью его дальнейшего восстановления. «Хранитель» необходим для мгновенного создания «снимка» объекта в тех случаях, когда прямое получение такого «снимка» раскрывает детали реализации объекта и нарушает принцип инкапсуляции. Иными словами, «хранитель» помогает выдерживать границы инкапсуляции и упростить структуру класса-хозяина за счет того, что ему не приходится хранить внутри себя резервные внутренние состояния. Пример «хранителя» представлен ниже. import java.util.Random public class Originator { private int .. data; Originator(int size) { data = new int[size]; for(int i = 0; i < size; i++) data[i] = i + 1; public inti] getData() { return data.c.one(); int tmp = data.posl.; data.posl ( = data[pos2]; data.pos2. = tmp; public void permutation() { Random rnd new Random(); for(int i = 0; i < 3 * data.length; i++) { int posl = rnd.nextint(data._ength); int pos2 = rnd.nextlnt(data._ength); swap(posl, pos2); public Memento getStateO { return new Memento(data.clone()); public void setstate(Memento state) { data = state.restore(); public class Memento { private int .. data; Memento(int argO) { data = argO; public int[J restore() { return data.clone(); В примере класс Originator предоставляет метод для генерации перестановки чисел. Внутреннее состояние класса (текущая перестановка) может быть сохранено с помощью метода getStateO как экземпляр класса Memento. Если в процессе работы с классом Originator возникнет необходимость, то «снимок» может быть использован для восстановления «удачной» перестановки ( метод setStateQ). Хотя использование «хранителя» чревато появлением значительных издержек при работе с программной системой, его использование просто необходимо во многих случаях (например, для создания контрольных точек восстановления системы). Шаблонный метод Одним из фундаментальных приемов повторного использования кода является «шаблонный метод» — поведенческий шаблон, который определяет основу алгоритма и позволяет подклассам переопределять некоторые шаги алгоритма. Наиболее значимое применение данный шаблон нашел в библиотеках классов, так как он предоставляет возможность вынести общее поведение в отдельную сущность. Структуру и принцип построения этого шаблона проще всего рассмотреть на следующем примере. public abstract class Sort { private int Г j data; public abstract boolean compare(int x, int y) ; private void swap(int posl, int pos2) { int tmp - data.posl.; data.posl. = data pos2_; data.pos2. = tmp; public int.. sort(int.. argO) { data = argO.clone(); boolean isSorted = false; while (!isSorted) { isSorted true; for(int i = 0; i < data._ength - 1; i++) if(compare(data.i., data.i +1])) { isSorted = false; swap(i, i + 1); return data; public class AscendingSort extends Sort { public boolean compare(int x, int y) { if(x <= y) |