Математическое обоснование solid принципов - Евгений...

74
HWdTech, LLC обоснование с помощью логики Хоара S.O.L.I.D.

Upload: dev2devconf

Post on 27-Jul-2015

67 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

HWdTech, LLC

обоснование с помощью логики Хоара

S.O.L.I.D.

Page 2: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

14 лет преподаю ИМИТ, ФКН ОмГУ ИТ-компании Школа программиста 10 лет разрабатываю ПО разработчик, архитектор, PM, руководство до 70 человек

Тюменцев Евгений

Page 3: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Предыстория вопроса

Формулировка принципов, ключевые события

03

Page 4: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Open-Closed Principle Программные объекты должны быть открыты для расширения, но в тоже время закрыты для модификации.

04

Page 5: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Liskov Substitution Principle Функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.

05

Page 6: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пример нарушения LSP 06

class Rectangle

{

double height, width;

public:

double getHeight() const { return height; }

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

double getWidth() const { return width; }

virtual 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); } }

class Square: public Rectangle

{

public:

virtual void setHeight(int value)

{

super.setHeight(value);

super.setWidth(value);

}

virtual void setWidth(int value)

{

super.setHeight(value);

super.setWidth(value);

}

}

Page 7: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Dependency Inversion Principle Высокоуровневые компоненты не должны зависеть от низкоуровневых компонент. И те, и те должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

07

Page 8: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пример нарушения DIP 08

void Copy()

{

int ch;

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

{

WritePrinter(c);

}

}

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); } }

enum OutputDevice { printer, disk }; void Copy(OutputDevice dev) { int c; while ((c = ReadKeyboard()) != EOF) { if (dev == printer) WritePrinter(c); else WriteDisk(c); } }

Page 9: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пример соответствия DIP 09

class IReader

{

public:

virtual int Read() = 0;

}

class IWriter

{

public:

virtual void Write(char) = 0;

}

void Copy(IReader& r, IWriter& w)

{

int c;

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

w.Write(c);

}

Page 10: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Interface Segregation Principle Класс не должен зависеть от интерфейсов, которые он не использует.

10

Page 11: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

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

11

Page 12: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

switch

If /else – if/else enum new

Операторы приведения типа Магические константы

Примеры конструкций, нарушающих OCP 12

Page 13: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пример нарушения ISP 13

class matrix

{

public:

virtual int size() const = 0;

virtual void get_ij(int I, int j, double &value) const = 0;

virtual void set_ij(int I, int j, double value) = 0;

};

class diagonal: public matrix

{

public:

void set_ij(int I, int j, double value)

{

// ? Что делать, если i != j

} };

Page 14: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Что-о-о? switch, new? Ты кто такой?

Page 15: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

LSP, OOPSLA’87 Если для каждого объекта o1 типа S существует объект o2 типа T, который для всех программ P определен в терминах T, то поведение P не изменится, если o1 заменить на o2 при условии, что S является подтипом T

История вопроса 15

Barbara Liskov

Page 16: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

LSP, OOPSLA’87

1988, опубликованы в

Object-oriented Software

Construction

История вопроса 16

Bertran Meyer

Page 17: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

LSP, OOPSLA’87

1988, опубликованы в

Object-oriented Software Construction

1994, A behavioral notion

of subtyping

История вопроса 17

Barbara Liskov

Page 18: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

LSP, OOPSLA’87

1988, опубликованы в

Object-oriented Software Construction

1994, A behavioral notion of subtyping

1995-96, статьи в The

C++ Report

“Uncle Bob”

Автор названия

S.O.L.I.D.

История вопроса 18

Robert Martin

Page 19: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

LSP, OOPSLA’87 1988, опубликованы в Object-oriented Software Construction 1994, A behavioral notion of subtyping 1995-96, статьи в The C++ Report

2009, Spolsky VS Uncle Bob they all sounded to me like extremely bureaucratic programming that came from the mind of somebody that has not written a lot of code…

История вопроса 19

Joel Spolsky

Page 20: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Выводимость и логика Хоара

Доказательство свойств программ

20

Page 21: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Выводимость 21

Пусть L – множество формул, B – формула.

Тогда L ⊦ B, если ∃ B1, B2, …, Bn , что

1.Bn – это B,

2.Bi – это либо формула из L, либо аксиома,

либо общезначимая формула, либо формула полученная при помощи правила вывода

Page 22: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Исчисление высказываний 22

Page 23: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Modus Ponens (правило вывода) 23

Page 24: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

1969 г. An Axiomatic Basis for Computer Programming

1971 г. Procedures and Parameters: An Axiomatic Approach

1980 г. премия Тьюринга

1990 г. Медаль “Пионер компьютерной техники”

2000 г. рыцарский титул за заслуги в области образования и компьютерной техники, премия

Киото

Логика Хоара

Чарльз Хоар

24

Page 25: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

{pred} statement {post}

Пример: {x == 42} y=x+1; {y == 43 ^ x == 42}

Тройка Хоара 25

Page 26: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

{P} skip {P}

Аксиома пустого оператора 26

Page 27: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пример: {(y+1)*3+w*(y+1+3) ==z} x=y+1; { x*3+ w*(x+3)==z}

Аксиома оператора присваивания 27

{P[E/x]} x := E {P}

Page 28: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома оператора цикла 28

{P ^ B} S {P} ╞ {P} while B do S done {B’ ^ P}

Page 29: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома условного оператора 29

{B ^ P} S {Q}, {B’ ^P} T {Q}╞ {P} if B then S else T endif {Q}

{B ^ P} S {Q}╞ {P} if B then S endif {Q}

Page 30: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиомы вывода 30

P1 → P, {P} S {Q}, Q → Q1 ╞ {P1} S {Q1}

P1 → P, {P} S {Q} ╞ {P1} S {Q} {P} S {Q}, Q → Q1 ╞ {P} S {Q1}

Page 31: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома вывода и условный оператор 31

Пусть L = {{P} f {Q}}, B – предикат. P^B→P (A3 исчисления высказываний) {P^B}f{Q} (аксиома вывода) {P}if (B) then f endif {Q} (аксиома условного оператора)

Page 32: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Неявные предположения 32

{P ^ B} с {Q}

{P} assert(B); c {Q}

Page 33: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Упрощение кода и аксиома вывода 33

node* insert(node& c, int v) { node *n = new node; n -> val = v; if(c.next) { n -> next = c.next; } else { n -> next = 0; } c.next = n; return n; }

null

Page 34: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома оператора цикла 34

Page 35: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Закольцованный список с буферным элементом 35

node* insert(node& c, int v) { node *n = new node; n -> val = v; if(c.next) { n -> next = c.next; } else { n -> next = 0; } c.next = n; return n; }

Page 36: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Закольцованный список с буферным элементом 36

node* insert(node& c, int v) { node *n = new node; n -> val = v; n -> next = c.next; c.next = n; return n; }

Page 37: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома композиции 37

{P} S {Q}, {Q} T {R} ╞ {P} S;T {R}

Page 38: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Контрактное программирование 38

• Предусловия

• Постусловия

• Инварианты

Page 39: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Модульные тесты 39

Arrange Act

Assert

Page 40: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

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

{P} S {Q}, {Q} T {R} ╞ {P} S;T {R}

Page 41: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

S.O.L.I.D.

Математическое обоснование

41

Page 42: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Аксиома оператора цикла 42

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

https://ru.wikipedia.org/wiki/Повторное_использование_кода

Page 43: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Когда происходит повторное использование?

Если нужно внести изменение в существующее приложение, то мы

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

тоже приложение, но с новой функциональностью.

43

Page 44: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

44

Выводимость прямо предписывает строить новые

тройки из предыдущих!

Page 45: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Чем грозят правки кода? 45

B1, B2, …, A1

B3, A1, …, A2

B5, A2, …, A3

A2, B6, …, A4

A2, A4, …, A5

Page 46: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Программа рассыпается как карточный домик! 46

B1, B2, …, A1

B3, A1, …, A2

B5, A2, …, A3

A2, B6, …, A4

A2, A4, …, A5

Page 47: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Из закрытости следует выводимость! 47

Программные объекты должны быть открыты для расширения, но в тоже время закрыты для модификации.

The Open-Closed Principle

Page 48: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Матрицы 48

class matrix {

int size;

double *body;

public:

matrix(int s): size(s) {

body = new double[s*s];

}

void transform() {

}

double det() const {

}

};

Page 49: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Матрицы: добавляем matrix() 49

class matrix {

int size;

double *body;

public:

matrix(int s): size(s) {

body = new double[s*s];

}

void transform() {

}

double det() const {

}

};

class matrix {

public:

matrix(): size(0), body(0) {

}

};

Page 50: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Матрицы: придется менять методы 50

class matrix {

int size;

double *body;

public:

matrix(): size(0), body(0) {

}

matrix(int s): size(s) {

body = new double[s*s];

}

void transform() {

if(!body) throw exception();

}

double det() const {

if(!body) throw exception();

}

};

Page 51: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Что случилось? 51

Инвариант класса matrix: size > 0

Конструктор matrix() нарушил инвариант: size = 0

Предусловия методов изменились с size > 0 на size ≥ 0, но

{size ≥ 0} det {Q} не выводится из {size > 0} det {Q}

Следовательно, надо изменять сами методы

Page 52: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

52

Конструктор должен устанавливать инвариант класса

Конструкторы по умолчанию часто этого не делают

Стоит несколько раз подумать, прежде чем использовать конструктор по умолчанию

Page 53: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Импликация на множестве 53

Пусть P1 → P,

B = { x | P1(x) = 1},

A = { x | P (x) = 1}.

Тогда B ⊂ A.

P ⊨ P1. Говорят, что P – более слабое условие, P1 – более сильное.

A

B

Page 54: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Построим вывод 54

{S} cb {P}, {P} c {Q}, {Q} ca {R} ⊦ {S} cb; c; ca {R}

Пусть P → P1, Q1 → Q

Известно, что

{S} cb {P}, {P1} c {Q1}, {Q} ca {R}

Тогда

{P} c {Q} (аксиома вывода)

{S} cb; c; ca {R}

Page 55: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

А если импликации нет? 55

{S} cb {P}, {P} c {Q}, {Q} ca {R} ⊦ {S} cb; c; ca {R}

Пусть P ↛ P1, но P ^ P1 → P1

Известно, что

{S} cb {P}, {P1} c {Q}, {Q} ca {R}

Тогда

{P1} c {Q} ⊦ {P} if (P1) then c endif {Q}

{S} cb; if(P1) then c endif; ca {R}

Page 56: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Оператор расширения 56

Оператор расширения должен допускать: 1. Ослабление предусловий

2. Усиление постусловий

Page 57: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Метафора для оператора расширения 57

Page 58: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Для оператора расширения получаем 58

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

The Dependency Inversion Principle

Page 59: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

59

Как выглядит расширение?

Page 60: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Статический полиморфизм 60

template <class It, class Op> void for_each(It begin, It end, Op op) { for(; begin != end; ++begin) op(*begin); } int arr[] = {1, 2, 3, 4, 5}; for_each(arr, arr+6, max<int>());

Page 61: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Динамический полиморфизм 61

• указатель на функцию void (*f) (int i); f pf; pf(5);

• виртуальный метод class Shape { public: virtual void Draw() = 0; }; shape->Draw();

Page 62: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Метапрограмирование 62

void Counter::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } }

Page 63: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Что такое класс?

Это набор методов, возможно, виртуальных.

63

Page 64: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Условие для методов класса 64

Для каждого метода подкласса

1. предусловия могут быть ослаблены

2. Постусловия усилены

Page 65: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Liskov Substitution Principle Функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом.

65

Page 66: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Случай нескольких операторов расширения 66

Page 67: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Жирный интерфейс 67

Page 68: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пустой метод – метод, который подходит не для всех! 68

The Interface Segregation Principle Класс не должен зависеть от интерфейсов, которые он не

использует.

Page 69: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

А не для классов? 69

bool (*validator) (Document const & doc);

std::vector<validator> rules = …;

for (int i = 0; i < n; ++i)

if(!rules[i](document))

return false;

return true;

Page 70: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

В каком случае гарантированно удастся избежать жирного

интерфейса?

70

Page 71: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

The Single Responsibility Principle 71

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

Page 72: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

А как же функциональщина?

72

Page 73: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Мы пишем книги

73

Нерассказанные факты об императивном

программировании

oopguide.ru

Разработка серверов и серверных приложений

actorsmodel.ru

Page 74: Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30.05.2015

Пишите: [email protected]

Звоните: +7 913 150 22 04

slideshare.com/etyumentcev

http://hwdtech.ru

Тюменцев Евгений Александрович