Successfully reported this slideshow.
Your SlideShare is downloading. ×

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

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 74 Ad
Advertisement

More Related Content

Slideshows for you (18)

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

Advertisement

More from Dev2Dev (20)

Recently uploaded (20)

Advertisement

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

  1. 1. HWdTech, LLC обоснование с помощью логики Хоара S.O.L.I.D.
  2. 2. 14 лет преподаю ИМИТ, ФКН ОмГУ ИТ-компании Школа программиста 10 лет разрабатываю ПО разработчик, архитектор, PM, руководство до 70 человек Тюменцев Евгений
  3. 3. Предыстория вопроса Формулировка принципов, ключевые события 03
  4. 4. The Open-Closed Principle Программные объекты должны быть открыты для расширения, но в тоже время закрыты для модификации. 04
  5. 5. The Liskov Substitution Principle Функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом. 05
  6. 6. Пример нарушения 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); } }
  7. 7. The Dependency Inversion Principle Высокоуровневые компоненты не должны зависеть от низкоуровневых компонент. И те, и те должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. 07
  8. 8. Пример нарушения 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); } }
  9. 9. Пример соответствия 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); }
  10. 10. The Interface Segregation Principle Класс не должен зависеть от интерфейсов, которые он не использует. 10
  11. 11. The Single Responsibility Principle Должна быть ровно одна причина для изменения класса. 11
  12. 12. switch If /else – if/else enum new Операторы приведения типа Магические константы Примеры конструкций, нарушающих OCP 12
  13. 13. Пример нарушения 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 } };
  14. 14. Что-о-о? switch, new? Ты кто такой?
  15. 15. LSP, OOPSLA’87 Если для каждого объекта o1 типа S существует объект o2 типа T, который для всех программ P определен в терминах T, то поведение P не изменится, если o1 заменить на o2 при условии, что S является подтипом T История вопроса 15 Barbara Liskov
  16. 16. LSP, OOPSLA’87 1988, опубликованы в Object-oriented Software Construction История вопроса 16 Bertran Meyer
  17. 17. LSP, OOPSLA’87 1988, опубликованы в Object-oriented Software Construction 1994, A behavioral notion of subtyping История вопроса 17 Barbara Liskov
  18. 18. 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
  19. 19. 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
  20. 20. Выводимость и логика Хоара Доказательство свойств программ 20
  21. 21. Выводимость 21 Пусть L – множество формул, B – формула. Тогда L ⊦ B, если ∃ B1, B2, …, Bn , что 1.Bn – это B, 2.Bi – это либо формула из L, либо аксиома, либо общезначимая формула, либо формула полученная при помощи правила вывода
  22. 22. Исчисление высказываний 22
  23. 23. Modus Ponens (правило вывода) 23
  24. 24. 1969 г. An Axiomatic Basis for Computer Programming 1971 г. Procedures and Parameters: An Axiomatic Approach 1980 г. премия Тьюринга 1990 г. Медаль “Пионер компьютерной техники” 2000 г. рыцарский титул за заслуги в области образования и компьютерной техники, премия Киото Логика Хоара Чарльз Хоар 24
  25. 25. {pred} statement {post} Пример: {x == 42} y=x+1; {y == 43 ^ x == 42} Тройка Хоара 25
  26. 26. {P} skip {P} Аксиома пустого оператора 26
  27. 27. Пример: {(y+1)*3+w*(y+1+3) ==z} x=y+1; { x*3+ w*(x+3)==z} Аксиома оператора присваивания 27 {P[E/x]} x := E {P}
  28. 28. Аксиома оператора цикла 28 {P ^ B} S {P} ╞ {P} while B do S done {B’ ^ P}
  29. 29. Аксиома условного оператора 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}
  30. 30. Аксиомы вывода 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}
  31. 31. Аксиома вывода и условный оператор 31 Пусть L = {{P} f {Q}}, B – предикат. P^B→P (A3 исчисления высказываний) {P^B}f{Q} (аксиома вывода) {P}if (B) then f endif {Q} (аксиома условного оператора)
  32. 32. Неявные предположения 32 {P ^ B} с {Q} {P} assert(B); c {Q}
  33. 33. Упрощение кода и аксиома вывода 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
  34. 34. Аксиома оператора цикла 34
  35. 35. Закольцованный список с буферным элементом 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; }
  36. 36. Закольцованный список с буферным элементом 36 node* insert(node& c, int v) { node *n = new node; n -> val = v; n -> next = c.next; c.next = n; return n; }
  37. 37. Аксиома композиции 37 {P} S {Q}, {Q} T {R} ╞ {P} S;T {R}
  38. 38. Контрактное программирование 38 • Предусловия • Постусловия • Инварианты
  39. 39. Модульные тесты 39 Arrange Act Assert
  40. 40. Рефакторинг 40 {P} S {Q}, {Q} T {R} ╞ {P} S;T {R}
  41. 41. S.O.L.I.D. Математическое обоснование 41
  42. 42. Аксиома оператора цикла 42 Повторное использование кода — методология проектирования компьютерных и других систем, заключающаяся в том, что система (компьютерная программа, программный модуль) частично либо полностью должна составляться из частей, написанных ранее компонентов и/или частей другой системы, и эти компоненты должны применяться более одного раза (если не в рамках одного проекта, то хотя бы разных). https://ru.wikipedia.org/wiki/Повторное_использование_кода
  43. 43. Когда происходит повторное использование? Если нужно внести изменение в существующее приложение, то мы пытаемся повторно использовать свой же собственный код, чтобы получить тоже приложение, но с новой функциональностью. 43
  44. 44. 44 Выводимость прямо предписывает строить новые тройки из предыдущих!
  45. 45. Чем грозят правки кода? 45 B1, B2, …, A1 B3, A1, …, A2 B5, A2, …, A3 A2, B6, …, A4 A2, A4, …, A5
  46. 46. Программа рассыпается как карточный домик! 46 B1, B2, …, A1 B3, A1, …, A2 B5, A2, …, A3 A2, B6, …, A4 A2, A4, …, A5
  47. 47. Из закрытости следует выводимость! 47 Программные объекты должны быть открыты для расширения, но в тоже время закрыты для модификации. The Open-Closed Principle
  48. 48. Матрицы 48 class matrix { int size; double *body; public: matrix(int s): size(s) { body = new double[s*s]; } void transform() { … } double det() const { … } };
  49. 49. Матрицы: добавляем 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) { } };
  50. 50. Матрицы: придется менять методы 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(); … } };
  51. 51. Что случилось? 51 Инвариант класса matrix: size > 0 Конструктор matrix() нарушил инвариант: size = 0 Предусловия методов изменились с size > 0 на size ≥ 0, но {size ≥ 0} det {Q} не выводится из {size > 0} det {Q} Следовательно, надо изменять сами методы
  52. 52. 52 Конструктор должен устанавливать инвариант класса Конструкторы по умолчанию часто этого не делают Стоит несколько раз подумать, прежде чем использовать конструктор по умолчанию
  53. 53. Импликация на множестве 53 Пусть P1 → P, B = { x | P1(x) = 1}, A = { x | P (x) = 1}. Тогда B ⊂ A. P ⊨ P1. Говорят, что P – более слабое условие, P1 – более сильное. A B
  54. 54. Построим вывод 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}
  55. 55. А если импликации нет? 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}
  56. 56. Оператор расширения 56 Оператор расширения должен допускать: 1. Ослабление предусловий 2. Усиление постусловий
  57. 57. Метафора для оператора расширения 57
  58. 58. Для оператора расширения получаем 58 Высокоуровневые компоненты не должны зависеть от низкоуровневых компонент. И те, и те должны зависеть от абстракций. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций. The Dependency Inversion Principle
  59. 59. 59 Как выглядит расширение?
  60. 60. Статический полиморфизм 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>());
  61. 61. Динамический полиморфизм 61 • указатель на функцию void (*f) (int i); f pf; pf(5); • виртуальный метод class Shape { public: virtual void Draw() = 0; }; shape->Draw();
  62. 62. Метапрограмирование 62 void Counter::setValue(int value) { if (value != m_value) { m_value = value; emit valueChanged(value); } }
  63. 63. Что такое класс? Это набор методов, возможно, виртуальных. 63
  64. 64. Условие для методов класса 64 Для каждого метода подкласса 1. предусловия могут быть ослаблены 2. Постусловия усилены
  65. 65. The Liskov Substitution Principle Функции, которые используют ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом. 65
  66. 66. Случай нескольких операторов расширения 66
  67. 67. Жирный интерфейс 67
  68. 68. Пустой метод – метод, который подходит не для всех! 68 The Interface Segregation Principle Класс не должен зависеть от интерфейсов, которые он не использует.
  69. 69. А не для классов? 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;
  70. 70. В каком случае гарантированно удастся избежать жирного интерфейса? 70
  71. 71. The Single Responsibility Principle 71 Должна быть ровно одна причина для изменения класса
  72. 72. А как же функциональщина? 72
  73. 73. Мы пишем книги 73 Нерассказанные факты об императивном программировании oopguide.ru Разработка серверов и серверных приложений actorsmodel.ru
  74. 74. Пишите: eytumentcev@hwdtech.ru Звоните: +7 913 150 22 04 slideshare.com/etyumentcev http://hwdtech.ru Тюменцев Евгений Александрович

×