58
УК 03.001.01-2011 Учебный курс. Обучение. ООП.

ук 03.001.02 2011

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: ук 03.001.02 2011

УК 03.001.01-2011 Учебный курс. Обучение. ООП.

Page 2: ук 03.001.02 2011

Базовые понятия

Page 3: ук 03.001.02 2011

Объектно-ориентированное программирование (ООП) – это методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.

(КН 02.002-1995, стр. 52)

• Основным элементом абстракции являются объекты

• Каждый объект является экземпляром определенного класса

• Классы образуют иерархию наследования

Page 4: ук 03.001.02 2011

Ключевые характеристики ООП

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

(КН 02.002-1995, стр. 55)

• Внешнее поведение

• Барьер абстракции

• Неоднозначность выделения абстракции

• Абстракция и объект реального мира – не одно и тоже (пример: вещественные числа и числа с плавающей точкой)

• Задача: является ли квадрат прямоугольником?

Page 5: ук 03.001.02 2011

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

(КН 02.002-1995, стр. 63)

• Сокрытие данных лишь частный случай инкапсуляции

• Поведение представляется только с помощью методов

• Задача о множествах объектов

Page 6: ук 03.001.02 2011

Модульность – это свойство системы, которая может была разложена на внутренне связные, но слабо связанные между собой модули.

(КН 02.002-1995, стр. 69)

• Что первично класс или модуль?

Иерархия – это упорядочивание абстракций, расположение их по уровням.

(КН 02.002-1995, стр. 71)

• “is a” (наследование)

• “part of” (агрегация, композиция)

Page 7: ук 03.001.02 2011

Принципы ООП

Page 8: ук 03.001.02 2011
Page 9: ук 03.001.02 2011

Открыто-замкнутый принцип

• Инкрементная разработка;

• Количество строк кода, которые пишет программист, в единицу времени ограничено;

• Если изменяется существующий код, то, скорее всего, прироста функционала не происходит;

• Повторное использование.

Программная сущность (класс, модуль, функция и т.д.) должна быть открыта для расширения, но закрыта для модификаций.

(СТ 02.003-1995, стр. 1)

Page 10: ук 03.001.02 2011

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

Поведение – это то, как объект действует и реагирует; поведение выражается в терминах состояния объекта и передачи сообщений.

(КН 02.002-1995, стр. 96) Необходимо избегать конструкций, которые создают закрытое

множество поведений: • switch (ПР 02.001) • Цепочка if/else (ПР 02.002) • Информация о типе во время выполнения (ПР 02.003) • Перечислимые типы (ПР 02.004) • Дублирование кода (ПР 01.001) • Все поля должны быть private (ПР 02.005) • Избегать использование глобальных переменных (ПР 01.002)

Page 11: ук 03.001.02 2011

Пример 1: Геометрические фигуры

class Shape

{

private ShapeType itsType;

private void DrawSquare();

private void DrawCircle();

public static void DrawAllShapes(Shape[] list)

{

for(Shape s : list)

{

switch (s.itsType)

{

case square:

DrawSquare();

break;

case circle:

DrawCircle();

break;

}

}

}

}

Page 12: ук 03.001.02 2011

interface Shape

{

void Draw();

}

class ShapeUtils

{

public static void DrawAllShapes(Shape[] list)

{

for(Shape s: list)

{

s.Draw();

}

}

}

class Square implements Shape

{

public void Draw()

{

}

}

Page 13: ук 03.001.02 2011

Пример 2: Рубрикатор

switch (article.Rubric) { case Auto: case Realty: filterPanels.Add(ucSearchOptions); break; case Job: switch (article.TypeID) { case Resume: filterPanels.Add(ucSeniorityPanel); filterPanels.Add(ucPricePanel); filterPanels.Add(ucWorkSchedulePanel); break; case Vacancy: filterPanels.Add(ucSeniorityPanel); filterPanels.Add(ucPricePanel); filterPanels.Add(ucWorkSchedulePanel); break; case Education: filterPanels.Add(ucPricePanel); break; } break; }

Page 14: ук 03.001.02 2011

siteRubricHelper.SetFilterOptions(panel);

public interface IRubricHelper

{

void SetFilterOptions(FilterPanel panel);

}

public class DefaultRubricHelper implements IRubricHelper

{

private FilterOptions ucSearchOptions;

public void SetFilterOptions(FilterPanel panel)

{

panel.Add(ucSearchOptions); }

}

Page 15: ук 03.001.02 2011

Стратегическая замкнутость

• Замкнутость не может быть 100%

• Замкнутость должна быть стратегической

• Проектирование ради стратегической замкнутости

• Паттерны проектирования – типовые примеры, обеспечивающие определенные виды замкнутости

Page 16: ук 03.001.02 2011

Принцип подстановки Б. Лисков

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

(СТ 02.004-1995, стр. 2)

Page 17: ук 03.001.02 2011

Пример 3: RTTI

void Draw(Shape s)

{

if (s instanceof Point)

{

DrawPoint(s as Point);

}

else if (s instanceof Circle)

{

DrawCircle(s as Circle);

}

else if(s instanceof Square)

{

DrawSquare(s as Square);

}

}

Page 18: ук 03.001.02 2011

Пример 4: Rectangle и Square class Rectangle

{

private double height;

private double width;

public double getHeight() { return height; }

public void setHeight(int value) { height = value; }

public double getWidth() { return width; }

public void setWidth(int value) { width = value; }

}

….

void f(Rectangle r)

{

r.setHeight (5);

r.setWidth (4);

Debug.Assert(r.getHeight() * r.getWidth() == 20);

}

class Square extends Rectangle

{

public void setHeight(int value)

{

super.setHeight(value);

super.setWidth(value);

}

public void setWidth(int value)

{

super.setHeight(value);

super.setWidth(value);

}

}

Page 19: ук 03.001.02 2011

• Построенные абстракции нельзя проверить на корректность сами по себе. Такую проверку можно выполнить лишь в контексте клиентов, использующих данные абстракции.

• Заранее построить очень гибкую модель “про запас” нельзя!

• Лучше использовать прототипирование

Прототип – самый простой вариант чего-либо, но содержащий самый сложный компонент.

Page 20: ук 03.001.02 2011

Принцип обращения зависимостей

Программа Copy void Copy()

{

int ch;

while ((ch = Keyboard()) != EOF)

{

WritePrinter(c);

}

}

enum OutputDevice

{

printer,

disk

};

void Copy(OutputDevice dev)

{

int c;

while ((c = ReadKeyboard()) != EOF)

{

if (dev == printer)

WritePrinter(c);

else

WriteDisk(c);

}

}

Page 21: ук 03.001.02 2011

interface IReader

{

int Read();

}

interface IWriter

{

void Write(char) = 0;

}

void Copy(IReader r, IWriter w)

{

int c;

while((c=r.Read()) != EOF)

w.Write(c);

}

Page 22: ук 03.001.02 2011

Схема зависимостей

Page 23: ук 03.001.02 2011

• Высокоуровневые модули не должны зависеть от низкоуровневых модулей. И те, и те должны зависеть от абстракций.

• Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

(СТ 02.005-1995, 6)

Page 24: ук 03.001.02 2011

СЛОИ

Page 25: ук 03.001.02 2011

Пример 5: Лампочка

class Lamp

{

public void TurnOn() {…}

public void TurnOff() {…}

}

class Button

{

private Lamp lamp;

public Button(Lamp lamp) {this.lamp = lamp;}

public void Detect()

{

if(GetPhisicalState())

{

lamp.TurnOn();

}

else

{

lamp.TurnOff();

}

}

}

Page 26: ук 03.001.02 2011

interface IButtonClient

{

void TurnOn();

void TurnOff();

}

class Button

{

private IButtonClient client;

public Button(IButtonClient client)

{

this.client = client;

}

public void Detect()

{

if (GetPhisicalState())

{

client.TurnOn();

}

else

{

client.TurnOff();

}

}

}

class Lamp

{

public void SwitchOn() {…}

public void SwitchOff() {…}

}

class LampAdapter implements IButtonClient

{

private Lamp lamp;

public LampAdapter(Lamp lamp)

{

this.lamp = lamp;

}

public void TurnOn()

{

lamp.SwitchOn();

}

public void TurnOff()

{

lamp.SwitchOff();

}

}

Page 27: ук 03.001.02 2011

• Сторонний код должен быть скрыт за обертками, реализующими собственные интерфейсы (ПР 04.001)

• Обертки можно строить к старому наследуемому коду

• Переписывание не всегда самый лучший путь

Page 28: ук 03.001.02 2011

Принцип соответствия интерфейсов

interface IButtonClient

{

void TurnOn();

void TurnOff();

}

Interface ITimerClient

{

void OnTimeout();

}

class Lamp implements IButtonCLient, ITimerClient

{

public void TurnOn() {…}

public void TurnOff() {…}

public void OnTimeout() {…}

}

class Button

{

private IButtonClient;

public Button(IButtonClient client) {…}

public void Detect() {…}

}

class Timer

{

private ITimerClient client;

public void Timeout()

{

client.OnTimeout();

}

}

Page 29: ук 03.001.02 2011

class EmergencyLamp extends Lamp

{

public override void OnTimeout()

{

// нельзя включать и отключать по таймеру,

// поэтому пустая реализация

}

}

class Lamp implements IButtonClient

{

}

class ButtonToTimerClientAdapter implements ITimerClient

{

// см. пример адаптера для IButtonClient

}

Interface IEmergencyClient

{

void OnAlert();

}

class ButtonToEmergencyClientAdapter implements IEmergencyClient

{

// см. пример адаптера для IButtonClient

}

Page 30: ук 03.001.02 2011

• ITimerClient, IEmergencyClient, IButtonCLient – это разные множества объектов, но которые частично пересекаются

• Жирные интерфейсы сигнализируют об ошибках в проектировании

• Жирных интерфейсов следует избегать (ПР 02.006) Исключение: применение паттерна Compositor

Page 31: ук 03.001.02 2011

Клиенты не должны зависеть от тех интерфейсов, которые они не используют

(СТ 02.006-1996, стр. 5)

Page 32: ук 03.001.02 2011

• Абстракции нельзя проверить сами по себе, вне контекста использования!!!

• Часто программисты делают предложения по функционалу, основываясь на текущих возможностях реализации

• Подход к программированию: программный код как результат решения конкретной задачи

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

Page 33: ук 03.001.02 2011

DI контейнеры как расширяемые фабрики

package examples.di;

public interface Greeting

{

String greet();

}

package examples.di.impl;

import examples.di.Greeting;

public class GreetingImpl implements Greeting

{

public String greet()

{

return "Hello World!";

}

}

Page 34: ук 03.001.02 2011

package examples.di;

public interface GreetingClient

{

void execute();

}

package examples.di.impl;

import examples.di.Greeting;

import examples.di.GreetingClient;

public class GreetingClientImpl implements GreetingClient

{

private Greeting greeting;

public GreetingClientImpl(Greeting greeting) { this.greeting = greeting; }

public void execute() { System.out.println(greeting.greet()); }

}

Page 35: ук 03.001.02 2011

package examples.di.main;

import examples.di.Greeting;

import examples.di.impl.GreetingClientImpl;

import examples.di.impl.GreetingImpl;

public class GreetingMain

{

public static void main(String[] args)

{

Greeting greeting = new GreetingImpl();

GreetingClientImpl greetingClient = new GreetingClientImpl(greeting);

greetingClient.execute();

}

}

Page 36: ук 03.001.02 2011

package examples.di.main;

import org.seasar.framework.container.S2Container;

import org.seasar.framework.container.factory.S2ContainerFactory;

import examples.di.GreetingClient;

public class GreetingMain3

{

private static final String PATH = "examples/di/dicon/GreetingMain3.dicon";

public static void main(String[] args)

{

S2Container container = S2ContainerFactory.create(PATH);

GreetingClient greetingClient = new GreetingClientImpl( container.getComponent("greeting”)

);

greetingClient.execute();

}

}

Page 37: ук 03.001.02 2011

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC"-//SEASAR//DTD S2Container 2.3//EN" "http://www.seasar.org/dtd/components23.dtd"> <components> <include path="aop.dicon"/> <component name="greeting” class="examples.di.impl.GreetingImpl"> <aspect>aop.traceInterceptor</aspect> </component> </components>

Page 38: ук 03.001.02 2011

package examples.di.impl;

import examples.di.Greeting;

import examples.di.GreetingClient;

public class GreetingClientImpl implements GreetingClient

{

private Greeting greeting;

public GreetingClientImpl()

{

S2Container container = S2ContainerFactory.create(PATH);

this.greeting = container.getComponent("greeting”);

}

public void execute() { System.out.println(greeting.greet()); }

}

Page 39: ук 03.001.02 2011

Принцип единой ответственности

Должна быть ровно одна причина для изменения класса

• Классы должны быть компактными (ПР 02.007)

• Следует избегать глубоких иерархий классов (ПР 02.008)

Page 40: ук 03.001.02 2011

Компоновка программы

• Разделяй и властвуй

• Структурирование

• Разная структура для разных объемов

• План выполнения запросов

• Объявления

Page 41: ук 03.001.02 2011

Модули

Каков критерий разбиения программы на модули?

Взаимосвязи между модулями? Принцип организации взаимосвязей?

Что первично: классы или модули?

Каким программным конструкциям соответствуют модули?

Какие цели преследуют модули?

Page 42: ук 03.001.02 2011

Принципы сцепления модулей

Принцип эквивалентности единиц повторного использования и релиза

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

(СТ 02.007-1996, стр. 4)

Page 43: ук 03.001.02 2011

Обобщенный принцип повторного использования

Классы, входящие в состав модуля повторно используются все вместе. Если Вы повторно используете один, то Вам доступны все остальные.

(СТ 02.007-1996, стр. 5)

Обобщенный принцип замкнутости

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

(СТ 02.007-1996, стр. 6)

Page 44: ук 03.001.02 2011

• В .Net модулем является сборка

• Разнесение классов по модулям нетривиальная задача

• Методы разнесения классов по модулям носит динамический характер

• Сначала создаются классы, затем выделяются модули

• Движущая сила: смещения акцента от возможности разработки до возможности повторного использования

• Один класс в одном файле (ПР 05.001)

Page 45: ук 03.001.02 2011

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

Принцип ациклических зависимостей.

Граф зависимостей между модулями должен быть ациклическим

(СТ 02.007-1996, стр. 6)

Page 46: ук 03.001.02 2011

Пример 5: Ациклический граф

Page 47: ук 03.001.02 2011

Пример 6: Циклический граф

Page 48: ук 03.001.02 2011
Page 49: ук 03.001.02 2011

Интеграционная штурмовщина

• Еженедельный билд

• Не успеваем вовремя его собрать, протестировать

• Большие накладные расходы на сборку билда

• Увеличиваются сроки между билдами

• Модули можно разрабатывать независимо друг от друга

• Релиз системы состоит из набора модулей, каждый из которых имеет свою версию

• Product Manager

Page 50: ук 03.001.02 2011

Стабильность

• Какие цели преследуют модули?

• Пример: программа копирования

• “Хорошая” зависимость – это зависимость от чего-то, что не слишком часто меняется

• Как измерить насколько зависимость хорошая?

Стабильность – способность системы функционировать, не изменяя собственную структуру и находиться в равновесии в течение определенного промежутка времени.

(Wikipedia.org)

Page 51: ук 03.001.02 2011

Принцип стабильных зависимостей

Модули должны зависеть только от более стабильных модулей.

(СТ 02.008-1996, стр. 8)

• Дизайн системы не может быть полностью стабильным

• Принцип обобщенной замкнутости

• Принцип единственной ответственности

Page 52: ук 03.001.02 2011

Метрики стабильности

• Центростремительная связность (Ca) – количество классов за пределами модуля, которые зависят от классов внутри модуля

• Центробежная связность (Ce) – количество классов внутри модуля, которые зависят от классов за пределами модуля

• Коэффициент нестабильности (I): I = Ce/(Ce+Ca) – I = 0 – максимально стабильный пакет

– I = 1 – максимально нестабильный пакет

Page 53: ук 03.001.02 2011

Принцип стабильных абстракций

Модули, которые максимально стабильны должны быть максимально абстрактны. Нестабильные модули должны быть конкретны. Абстрактность модуля должна быть обратно пропорциональна нестабильности.

(СТ 02.008-1996, стр. 11)

Абстрактность модуля = Абстрактные классы и интерфейсы/Общее число классов и интерфейсов

Page 54: ук 03.001.02 2011
Page 55: ук 03.001.02 2011

Итоги

• Изменения через написание нового без переписывания старого

• Полностью от переписывания отказаться нельзя, поэтому важно проектировать систему

• Цель проектирования – управлять замкнутостью

• Проектирование предшествует программированию

• Сначала классы, а потом модули

• Нет интеграционной штурмовщине

• Важно правильно разбивать систему на модули

• Интерфейсы рулят

Page 56: ук 03.001.02 2011

• Проектирование – Design Patterns

– Прототипирование как средство уменьшения рисков

• Существующий код – Рефакторинг

– Фасады

– Автоматическое тестирование

Page 57: ук 03.001.02 2011

Что дальше

• Контрактная модель программирования

• Design Patterns

• Рефакторинг

• Автоматическое тестирование

Page 58: ук 03.001.02 2011

Вопросы?