2. 14 лет преподаю
ИМИТ, ФКН ОмГУ
ИТ-компании
Школа программиста
10 лет разрабатываю ПО
разработчик, архитектор,
PM, руководство до 70
человек
Тюменцев Евгений
5. The Liskov Substitution Principle
Функции, которые используют
ссылки на базовые классы, должны
иметь возможность использовать
объекты производных классов, не
зная об этом.
05
7. The Dependency Inversion Principle
Высокоуровневые компоненты не
должны зависеть от низкоуровневых
компонент. И те, и те должны
зависеть от абстракций.
Абстракции не должны зависеть от
деталей. Детали должны зависеть
от абстракций.
07
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. Пример соответствия 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. The Interface Segregation Principle
Класс не должен зависеть от
интерфейсов, которые он не
использует.
10
12. switch
If /else – if/else
enum
new
Операторы приведения типа
Магические константы
Примеры конструкций, нарушающих OCP 12
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
}
};
15. LSP, OOPSLA’87
Если для каждого объекта o1
типа S существует объект o2
типа T, который для всех
программ P определен в
терминах T, то поведение P
не изменится, если o1
заменить на o2 при условии,
что S является подтипом T
История вопроса 15
Barbara Liskov
17. LSP, OOPSLA’87
1988, опубликованы в
Object-oriented Software Construction
1994, A behavioral notion
of subtyping
История вопроса 17
Barbara Liskov
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. 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
21. Выводимость 21
Пусть L – множество формул, B – формула.
Тогда L ⊦ B, если ∃ B1, B2, …, Bn , что
1.Bn – это B,
2.Bi – это
либо формула из L,
либо аксиома,
либо общезначимая формула,
либо формула полученная при
помощи правила вывода
24. 1969 г. An Axiomatic Basis for
Computer Programming
1971 г. Procedures and Parameters:
An Axiomatic Approach
1980 г. премия Тьюринга
1990 г. Медаль “Пионер
компьютерной техники”
2000 г. рыцарский титул за заслуги в
области образования и
компьютерной техники, премия
Киото
Логика Хоара
Чарльз Хоар
24
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
node* insert(node& c, int v) {
node *n = new node;
n -> val = v;
n -> next = c.next;
c.next = n;
return n;
}
42. Аксиома оператора цикла 42
Повторное использование кода — методология
проектирования компьютерных и других
систем, заключающаяся в том, что система
(компьютерная программа, программный
модуль) частично либо полностью должна
составляться из частей, написанных ранее
компонентов и/или частей другой системы, и
эти компоненты должны применяться более
одного раза (если не в рамках одного проекта,
то хотя бы разных).
https://ru.wikipedia.org/wiki/Повторное_использование_кода
43. Когда происходит повторное
использование?
Если нужно внести изменение в
существующее приложение, то мы
пытаемся повторно использовать свой
же собственный код, чтобы получить
тоже приложение, но с новой
функциональностью.
43
47. Из закрытости следует выводимость! 47
Программные объекты должны быть
открыты для расширения, но в тоже
время закрыты для модификации.
The Open-Closed Principle
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. Матрицы: добавляем 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
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
Инвариант класса matrix: size > 0
Конструктор matrix() нарушил инвариант: size = 0
Предусловия методов изменились с size > 0 на size ≥ 0, но
{size ≥ 0} det {Q} не выводится из {size > 0} det {Q}
Следовательно, надо изменять сами методы
53. Импликация на множестве 53
Пусть P1 → P,
B = { x | P1(x) = 1},
A = { x | P (x) = 1}.
Тогда B ⊂ A.
P ⊨ P1. Говорят, что P –
более слабое условие,
P1 – более сильное.
A
B
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
{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}
58. Для оператора расширения получаем 58
Высокоуровневые компоненты не
должны зависеть от низкоуровневых
компонент. И те, и те должны
зависеть от абстракций.
Абстракции не должны зависеть от
деталей. Детали должны зависеть от
абстракций.
The Dependency Inversion Principle
64. Условие для методов класса 64
Для каждого метода подкласса
1. предусловия могут быть ослаблены
2. Постусловия усилены
65. The Liskov Substitution Principle
Функции, которые используют
ссылки на базовые классы, должны
иметь возможность использовать
объекты производных классов, не
зная об этом.
65
68. Пустой метод – метод, который подходит не для всех! 68
The Interface Segregation Principle
Класс не должен зависеть от
интерфейсов, которые он не
использует.
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