Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
МІНІСТЕРСТВО ОСВІТИ НАУКИ ТА СПОРТУ УКРАЇНИ
ЖИТОМИРСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
ФІКТ
ПІК-8
Курсовий проект
З дисципліни Архітектура та дизайн ПЗ
На тему: Шаблони проектування
Паттерн Proxy (заместитель, surrogate, суррогат)
Паттерн Interpreter (интерпетатор)
Паттерн Composite (компоновщик)
Виконав: Євпак В.А.
Перевірив: Левченко А.Ю.
Житомир 2012р.
UML (англ. Unified Modeling Language унифицированный язык моделирования) язык графического описания для объектного моделирования в области разработки программного обеспечения. UML является языком широкого профиля, это открытый стандарт, использующий графические обозначения для создания абстрактной модели системы, называемой UML-моделью. UML был создан для определения, визуализации, проектирования и документирования, в основном, программных систем. UML не является языком программирования, но на основании UML-моделей возможна генерация кода.
Использование
Использование UML не ограничивается моделированием программного обеспечения. Его также используют для моделирования бизнес-процессов, системного проектирования и отображения организационных структур.
UML позволяет также разработчикам программного обеспечения достигнуть соглашения в графических обозначениях для представления общих понятий (таких как класс, компонент, обобщение (англ. generalization), агрегация (англ. aggregation) и поведение) и больше сконцентрироваться на проектировании и архитектуре.
История объектно-ориентированных методов и нотации.
В 1994 году Гради Буч и Джеймс Рамбо, работавшие в компании Rational Software, объединили свои усилия для создания нового языка объектно-ориентированного моделирования. За основу языка ими были взяты методы моделирования, разработанные Бучем и Рамбо (Object-Modeling Technique, OMT). OMT был ориентирован на анализ, а Booch на проектирование программных систем. В октябре 1995 года была выпущена предварительная версия 0.8 унифицированного метода (англ. Unified Method). Осенью 1995 года к компании Rational присоединился Ивар Якобсон, автор метода Object-Oriented Software Engineering OOSE. OOSE обеспечивал превосходные возможности для спецификации бизнес-процессов и анализа требований при помощисценариев использования. OOSE был также интегрирован в унифицированный метод.
На этом этапе основная роль в организации процесса разработки UML перешла к консорциуму OMG (Object Management Group). Группа разработчиков в OMG, в которую также входили Буч, Рамбо и Якобсон, выпустила спецификации UML версий 0.9 и 0.91 в июне и октябре 1996 года.
Версия |
Дата принятия |
1.1 |
ноябрь 1997[1] |
1.3 |
март 2000[2] |
1.4 |
сентябрь 2001[3] |
1.4.2. |
июль 2004[2] |
1.5 |
март 2003[4] |
2.0 |
июль 2005[5] |
2.1 |
формально не была принята[2] |
2.1.1 |
август 2007[6] |
2.1.2 |
ноябрь 2007[7] |
2.2 |
февраль 2009[8] |
2.3 |
май 2010[9] |
2.4 beta 2 |
март 2011[10] |
На волне растущего интереса к UML к разработке новых версий языка в рамках консорциума UML Partners присоединились такие компании, как Digital Equipment Corporation, Hewlett-Packard, i-Logix, IntelliCorp, IBM, ICON Computing, MCI Systemhouse,Microsoft, Oracle Corporation, Rational Software, Texas Instruments и Unisys. Результатом совместной работы стала спецификация UML 1.0, вышедшая в январе 1997 года. В ноябре того же года за ней последовала версия 1.1, содержавшая улучшения нотации, а также некоторые расширения семантики.
Последующие релизы UML включали версии 1.3, 1.4 и 1.5, опубликованные, соответственно, в июне 1999, сентябре 2001 и марте 2003 года.
Формальная спецификация последней версии UML 2.0 опубликована в августе 2005 года. Семантика языка была значительно уточнена и расширена для поддержки методологии Model Driven Development MDD (англ.). Последняя версия UML 2.4.1 опубликована в августе 2011 года.
UML 1.4.2 принят в качестве международного стандарта ISO/IEC 19501:2005.
В UML используются следующие виды диаграмм (для исключения неоднозначности приведены также обозначения на английском языке):
Структурные диаграммы:
Диаграммы поведения:
|
Структуру диаграмм UML 2.3 можно представить на диаграмме классов UML:
Паттерн Proxy (заместитель, surrogate, суррогат)
Назначение паттерна Proxy
Паттерн Proxy является суррогатом или замеcтителем другого объекта и контролирует доступ к нему.
Предоставляя дополнительный уровень косвенности при доступе к объекту, может применяться для поддержки распределенного, управляемого или интеллектуального доступа.
Являясь "оберткой" реального компонента, защищает его от излишней сложности.
Решаемая проблема
Вам нужно управлять ресурсоемкими объектами. Вы не хотите создавать экземпляры таких объектов до момента их реального использования.
Обсуждение паттерна Proxy
Суррогат или заместитель это объект, интерфейс которого идентичен интерфейсу реального объекта. При первом запросе клиента заместитель создает реальный объект, сохраняет его адрес и затем отправляет запрос этому реальному объекту. Все последующие запросы просто переадресуются инкапсулированному реальному объекту.
Существует четыре ситуации, когда можно использовать паттерн Proxy:
Виртуальный proxy является заместителем объектов, создание которых обходится дорого. Реальный объект создается только при первом запросе/доступе клиента к объекту.
Удаленный proxy предоставляет локального представителя для объекта, который находится в другом адресном пространстве ("заглушки" в RPC и CORBA).
Защитный proxy контролирует доступ к основному объекту. "Суррогатный" объект предоставляет доступ к реальному объекту, только вызывающий объект имеет соответствующие права.
Интеллектуальный proxy выполняет дополнительные действия при доступе к объекту.
Вот типичные области применения интеллектуальных proxy:
Подсчет числа ссылок на реальный объект. При отсутствии ссылок память под объект автоматически освобождается (известен также как интеллектуальный указатель или smart pointer).
Загрузка объекта в память при первом обращении к нему.
Установка запрета на изменение реального объекта при обращении к нему других объектов.
Структура паттерна Proxy
Заместитель Proxy и реальный объект RealSubject имеют одинаковые интерфейсы класса Subject, поэтому заместитель может использоваться "прозрачно" для клиента вместо реального объекта.
UML-диаграмма классов паттерна Proxy
Пример паттерна Proxy
Паттерн Proxy для доступа к реальному объекту использует его суррогат или заместитель. Банковский чек является заместителем денежных средств на счете. Чек может быть использован вместо наличных денег для совершения покупок и, в конечном счете, контролирует доступ к наличным деньгам на счете чекодателя.
Использование паттерна Proxy
Определите ту часть системы, которая лучше всего реализуется через суррогата.
Определите интерфейс, который сделает суррогата и оригинальный компонент взаимозаменяемыми.
Рассмотрите вопрос об использовании фабрики, инкапсулирующей решение о том, что желательно использовать на практике: оригинальный объект или его суррогат.
Класс суррогата содержит указатель на реальный объект и реализует общий интерфейс.
Указатель на реальный объект может инициализироваться в конструкторе или при первом использовании.
Методы суррогата выполняют дополнительные действия и вызывают методы реального объекта.
Особенности паттерна Proxy
Adapter предоставляет своему объекту другой интерфейс . Proxy предоставляет тот же интерфейс. Decorator предоставляет расширенный интерфейс.
Decorator и Proxy имеют разные цели, но схожие структуры. Оба вводят дополнительный уровень косвенности: их реализации хранят ссылку на объект, на который они отправляют запросы.
Реализация паттерна Proxy
Паттерн Proxy: до и после
До
Proxy in PHP
Read full article
In the proxy pattern one class stands in for and handles all access to another class.
This can be because the real subject is in a different location (server, platform, etc), the real subject is cpu or memory intensive to create and is only created if necessary, or to control access to the real subject. A proxy can also be used to add additional access functionality, such as recording the number of times the real subject is actually called.
In this example, the ProxyBookList is created in place of the more resource intensive BookList. ProxyBookList will only instantiate BookList the first time a method in BookList is called.
<?php
class ProxyBookList {
private $bookList = NULL;
//bookList is not instantiated at construct time
function __construct() { }
function getBookCount() {
if (NULL == $this->bookList) {
$this->makeBookList(); }
return $this->bookList->getBookCount(); }
function addBook($book) {
if (NULL == $this->bookList) {
$this->makeBookList(); }
return $this->bookList->addBook($book); }
function getBook($bookNum) {
if (NULL == $this->bookList) {
$this->makeBookList(); }
return $this->bookList->getBook($bookNum); }
function removeBook($book) {
if (NULL == $this->bookList) {
$this->makeBookList(); }
return $this->bookList->removeBook($book); }
//Create
function makeBookList() {
$this->bookList = new bookList(); }}
class BookList {
private $books = array();
private $bookCount = 0;
public function __construct() { }
public function getBookCount() { return $this->bookCount; }
private function setBookCount($newCount) {
$this->bookCount = $newCount; }
public function getBook($bookNumberToGet) {
if ( (is_numeric($bookNumberToGet)) && ($bookNumberToGet <= $this->getBookCount()))
return $this->books[$bookNumberToGet];
} else {
return NULL; } }
public function addBook(Book $book_in) {
$this->setBookCount($this->getBookCount() + 1);
$this->books[$this->getBookCount()] = $book_in;
return $this->getBookCount(); }
public function removeBook(Book $book_in) {
$counter = 0;
while (++$counter <= $this->getBookCount()) {
if ($book_in->getAuthorAndTitle() == $this->books[$counter]->getAuthorAndTitle()) {
for ($x = $counter; $x < $this->getBookCount(); $x++) {
$this->books[$x] = $this->books[$x + 1]; }
$this->setBookCount($this->getBookCount() - 1); } }
return $this->getBookCount(); }}
class Book {
private $author;
private $title;
function __construct($title_in, $author_in) {
$this->author = $author_in;
$this->title = $title_in; }
function getAuthor() {
return $this->author; }
function getTitle() {
return $this->title; }
function getAuthorAndTitle() {
return $this->getTitle().' by '.$this->getAuthor(); }}
writeln( 'BEGIN TESTING PROXY PATTERN';
writeln('');
$proxyBookList = new ProxyBookList();
$inBook = new Book('PHP for Cats','Larry Truett');
$proxyBookList->addBook($inBook);
writeln('test 1 - show the book count after a book is added');
writeln($proxyBookList->getBookCount());
writeln('');
writeln('test 2 - show the book');
$outBook = $proxyBookList->getBook(1);
writeln($outBook->getAuthorAndTitle());
writeln('');
$proxyBookList->removeBook($outBook);
writeln('test 3 - show the book count after a book is removed');
writeln($proxyBookList->getBookCount());
writeln('');
writeln('END TESTING PROXY PATTERN');
function writeln($line_in) {
echo $line_in."<br/>"; }?>
BEGIN TESTING PROXY PATTERN
test 1 - show the book count after a book is added
test 2 - show the book
PHP for Cats by Larry Truett
test 3 - show the book count after a book is removed
END TESTING PROXY PATTERN ttp://sourcemaking.com/design_patterns/proxy/
Краткая историческая справка
Паттерн Interpreter
По паттерну найти исторических сведеней не удалось. Россмотрены такие сайты:
Паттерн Interpreter (интерпетатор)
Назначение паттерна Interpreter
Для заданного языка определяет представление его грамматики, а также интерпретатор предложений этого языка.
Отображает проблемную область в языке, язык в грамматику, а грамматику в иерархии объектно-ориентированного проектирования.
Решаемая проблема
Пусть в некоторой, хорошо определенной области периодически случается некоторая проблема. Если эта область может быть описана некоторым “языком“, то проблема может быть легко решена с помощью “интерпретирующей машины“.
Обсуждение паттерна Interpreter
Паттерн Interpreter определяет грамматику простого языка для проблемной области, представляет грамматические правила в виде языковых предложений и интерпретирует их для решения задачи. Для представления каждого грамматического правила паттерн Interpreter использует отдельный класс. А так как грамматика, как правило, имеет иерархическую структуру, то иерархия наследования классов хорошо подходит для ее описания.
Абстрактный базовый класс определяет метод interpret(), принимающий (в качестве аргумента) текущее состояние языкового потока. Каждый конкретный подкласс реализует метод interpret(), добавляя свой вклад в процесс решения проблемы.
Структура паттерна Interpreter
Паттерн Interpreter моделирует проблемную область с помощью рекурсивной грамматики. Каждое грамматическое правило может быть либо составным (правило ссылается на другие правила) либо терминальным (листовой узел в структуре ”дерево”).
Для рекурсивного обхода ”предложений” при их интерпретации используется паттерн Composite.
UML-диаграмма классов паттерна Interpreter
Пример паттерна Interpreter
Паттерн Intepreter определяет грамматическое представление для языка и интерпретатор для интерпретации грамматики. Музыканты являются примерами интерпретаторов. Тональность и продолжительность звуков могут быть описаны нотами. Такое представление является музыкальным языком. Музыканты, используя ноты, способны воспроизвести оригинальные частоту и длительность каждого представленного звука.
Использование паттерна Interpreter
Определите “малый“ язык, “инвестиции” в который будут оправданными.
Разработайте грамматику для языка.
Для каждого грамматического правила (продукции) создайте свой класс.
Полученный набор классов организуйте в структуру с помощью паттерна Composite.
В полученной иерархии классов определите метод interpret(Context).
Объект Context инкапсулирует информацию, глобальную по отношению к интерпретатору. Используется классами во время процесса ”интерпретации”.
Особенности паттерна Interpreter
Абстрактное синтаксическое дерево интерпретатора пример паттерна Composite.
Для обхода узлов дерева может применяться паттерн Iterator.
Терминальные символы могут разделяться c помощью Flyweight.
Паттерн Interpreter не рассматривает вопросы синтаксического разбора. Когда грамматика очень сложная, должны использоваться другие методики.
Реализация паттерна Interpreter
Совместное использование паттернов Interpreter и Template Method
Рассмотрим задачу интерпретирования (вычисления) значений строковых представлений римских чисел. Используем следующую грамматику.
romanNumeral ::= {thousands} {hundreds} {tens} {ones}
thousands,hundreds,tens,ones ::= nine | four | {five} {one} {one} {one}
nine ::= "CM" | "XC" | "IX"
four ::= "CD" | "XL" | "IV"
five ::= 'D' | 'L' | 'V'
one ::= 'M' | 'C' | 'X' | 'I'
Для проверки и интерпретации строки используется иерархия классов с общим базовым классом RNInterpreter, имеющим 4 под-интерпретатора. Каждый под-интерпретатор получает "контекст" (оставшуюся неразобранную часть строки и накопленное вычисленное значение разобранной части) и вносит свой вклад в процесс обработки. Под-переводчики просто определяют шаблонные методы, объявленные в базовом классе RNInterpreter.
#include <iostream.h>
#include <string.h>
class Thousand;
class Hundred;
class Ten;
class One;
class RNInterpreter{
public:
RNInterpreter(); // ctor for client
RNInterpreter(int){}
// ctor for subclasses, avoids infinite loop
int interpret(char*); // interpret() for client
virtual void interpret(char *input, int &total) {
// for internal use
int index;
index = 0;
if (!strncmp(input, nine(), 2)) {
total += 9 * multiplier();
index += 2; }
else if (!strncmp(input, four(), 2)) {
total += 4 * multiplier();
index += 2; } else {
if (input[0] == five()) {
total += 5 * multiplier();
index = 1; }
else
index = 0;
for (int end = index + 3; index < end; index++)
if (input[index] == one())
total += 1 * multiplier();
else
break; }
strcpy(input, &(input[index]));
} // remove leading chars processed
protected:
// cannot be pure virtual because client asks for instance
virtual char one(){}
virtual char *four(){}
virtual char five(){}
virtual char *nine(){}
virtual int multiplier(){}
private:
RNInterpreter *thousands;
RNInterpreter *hundreds;
RNInterpreter *tens;
RNInterpreter *ones;};
class Thousand: public RNInterpreter{
public:
// provide 1-arg ctor to avoid infinite loop in base class ctor
Thousand(int): RNInterpreter(1){}
protected:
char one() {
return 'M'; }
char *four() {
return ""; }
char five() {
return '\0'; }
char *nine() {
return ""; }
int multiplier() {
return 1000; }};
class Hundred: public RNInterpreter{
public:
Hundred(int): RNInterpreter(1){}
protected:
char one() {
return 'C'; }
char *four() {
return "CD"; }
char five() {
return 'D'; }
char *nine() {
return "CM"; }
int multiplier() {
return 100; }};
class Ten: public RNInterpreter{
public:
Ten(int): RNInterpreter(1){}
protected:
char one() {
return 'X'; }
char *four() {
return "XL"; }
char five() {
return 'L'; }
char *nine() {
return "XC"; }
int multiplier() {
return 10; }};
class One: public RNInterpreter{
public:
One(int): RNInterpreter(1){}
protected:
char one() {
return 'I'; }
char *four() {
return "IV"; }
char five() {
return 'V'; }
char *nine() {
return "IX"; }
int multiplier() {
return 1; }};
RNInterpreter::RNInterpreter(){
// use 1-arg ctor to avoid infinite loop
thousands = new Thousand(1);
hundreds = new Hundred(1);
tens = new Ten(1);
ones = new One(1);}
int RNInterpreter::interpret(char *input){
int total;
total = 0;
thousands->interpret(input, total);
hundreds->interpret(input, total);
tens->interpret(input, total);
ones->interpret(input, total);
if (strcmp(input, ""))
// if input was invalid, return 0
return 0;
return total;}
int main(){
RNInterpreter interpreter;
char input[20];
cout << "Enter Roman Numeral: ";
while (cin >> input) {
cout << " interpretation is "
<< interpreter.interpret(input) << endl;
cout << "Enter Roman Numeral: "; }}
Вывод программы:1
Enter Roman Numeral: MCMXCVI
interpretation is 1996
Enter Roman Numeral: MMMCMXCIX
interpretation is 3999
Enter Roman Numeral: MMMM
interpretation is 0
Enter Roman Numeral: MDCLXVIIII
interpretation is 0
Enter Roman Numeral: CXCX
interpretation is 0
Enter Roman Numeral: MDCLXVI
interpretation is 1666
Enter Roman Numeral: DCCCLXXXVIII
interpretation is 888
Паттерн Composite (компоновщик)
Назначение паттерна Composite
Используйте паттерн Composite если:
Необходимо объединять группы схожих объектов и управлять ими.
Объекты могут быть как примитивными (элементарными), так и составными (сложными). Составной объект может включать в себя коллекции других объектов, образуя сложные древовидные структуры. Пример: директория файловой системы состоит из элементов, каждый их которых также может быть директорией.
Код клиента работает с примитивными и составными объектами единообразно.
Описание паттерна Composite
Управление группами объектов может быть непростой задачей, особенно, если эти объекты содержат собственные объекты.
Для военной стратегической игры ”Пунические войны ”, описывающей военное противостояние между Римом и Карфагеном (см. раздел Порождающие паттерны), каждая боевая единица (всадник, лучник, пехотинец) имеет свою собственную разрушающую силу. Эти единицы могут объединяться в группы для образования более сложных военных подразделений, например, римские легионы, которые, в свою очередь, объединяясь, образуют целую армию. Как рассчитать боевую мощь таких иерархических соединений?
Паттерн Composite предлагает следующее решение. Он вводит абстрактный базовый класс Component с поведением, общим для всех примитивных и составных объектов. Для случая стратегической игры - это метод getStrength() для подсчета разрушающей силы. Подклассы Primitive and Composite являются производными от класса Component. Составной объект Composite хранит компоненты-потомки абстрактного типа Component, каждый из которых может быть также Composite.
UML-диаграмма классов паттерна Composite
Для добавления или удаления объектов-потомков в составной объект Composite, класс Component определяет интерфейсы add() и remove().
Реализация паттерна Composite
Применим паттерн Composite для нашей стратегической игры. Сначала сформируем различные военные соединения римской армии, а затем рассчитаем разрушающую силу.
#include <iostream>
#include <vector>
#include <assert.h>
// Component
class Unit
{
public:
virtual int getStrength() = 0;
virtual void addUnit(Unit* p) {
assert( false);
}
virtual ~Unit() {}
};
// Primitives
class Archer: public Unit
{
public:
virtual int getStrength() {
return 1;
}
};
class Infantryman: public Unit
{
public:
virtual int getStrength() {
return 2;
}
};
class Horseman: public Unit
{
public:
virtual int getStrength() {
return 3;
}
};
// Composite
class CompositeUnit: public Unit
{
public:
int getStrength() {
int total = 0;
for(int i=0; i<c.size(); ++i)
total += c[i]->getStrength();
return total;
}
void addUnit(Unit* p) {
c.push_back( p);
}
~CompositeUnit() {
for(int i=0; i<c.size(); ++i)
delete c[i];
}
private:
std::vector<Unit*> c;
};
// Вспомогательная функция для создания легиона
CompositeUnit* createLegion()
{
// Римский легион содержит:
CompositeUnit* legion = new CompositeUnit;
// 3000 тяжелых пехотинцев
for (int i=0; i<3000; ++i)
legion->addUnit(new Infantryman);
// 1200 легких пехотинцев
for (int i=0; i<1200; ++i)
legion->addUnit(new Archer);
// 300 всадников
for (int i=0; i<300; ++i)
legion->addUnit(new Horseman);
return legion;
}
int main()
{
// Римская армия состоит из 4-х легионов
CompositeUnit* army = new CompositeUnit;
for (int i=0; i<4; ++i)
army->addUnit( createLegion());
cout << "Roman army damaging strength is "
<< army->getStrength() << endl;
// …
delete army;
return 0;
}
Следует обратить внимание на один важный момент. Абстрактный базовый класс Unit объявляет интерфейс для добавления новых боевых единиц addUnit(), несмотря на то, что объектам примитивных типов (Archer, Infantryman, Horseman) подобная операция не нужна. Сделано это в угоду прозрачности системы в ущерб ее безопасности. Клиент знает, что объект типа Unit всегда будет иметь метод addUnit(). Однако его вызов для примитивных объектов считается ошибочным и небезопасным.
Можно сделать систему более безопасной, переместив метод addUnit() в составной объект CompositeUnit. Однако при этом возникает следующая проблема: мы не знаем, содержит ли объект Unit метод addUnit().
Рассмотрим следующий фрагмент кода.
class Unit
{
public:
virtual CompositeUnit* getComposite() {
return 0;
}
// …
};
// Composite
class CompositeUnit: public Unit
{
public:
void addUnit(Unit* p);
CompositeUnit* getComposite() {
return this;
}
// …
};
В абстрактном базовом классе Unit появился новый виртуальный метод getComposite() с реализацией по умолчанию, которая возвращает 0. Класс CompositeUnit переопределяет этот метод, возвращая указатель на самого себя. Благодаря этому методу можно запросить у компонента его тип. Если он составной, то можно применить операцию addUnit().
if (unit->getComposite())
{
unit->getComposite()->addUnit( new Archer);
}
Результаты применения паттерна Composite
Достоинства паттерна Composite
В систему легко добавлять новые примитивные или составные объекты, так как паттерн Composite использует общий базовый класс Component.
Код клиента имеет простую структуру примитивные и составные объекты обрабатываются одинаковым образом.
Паттерн Composite позволяет легко обойти все узлы древовидной структуры
Недостатки паттерна Composite
Неудобно осуществить запрет на добавление в составной объект Composite объектов определенных типов. Так, например, в состав римской армии не могут входить боевые слоны.