Паттерны проектирования

Download Report

Transcript Паттерны проектирования

Slide 1

Паттерны проектирования


Slide 2

Что такое паттерны?


Идея!


Slide 3

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


Компания выпустила игру SimDuck – успешный имитатор утиного пруда, в
котором плавают и крякают утки разных видов.


Slide 4

Теперь утки будут летать!


Для этого необходимо в суперкласс Duck добавить метод fly()


Slide 5

Побочные эффекты



В программе имитаторе начали летать все утки! Даже резиновые!
Локальное изменение кода привело к нелокальному побочному эффекту!


Slide 6

Вариант разрешения ситуации


Можно переопределить метод fly() в
классе RubberDuck() и оставить тело
метода пустым.



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


Slide 7

Тест
Какие из перечисленных недостатков относятся к
применению наследования для класса Duck?
1. Дублирование кода в подклассах
2. Трудности с изменением поведения на стадии
выполнения
3. Уток нельзя научить танцевать
4. Трудности с получением информации обо всех
аспектах поведения уток
5. Утки не могут летать и крякать одновременно
6. Изменения могут оказать непредвиденное влияние
на другие классы



Slide 8

Еще один вариант разрешения ситуации







Если спецификация программы будет меняться и
будут добавляться новые классы, то придется
искать и изменять методы fly() и quack() ВЕЧНО!
Более простой (чем пустое переопределение
методов) способ заставить только некоторых уток
летать и крякать – использовать интерфейсы!
При этом методы fly() и quack() необходимо
исключить из родительского суперкласса Duck.
Только те утки, которые должны летать и крякать,
реализуют соответствующий интерфейс.


Slide 9

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


Slide 10

Использование интерфейса.
Недостатки
Наследование не является оптимальным в данном
случае.
 Применение интерфейса частично решает проблему
(летают только те утки, которые реализуют интерфейс),
но создает новую – невозможность повторного
использования кода, т.к. интерфейсы не имеют
реализации.
 Если утки должны летать по-разному, то применение
интерфейса в таком виде невозможно.
 Единственная постоянная вещь в вашей программе –
изменения! Иначе приложение будет не востребовано!



Slide 11

Первый принцип проектирования
Выделите аспекты приложения, которые могут изменяться, и
отделите их от тех, которые всегда остаются постоянными!
 Другая формулировка этого принципа:”Выделите переменные
составляющие и инкапсулируйте их, чтобы позднее их
можно было изменять или расширять без воздействия на
постоянные составляющие!”


Если не считать проблем с fly() и quack() класс Duck работает
хорошо.
 fly() и quack() – изменяющиеся части класса Duck в зависимости от
подкласса.
 Чтобы отделить эти переменные аспекты поведения от класса,
необходимо вынести их за пределы класса и создать новый набор
классов для представления этих аспектов.



Slide 12

Выделение аспектов


Slide 13

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







Для представления каждого аспекта поведения FlyBehavior или
QuackBehavior будет использоваться интерфейс, а каждая
реализация аспекта поведения будет представлена реализацией
этого интерфейса (классами поведения).
Ранее поведение реализовывалось либо в классе Duck, либо в его
подклассах, т.е. зависело от реализации.
В новом варианте архитектуры подклассы Duck используют
поведение, представленное интерфейсами.


Slide 14

Почему интерфейс?
Интерфейс в данном случае означает супертип. Главной целью
применения полиморфизма посредством программирования на
уровне супертипа является отсутствие жесткой привязки к
конкретному объекту во время выполнения.
 Программирование на уровне реализации


Dod d=new Dog();
d.bark();


Программирование на уровне супертипа
Animal an=new Dog();
An.getSound();


Slide 15

Реализация поведения уток


Slide 16

Преимущества использования подхода с
классами поведения






Возможно использовать поведение и в других
классах, потому что это поведение не скрывается в
классах Duck.
Можно добавлять новые аспекты поведения без
изменения существующих классов поведения и без
последствий для классов Duck.
Что необходимо сделать в новой архитектуре, что
бы утки могли летать на реактивной тяге?


Slide 17

Интеграция поведения с классом Duck
1.

2.

Сначала в класс Duck включаются две переменные
flyBehavior и quackBehavior с типом интерфейса поведения.
Каждый объект на стадии выполнения присваивает этим
переменным полиморфные значения.
Методы fly() и quack() удаляются из класса Duck и
заменяются аналогичными методами performFly() и
performQuack().


Slide 18

Интеграция поведения с классом Duck





Вместо того, что бы выполнять действие самостоятельно,
объект Duck поручает эту работу объекту, на который
ссылается quachBehavior.
В этой части кода совершенно не интересно, что это за
объект. Лишь бы он умел выполнять quack().


Slide 19

Инициализация поведения


Slide 20

Тестирование кода. №1


Slide 21

Тестирование кода №2


Slide 22

Тестирование кода №3


Slide 23

Запуск кода


Slide 24

Общий вид. Третий принцип

Отдавайте предпочтение композиции перед наследованием!


Slide 25

Задание


Slide 26

Решение


Slide 27

Исключительные ситуации в
Java и обработка событий


Slide 28

Обработка ошибок
Назначение механизма обработки
исключительных ситуаций – передача
данных из того места кода, где возник сбой,
обработчику ошибок, который может
справиться с ними.
 При возникновении ошибок программа
должна сделать одно из двух:





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


Slide 29

Наиболее “популярные” виды
ошибок
Ошибки ввода (неправильный адрес
сайта)
 Сбои оборудования (принтер отключен,
сайт недоступен)
 Физические ограничения (переполнен
диск, исчерпана память)
 Ошибки программирования (неверная
работа методов)
При возникновении ошибки возбуждается
объект, инкапсулирующий информацию об
ошибке.



Slide 30

Иерархия наследования
исключительных ситуаций
Throwable

Error



Exception

IOException

Runtime
Exception


Error – внутренние ошибки, связанные с исчерпанием ресурсов.
Возбудить объект подобного типа программист не может!
 Exception – основная иерархия ошибок, которой следует уделять
внимание.



Slide 31

Иерархия Exception


Исключительные ситуации RuntimeException возникают
вследствие ошибок программирования. Все другие
исключительные ситуации – стечение обстоятельств.
RuntimeException

другие

Правило:
Неверное
приведениеситуация
типов RuntimeException, то это Ваша
есливозникла
исключительная
вина!
 Выход за допустимые пределы массива

Попытка
обратиться
к объекту
ссылке null –
Все ошибки,
производные
от класса
Error по
и RuntimeError,
неконтролируемые,
все 
остальные
– контролируемые.
Обрыв файла



Попытка соединиться с неправильным URL
Попытка обнаружить несуществующий объект класса Class


Slide 32

Объявление исключительных
ситуаций, возбуждаемых методом


Идея – метод предсказывает, какие ошибки могут возникнуть.
class MyAnimation{
public Image loadImage(String s) throws IOException,
MalformedURLException{

}
}



Неконтролируемые исключительные ситуации от классов
Error и RuntimeException объявлять не нужно. Пример
class MyAnimation{
void drawImage(int i) throws ArrayIndexOutOfBoundsException{

}
}


Slide 33

Возбуждение исключительных
ситуаций


Пример. Метод ReadData считывает из файла размером 1024
символа, однако чтение прервалось после 733 символа.

String ReadData (BufferReader in)
throws EOFException{

while(…)
{ if (ch == 1) //обнаружен конец файла
{ if (n < len )
EOFException e=new EOFException();
throw e;
}

} return s;
}

Если существует подходящий
класс исключений:
• Найти подходящий класс
исключений.
2. Создать объект этого
класса.
3. Возбудить его.


Slide 34

Создание классов
исключительных ситуаций


Если в программе возникает ситуация, не предусмотренная
ни одним из существующих классов исключения, достаточно
создать свой собственный класс от Exception или его
потомков (IOException).
class FileFormatException extend IOException{
public FileFormatException (){} //конструктор по умолчанию
Что(String
дальше?
public FileFormatException
g){ //”подробный” конструктор
super(g);}
}

String ReadData (BufferReader in) throws FileFormatException{
while(…)
{ if (ch == 1) //обнаружен конец файла
{ if (n < len ) throw new FileFormatException();}
…}
return s; }


Slide 35

Перехват исключительных
ситуаций (try/catch)
public void read (BufferReader reader){  Если оператор в секции try
try{
вызвал исключение, то
boolean done=false;
программа пропускает
while(!done)
оставшиеся операторы и
{ String line=reader.readLine();
переходит в секцию catch.
if (line == null) // конец файла
done = true;
 Стандартный метод
else
readLine() может возбуждать
{Обработка строки line;} }
исключение IOException.
}
 Если в секции catch нет
catch (IOException exception){
обработчика для такого
exception.printStackTrace();}
исключения, то метод
}

останавливает работу.


Slide 36

Распространение
исключительных ситуаций


Если в методе readLine() возникла ошибка, то можно
переложить обработку на вызывающий метод. Для этого
достаточно указать, что метод readLine() может возбуждать
исключительную ситуацию IOException.
public void read (BufferReader reader) throws IOException{
boolean done=false;
while(!done)
{ String line=reader.readLine();
if (line == null) // конец файла
done = true;
else
{Обработка строки line;} }
}


Slide 37

Перехват нескольких исключений

try {…}
catch (MalformedURLException e1){
…}
catch (UnknownHostException e2){
…}
catch (IOException e3){
…}



Объекты (e1, e2, e3)
исключительных
ситуаций могут
содержать информацию
о ней - e3.getMessage();
Тип объекта можно
определить
e3.getClass().getName()
.


Slide 38

Повторное возбуждение
исключительной ситуации


Иногда нужно перехватить исключение, сделать некоторые
действия и отправить исключение на дальнейшую обработку
в вызывающий метод:
Graphics g = image.getGraphics();
try{ код, который может вызвать исключение;}
catch(MalformedURLException e){
g.dispose();
throw e;}



Обработка исключения и передача нового:
Graphics g = image.getGraphics();
try{ код, который может вызвать исключение;}
catch(MalformedURLException e){
g.dispose();
throw new Exception(“My new exception”);}


Slide 39

Раздел finally
Graphics g = image.getGraphics();
try{ код, который может вызвать исключение;}
catch(IOException e){}
finally{
g.dispose();}

Graphics g = image.getGraphics();
try{ код, который может вызвать исключение;}
finally{
g.dispose();}


Slide 40

Обработка событий


Slide 41

Модель событий




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


Slide 42

Модель событий







В объекте, который порождает событие, содержится список
слушателей, заинтересованных в получении уведомления,
что событие произошло. Источник оповещает слушателя
путем вызова спецметода и передачи события EventObject.
Регистрация события происходит с помощью метода
addXXXListener, принадлежащего компоненту, вызвавшему
событие, где XXX – тип обрабатываемого события
Метод getSource() класса event возвращает объект,
вызвавший событие
Удаление слушателя производится методом
removeXXXListener


Slide 43


Slide 44

Модель событий


В интерфейсе слушателя может определятся несколько
методов, например для MouseEvent. Если какой-либо класс
реализовывает интерфейс, то он обязан реализовать все
его методы. Это не удобно, если нас интересует всего один
из нескольких. Для этого для каждого интерфейса
слушателя, содержащего несколько методов, определен
простой класс адаптер, в котором эти методы пустые. Если
необходимо, то их можно переопределять.

Public class windowclosing extends WindowAdapter{
public void windowClosing(WindowEvent e){
………..}
}


Slide 45


Slide 46

Пример
public class Button2 extends JApplet {
private JButton b1 = new JButton("Button 1");
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String name = ((JButton)e.getSource()).getText();
txt.setText(name); }
}
private ButtonListener blistener = new ButtonListener();
public void init() {
b1.addActionListener(blistener);
......}
}


Slide 47

Пример
Можно не описывать как в предыдущем примере класс,
реализующий интерфейс, а затем создавать его экземпляр.
Следующий пример реализует те же действия, но более
коротким путем.
private ActionListener bl = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String name = ((JButton)e.getSource()).getText();
txt.setText(name);
}


Slide 48

Некоторые важные интерфейсы


MouseMotionListener и MouseMotionEvent:





mouseMoved()
mouseDragged()

MouseListener и MouseEvent:





mousePressed()
mouseReleased()
mouseClicked()
Методы события getX() и getY() возвращают координаты


Slide 49

Некоторые важные интерфейсы


KeyListener и KeyEvent:





keyTyped()
keyPressed()
keyReleased()
Метод события KeyEvent getKeyChar() возвращает символ


Slide 50



Благодарю за внимание!