Главная страница
Навигация по странице:

  • Рисунок 1.1 – UML-диаграмма шаблона Одиночка

  • Рисунок 1.2 – UML-диаграмма шаблона Одиночка

  • Рисунок 2.1 – древовидная структура программы

  • Рисунок 2.2 – UML -диаграмма

  • Рисунок 2.3 – UML -диаграмма шаблона Компоновщик

  • паттерны. 1 Назначение шаблонов проектирования


    Скачать 359 Kb.
    Название1 Назначение шаблонов проектирования
    Анкорпаттерны
    Дата02.01.2022
    Размер359 Kb.
    Формат файлаdoc
    Имя файлаKursach_1.doc
    ТипРешение
    #323020

    1 Назначение шаблонов проектирования


    Шаблон проектирования или паттерн (англ. design pattern) в разработке программного обеспечения — повторяемая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста.

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

    Паттерны проектирования предназначены для:

    1. эффективного решения характерных задач проектирования;

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

    3. указания отношения и взаимодействия между классами и объектами.

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

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

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

    Основные шаблоны проектирования были описаны в 1994 году «Бандой четырёх» (Gang of Four (GoF)) (Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес) в книге «Приёмы объектно-ориентированного проектирования. Паттерны проектирования» (англ.  «Design Patterns: Elements of Reusable Object-Oriented Software»), и состояли из:

    1. Порождающие шаблоны (Creational) — шаблоны проектирования, которые абстрагируют процесс инстанцирования. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Шаблон, порождающий классы, использует наследование, чтобы изменять инстанцируемый класс, а шаблон, порождающий объекты, делегирует инстанцирование другому объекту:

    • Абстрактная фабрика (Класс, который представляет собой интерфейс для создания компонентов системы);

    • Строитель (Класс, который представляет собой интерфейс для создания сложного объекта);

    • Фабричный метод (Определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать);

    • Прототип (Определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор);

    • Одиночка (Класс, который может иметь только один экземпляр).

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

    • Адаптер (Объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс);

    • Мост (Структура, позволяющая изменять интерфейс обращения и интерфейс реализации класса независимо);

    • Компоновщик (Объект, который объединяет в себе объекты, подобные ему самому);

    • Декоратор или Wrapper/Обёртка (Класс, расширяющий функциональность другого класса без использования наследования);

    • Фасад (Объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое);

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

    • Заместитель (Объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него).

    1. Поведенческие шаблоны (Behavioral) определяют взаимодействие между объектами, увеличивая таким образом его гибкость:

    • Цепочка обязанностей (Предназначен для организации в системе уровней ответственности);

    • Команда, Action, Transaction (Представляет действие. Объект команды заключает в себе само действие и его параметры);

    • Интерпретатор (Решает часто встречающуюся, но подверженную изменениям, задачу);

    • Итератор, Cursor (Представляет собой объект, позволяющий получить последовательный доступ к элементам объекта-агрегата без использования описаний каждого из объектов, входящих в состав агрегации);

    • Посредник (Обеспечивает взаимодействие множества объектов, формируя при этом слабую связанность и избавляя объекты от необходимости явно ссылаться друг на друга);

    • Хранитель (Позволяет не нарушая инкапсуляцию зафиксировать и сохранить внутренние состояния объекта так, чтобы позднее восстановить его в этих состояниях);

    • Наблюдатель или Издатель-подписчик (Определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии);

    • Состояние (Используется в тех случаях, когда во время выполнения программы объект должен менять своё поведение в зависимости от своего состояния);

    • Стратегия (Предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости);

    • Шаблонный метод (Определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом);

    • Посетитель (Описывает операцию, которая выполняется над объектами других классов. При изменении класса Visitor нет необходимости изменять обслуживаемые классы).

    1.1Паттерн проектирования Одиночка (Singleton)


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

    Архитектура паттерна Singleton основана на идее использования глобальной переменной, имеющей следующие важные свойства:

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

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

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

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

    Поведение Одиночки на Java невозможно реализовать с помощью обычного конструктора, потому что конструктор всегда возвращает новый объект. Поэтому все реализации Singleton’a сводятся к тому, чтобы скрыть конструктор и создать публичный статический метод, который будет управлять существованием объекта-одиночки и «уничтожать» всех вновь-появляющихся объектов. В случае вызова Singleton’a он должен либо создать новый объект (если его еще нет в программе), либо вернуть уже созданный.



    Рисунок 1.1 – UML-диаграмма шаблона Одиночка

    Плюсы:

    • контролируемый доступ к единственному экземпляру.

    Минусы:

    • глобальные объекты могут быть вредны для объектного программирования, в некоторых случаях приводят к созданию немасштабируемого проекта;

    • усложняет написание модульных тестов и следование TDD (Разработка через тестирование (англ. test-driven development, TDD) — техника разработки программного обеспечения, которая основывается на повторении очень коротких циклов разработки: сначала пишется тест, покрывающий желаемое изменение, затем пишется код, который позволит пройти тест, и под конец проводится рефакторинг нового кода к соответствующим стандартам);

    • усложняется контроль за межпоточными гонками и задержками.

    Проблема: Одиночка решает сразу две проблемы, нарушая принцип единственной ответственности класса:

    • Гарантирует наличие единственного экземпляра класса;

    • Предоставляет глобальную точку доступа.

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

    Если у вас есть доступ к классу одиночки, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его ни вызвали, он всегда будет отдавать один и тот же объект.

    Пример реализации паттерна Одиночка на Java:

    public final class Singleton {

    private static Singleton _instance = null;
    private Singleton() {}
    public static synchronized Singleton getInstance() {

    if (_instance == null)

    _instance = new Singleton();

    return _instance;

    }

    }

    1.2Паттерн проектирования Фасад (Facade)


    Шаблон фасад (англ. Facade) — структурный шаблон проектирования, позволяющий скрыть сложность системы путём сведения всех возможных внешних вызовов к одному объекту, делегирующему их соответствующим объектам системы.

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

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

    Клиенты общаются с подсистемой через Facade. При получении запроса от клиента объект Facade переадресует его нужному компоненту подсистемы. Для клиентов компоненты подсистемы остаются "тайной, покрытой мраком". Подсистемы SubsystemOne и SubsystemThree не взаимодействуют напрямую с внутренними компонентами подсистемы SubsystemTwo. Они используют "фасад" SubsystemTwoWrapper (т.е. абстракцию более высокого уровня).

    Использование паттерна:

    • Определите для подсистемы простой, унифицированный интерфейс.

    • Спроектируйте класс "обертку", инкапсулирующий подсистему.

    • Вся сложность подсистемы и взаимодействие ее компонентов скрыты от клиентов. "Фасад" / "обертка" переадресует пользовательские запросы подходящим методам подсистемы.

    • Клиент использует только "фасад".

    • Рассмотрите вопрос о целесообразности создания дополнительных "фасадов".



    Рисунок 1.2 – UML-диаграмма шаблона Одиночка

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

    Решение: определить одну точку взаимодействия с подсистемой — фасадный объект, обеспечивающий общий интерфейс с подсистемой, и возложить на него обязанность по взаимодействию с её компонентами. Фасад — это внешний объект, обеспечивающий единственную точку входа для служб подсистемы. Реализация других компонентов подсистемы закрыта и не видна внешним компонентам. Фасадный объект обеспечивает реализацию GRASP паттерна Устойчивый к изменениям (Protected Variations) с точки зрения защиты от изменений в реализации подсистемы.

    Пример реализации паттерна Фасад на Java:

    class CPU {

    public void freeze() { ... }

    public void jump(long position) { ... }

    public void execute() { ... }

    }
    class Memory {

    public void load(long position, byte[] data) { ... }

    }
    class HardDrive {

    public byte[] read(long lba, int size) { ... }

    }

    /* Facade */
    class Computer {

    private CPU cpu;

    private Memory memory;

    private HardDrive hardDrive;
    public Computer() {

    this.cpu = new CPU();

    this.memory = new Memory();

    this.hardDrive = new HardDrive();

    }
    public void startComputer() {

    cpu.freeze();

    memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));

    cpu.jump(BOOT_ADDRESS);

    cpu.execute();

    }

    }
    /* Client */
    class Application {

    public static void main(String[] args) {

    Computer computer = new Computer();

    computer.startComputer();

    }

    }


    2 Практические исследования

    2.1 Постановка задачи


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

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

    На рисунке 2.1 показана древовидная структура организации сотрудников в компании.



    Рисунок 2.1 – древовидная структура программы

    Так как в приложении существует иерархия, удобно будет выполнить её посредством шаблона “Компоновщик”, что добавит уровень абстракции к моделям и в конечном итоге снижает их сложность.
    На рисунке, 2.2 UML-диаграмма шаблона проектирования Компоновщик, используемый в программе.



    Рисунок 2.2 – UML-диаграмма

    2.2 Описание сущностей


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

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

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

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

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

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

    Составной паттерн состоит из четырех участников:

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

    Leaf (Лист) - определяет поведение примитивных объектов в композиции. Он представляет собой листовые объекты в композиции.

    Composite (Контейнер) - хранит дочерние компоненты и реализует связанные с ними операции в интерфейсе компонента.

    Client (Клиент) - управляет объектами в композиции через интерфейс компонента. Клиент использует интерфейс класса компонента для взаимодействия с объектами в структуре композиции. Если получатель является “листом”, запрос обрабатывается напрямую. Если получатель является составным, он обычно пересылает запрос своим дочерним компонентам, возможно, выполняя дополнительные операции до и после пересылки.

    UML-диаграмма паттерна Компоновщик изображена на рисунке 2.3



    Рисунок 2.3 – UML-диаграмма шаблона Компоновщик

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

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

    Преимущества: Поддерживает иерархии классов, состоящие из примитивных и составных объектов. Из примитивных объектов можно составлять более сложные, которые, в свою очередь, участвуют в более сложных композициях и так далее. Любой клиент, ожидающий примитивного объекта, сможет работать и с составным. Облегчает, упрощает архитектуру клиента. Клиенты могут единообразно работать и с индивидуальными объектами и с составными структурами. Обычно клиенту неизвестно, взаимодействует ли он с листовым или составным объектом. Это упрощает код клиента, поскольку нет необходимости писать функции, ветвящиеся в зависимости от того, с объектом какого класса они работают. Облегчает добавление новых видов компонентов. Новые подклассы классов Composite или Leaf будут автоматически работать с уже существующими структурами и клиентским кодом. Изменять клиента при добавлении новых компонентов не нужно. Способствует созданию общего дизайна. Однако такая простота добавления новых компонентов имеет и свои отрицательные стороны: становится трудно наложить ограничения на то, какие объекты могут входить в состав композиции. Иногда желательно, чтобы составной объект мог включать только определенные виды компонентов. Паттерн компоновщик не позволяет воспользоваться для реализации таких ограничений статической системой типов. Вместо этого следует проводить проверки во время выполнения. Способствует использованию рекурсии. Главное достоинство которой, – компактность, малый объем, читабельность кода.

    Недостатки: Создаёт слишком общий дизайн классов.

    2.3 Реализация шаблонов проектирования


    В программе имеется интерфейс компонента. Он представляет объект в композиции. Он имеет все стандартные операции, которые будут применимы как к менеджеру (Manager), так и к разработчику (Employee).

    Employee.java (Компонент).

    Класс Public void add предназначен для добавления разработчика, Public void remove удаляет разработчика. Public Employee getchild назначает родительский класс. Public String getname установливает имя разработчика. Public double getsalary устанавливает зарплату разработчика. Public void print предназначен для вывода результата. Код компонента interface:
    package kursovoy.composite;

     

    public interface Employee {

          public void add(Employee employee);

         public void remove(Employee employee);

         public Employee getChild(int i);

         public String getName();

         public double getSalary();

         public void print();}

    Так же необходимо добавить класс сотрудника Manager (менеджер (составной класс)). Ключевым моментом здесь является то, что все обычные методы делегируют свои операции дочерним объектам. У него есть метод для доступа и изменения своих дочерних объектов. Public class Manager implements Employee Инт ерфейс, который использует поля имени и зарплаты дочернего класса Employee. Public Manager назначает переменные имени и зарплаты для менеджера и создаёт ссылки. Public void add создаёт массив из сотрудников. Public Employee getchild, public String getName, public double getSalary создают шаблон сотрудника, который будет содержать значения имени и зарплаты, а так жеиспользоваться в дочерних классах. Public void print отображает имя и зарплату менеджера. Iterator employeeiterator перебирает коллекцию employee и выводит на экран. Public void remove удаляет сотрудника из коллекции.

    Код компонента Manager:

    package kursovoy.composite;

     

    import java.util.ArrayList;

    import java.util.Iterator;

    import java.util.List;

     

    public class Manager implements Employee{

     

    private String name;

    private double salary;

     

    public Manager(String name,double salary){
      this.name = name;

      this.salary = salary;
    }
    List employees = new ArrayList();

    public void add(Employee employee) {

        employees.add(employee);

    }

     

    public Employee getChild(int i) {

      return employees.get(i);

    }

    public String getName() {
      return name;

    }

     

    public double getSalary() {
      return salary;

    }

     

    public void print() {

      System.out.println(" -------------");

      System.out.println(" Name ="+getName());

      System.out.println(" Salary ="+getSalary());

      System.out.println(" -------------");

      

      Iterator employeeIterator = employees.iterator();

        while(employeeIterator.hasNext()){

         Employee employee = employeeIterator.next();

         employee.print();

        }

    }

     

    public void remove(Employee employee) {

      employees.remove(employee);

    }

     }





    Программа включает компонент разработчика (Developer). Этот класс является листовым узлом, поэтому все операции, связанные с доступом к дочерним элементам, будут пустыми, поскольку у него нет дочерних узлов. Public class Developer implements Employee создаёт класс, используя элементы класса Employee. Private String name, private double salary создают тип переменных. Public Developer создаёт ссылки на поля name и salary. Public String getname, public double getsalary добавляет в коллекцию сотрудников имя и зарплату. Public void print выводит на экран значения имени и зарплаты сотрудника “разработчик”.

    Developer.java (лист):

    package kursovoy.composite;

     

    public class Developer implements Employee{

     

        private String name;

        private double salary;

     

        public Developer(String name,double salary){
            this.name = name;

            this.salary = salary;

        }

        public void add(Employee employee) {

           // так как это листовой узел, этот метод не применим к этому классу.

        }

     

        public Employee getChild(int i) {

            // так как это листовой узел, этот метод не применим к этому классу.

            return null;

        }

     

        public String getName() {

            return name;

        }

     

        public double getSalary() {

            return salary;

        }

     

        public void print() {
            System.out.println(" -------------");

            System.out.println(" Name ="+getName());

            System.out.println(" Salary ="+getSalary());

            System.out.println(" -------------");

        }

     

        public void remove(Employee employee) {

            // так как это листовой узел, этот метод не применим к этому классу.

        }

     }

    2.4 Тестирование программы


    Файл CompositeDesignPatternMain используется для тестирования программы, путём создания сотрудников и выводу на экран списка в соответствии с должностью, а так же показывает значения их имени и зарплаты.

    Employee emp1=new Developer создаёт нового сотрудника “разработчик” и назначает ему имя John и зарплату 10000. Employee emp2=new Developer создаёт нового сотрудника “разработчик” и назначает ему имя David и зарплату 15000. Employee manager1=new Manager создаёт нового сотрудника “менеджер” и назначает ему имя Daniel и зарплату 25000. Manager1.add(emp1), manager1.add(emp2) добавляют к сотруднику “manager” двух созданных сотрудников “developer”. Employee emp3=new Developer создаёт нового сотрудника “разработчик” и назначает ему имя Michael и зарплату 20000. Manager generalmanager создаёт сотрудника “general manager” и назначает ему имя Mark и зарплату 50000. Generalmanager.add(emp3)добавляет к сотркднику “general manager” сотрудника “разработчик” (emp3). Generalmanager.add(manager1)добавляет к сотркднику “general manager” сотрудника “manager” (manager1). Generalmanager.print() выводит на экран значение “general Manager” , а так же всех присоединённых к нему сотрудников.

    CompositeDesignPatternMain.java:

    package kursovoy.composite;

     

    public class CompositeDesignPatternMain {

     

    public static void main(String[] args) {
      Employee emp1=new Developer("John", 10000);

      Employee emp2=new Developer("David", 15000);

      Employee manager1=new Manager("Daniel",25000);

      manager1.add(emp1);

      manager1.add(emp2);

      Employee emp3=new Developer("Michael", 20000);

      Manager generalManager=new Manager("Mark", 50000);

      generalManager.add(emp3);

      generalManager.add(manager1);

      generalManager.print();

    }

    }
    Вывод:

    -------------

    Name =Mark

    Salary =50000.0

    -------------

    -------------

    Name =Michael

    Salary =20000.0

    -------------

    -------------

    Name =Daniel

    Salary =25000.0

    -------------

    -------------

    Name =John

    Salary =10000.0

    -------------

    -------------

    Name =David

    Salary =15000.0

    -------------




    написать администратору сайта