71
Математическое обоснование SOLID принципов Евгений Тюменцев HWdTech, LLC hwdtech.ru 10-я конференция .NET разработчиков 19 апреля 2015 dotnetconf.ru

математическое обоснование Solid принципов. Конференция dotnetconf (Челябинск, апрель 2015)

Embed Size (px)

Citation preview

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

SOLID принципов

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

HWdTech, LLC

hwdtech.ru

10-я конференция .NET разработчиков

19 апреля 2015

dotnetconf.ru

2

Докладчик

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

14 лет преподаю

ИМИТ, ФКН ОмГУ

ИТ-компании

Школа программиста

10 лет разрабатываю ПО

разработчик, архитектор,

PM, руководство до 70 человек

3

SOLID

5 архитектурных принципов

1988 г. Б. Мейер впервые описал

1995-96 г. Р. Мартин популяризировал, ввел

термин SOLID

4

The Open-Closed Principle

Программные объекты должны быть

открыты для расширения, но в тоже время

закрыты для модификации.

5

The Liskov Substitution Principle

Функции, которые используют ссылки на

объекты базовых классов, должны

использовать объекты производных

классов, ничего не зная о них.

6

The Dependency Inversion Principle

Высокоуровневые компоненты не должны

зависеть от низкоуровневых компонент. И

те, и те должны зависеть от абстракций

(оператора расширения).

7

The Interface Segregation Principle

Класс не должен зависеть от интерфейсов,

которые он не использует

8

The Single Responsibility Principle

Должна быть ровно одна причина для

изменения класса

9

Нарушают SOLID принципы

enum

switch

if – else – if

операторы приведения типа

new

10

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

11

Выводимость

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

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

1.Bn – это B,

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

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

12

Аксиомы исчисления высказываний

13

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

14

Логика Хоара

1969 г. An Axiomatic Basis for Computer Programming

1971 г. Procedures and Parameters: An Axiomatic Approach

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

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

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

техники, премия Киото

Чарльз Хоар

15

Тройка Хоара

{pred} statement {post}

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

16

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

{P} skip {P}

17

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

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

{ x*3+ w*(x+3)==z}

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

18

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

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

19

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

{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}

20

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

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

21

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

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}

22

Акиомы вывода согласуются с

условным оператором

Пусть L = {{P} f {Q}}, B – предикат.

P^B→P (A3 ИВ)

{P^B}f{Q} (аксиома вывода)

{P}if (B) then f endif {Q} (аксиома условного

оператора)

23

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

{P ^ B} с {Q}

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

24

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

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

25

Как упростить операцию insert?

P = c – последний V с – непоследний

{P} insert(c, x) {Q}

26

Как упростить операцию insert?

P = c – последний V с – непоследний

{P} insert(c, x) {Q}

Если B = {∀с с – непоследний} – инвариант

P^B→P, {P}insert(c,x){Q} ⊦ {P^B}insert(c,x){Q}

27

Как упростить операцию insert?

P = c – последний V с – непоследний

{P} insert(c, x) {Q}

Если B = {∀с с – непоследний} – инвариант

P^B→P, {P}insert(c,x){Q} ⊦ {P^B}insert(c,x){Q}

Но! P^B = c- непоследний

{c - непоследний} insert(c,x) {Q}

28

Закольц. список с буф. элементом

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;

}

29

Закольц. список с буф. элементом

node* insert_before(node& c, int v) {node *n = new node;n -> val = v;

n -> next = c.next;c.next = n;

return n;}

30

Следствия аксиомы композиции

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

31

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

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

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

• Инварианты

32

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

ArrangeAct

Assert

33

Рефакторинг

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

34

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

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

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

35

А можно ли по-другому?

Выводимость прямо предписывает строить новые тройки из предыдущих!

36

Когда происходит повторное

использование?

Если нужно внести изменение в существующее

приложение, то мы пытаемся повторно

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

получить тоже приложение, но с новой

функциональностью.

37

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

B1, B2, …, A1

B3, A1, …, A2

B5, A2, …, A3

A2, B6, …, A4

A2, A4, …, A5

38

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

B1, B2, …, A1

B3, A1, …, A2

B5, A2, …, A3

A2, B6, …, A4

A2, A4, …, A5

39

The Open-Closed Principle

Программные объекты должны быть

открыты для расширения, но в тоже время

закрыты для модификации.

40

Пример: матрицы

class matrix {

int size;

double *body;

public:

matrix(int s): size(s) {

body = new double[s*s];

}

void transform() {

}

double det() const {

}

};

41

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

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) {

}

};

42

Придется менять методы

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

}

};

43

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

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

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

44

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

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

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

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

≥ 0, но

45

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

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

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

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

≥ 0, но

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

46

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

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

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

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

≥ 0, но

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

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

47

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

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

инвариант класса

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

расширяют допустимые значения

параметров

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

использовать конструктор по умолчанию

48

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

A

B

49

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

Пусть P1 → P,

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

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

Тогда B ⊂ A.

P ⊨ P1. Говорят, что P –

более слабое условие,

P1 – более сильное.

A

B

50

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

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

51

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

{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}

52

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

{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}

53

The Dependency Inversion Principle

Высокоуровневые компоненты не должны

зависеть от низкоуровневых компонент. И

те, и те должны зависеть от абстракций

(оператора расширения).

54

Иначе

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

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}

56

Иначе

{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}

57

Правило оператора расширения

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

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

58

Метафора для оператора

расширения

59

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

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

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

60

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

2. Во время выполнения

указатель на функцию

void (*f) (int i);

f pf;

pf(5);

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

class Shape {

public:

virtual void Draw() = 0;

};

shape->Draw();

61

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

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

void Counter::setValue(int value)

{

if (value != m_value) {

m_value = value;

emit valueChanged(value);

}

}

62

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

Это набор связанных методов, возможно,

полиморфных

63

The Liskov Substitution Principle

Функции, которые используют ссылки на

объекты базовых классов, должны

использовать объекты производных

классов, ничего не зная о них.

64

Несколько операторов расширения

65

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

66

The Interface Segregation Principle

Класс не должен зависеть от интерфейсов,

которые он не использует

67

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

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

std::vector<validator> rules = …;

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

if(!rules[i](document))

return false;

return true;

68

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

избежать жирного интерфейса?

69

The Single Responsibility Prinsiple

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

70

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

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

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

oopguide.ru

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

actorsmodel.ru

71

Спасибо за внимание

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

HWdTech, LLC

[email protected]

slideshare.com/etyumentcev