Программирование по контракту — различия между версиями
Sergej (обсуждение | вклад) (→Пример) |
м (rollbackEdits.php mass rollback) |
||
(не показаны 62 промежуточные версии 5 участников) | |||
Строка 16: | Строка 16: | ||
}} | }} | ||
== Пример == | == Пример == | ||
− | Необходимо гарантировать, что | + | Необходимо гарантировать, что функции данного класса будут возвращать корректные данные, либо, вообще не будут работать. |
− | class Time { | + | public class Time { |
− | + | private int hours; | |
− | + | private int minutes; | |
− | + | private int seconds; | |
− | getHours(); | + | public int getHours(); |
{ | { | ||
− | return | + | return hours; |
} | } | ||
− | getMinutes(); | + | public int getMinutes(); |
{ | { | ||
− | return | + | return minutes; |
} | } | ||
− | getSeconds() | + | public int getSeconds() |
{ | { | ||
− | return | + | return seconds; |
} | } | ||
− | setHours(newHOURS); | + | public void setHours(int newHOURS); |
{ | { | ||
− | + | hours = newHOURS; | |
} | } | ||
− | setMinutes(newMINUTES); | + | public void setMinutes(int newMINUTES); |
{ | { | ||
− | + | minutes = newMINUTES; | |
} | } | ||
− | setSeconds(newSECONDS) | + | public void setSeconds(int newSECONDS) |
{ | { | ||
− | + | seconds = newSECONDS; | |
} | } | ||
} | } | ||
− | + | Инвариант: | |
− | + | ||
+ | hours >= 0 and hours <= 23 | ||
+ | |||
+ | minutes >= 0 and minutes < 60 | ||
+ | |||
+ | seconds >= 0 and seconds < 60 | ||
+ | |||
+ | Постусловия и предусловия: | ||
+ | |||
+ | |||
+ | int getHours() | ||
+ | post: возвращенное значение будет являться текущим часом. | ||
+ | |||
+ | |||
+ | int getMinutes() | ||
+ | post: возвращенное значение будет являться текущей минутой. | ||
+ | |||
+ | |||
+ | int getSeconds() | ||
+ | post: возвращенное значение будет являться текущей секундой. | ||
+ | |||
+ | |||
+ | void setHours(int newHours) | ||
+ | pre: 0 <= newHours <= 23 | ||
+ | post: hours == newHours | ||
+ | |||
+ | |||
+ | void setMinutes(int newMinutes) | ||
+ | pre: 0 <= newMinutes < 60 | ||
+ | post: minutes == newMinutes | ||
+ | |||
+ | |||
+ | void setSeconds(int newSeconds) | ||
+ | pre: 0 <= newSeconds < 60 | ||
+ | post: seconds == newSeconds | ||
+ | |||
==== Решение 1 ==== | ==== Решение 1 ==== | ||
Выбрасывать исключение. Имеет недостатки: неочевидность проверки, необходимость писать кучу кода вручную. | Выбрасывать исключение. Имеет недостатки: неочевидность проверки, необходимость писать кучу кода вручную. | ||
− | + | void setHours(int newHours){ | |
− | if ( | + | if (newHours < 0 || newHours > 23) |
throw GREAT_Time_Exception; | throw GREAT_Time_Exception; | ||
− | + | hours = newHours; | |
} | } | ||
Строка 60: | Строка 95: | ||
@Contracted // говорит о том, что класс использует контракты – для отображения в IDE | @Contracted // говорит о том, что класс использует контракты – для отображения в IDE | ||
class Time | class Time | ||
− | { | + | { |
− | + | void setHours(int newHours); | |
− | |||
{ | { | ||
− | + | hours = newHours; | |
} | } | ||
@Requires ({“newHOURS>= 0”,“newHOURS<= 23” }) | @Requires ({“newHOURS>= 0”,“newHOURS<= 23” }) | ||
− | @Ensures ( | + | @Ensures (“hours == newHOURS”) |
} | } | ||
@Requires – буквально означает, «Убедиться, что ДО выполнения подпрограммы («условие выполняется»)» Иначе – бросить исключение. | @Requires – буквально означает, «Убедиться, что ДО выполнения подпрограммы («условие выполняется»)» Иначе – бросить исключение. | ||
Строка 74: | Строка 108: | ||
Здесь мы видим, что, как и в Решение 1, осуществляется проверка пред и пост условий для наших методов. В чем разница? Разница в том, что во втором случае это более наглядно и удобно. | Здесь мы видим, что, как и в Решение 1, осуществляется проверка пред и пост условий для наших методов. В чем разница? Разница в том, что во втором случае это более наглядно и удобно. | ||
+ | |||
== Пример == | == Пример == | ||
Рассмотрим стек на массиве. У него есть переменные | Рассмотрим стек на массиве. У него есть переменные | ||
Строка 92: | Строка 127: | ||
<tex> isEmpty - </tex> проверка на пустоту | <tex> isEmpty - </tex> проверка на пустоту | ||
+ | |||
+ | public class ArrayStack { | ||
+ | private int size = 0; | ||
+ | private Object[] elements = new Object[2]; | ||
+ | public void push(Object element){ | ||
+ | assert element != null; | ||
+ | ensureCapacity(size + 1); | ||
+ | elements[size++] = element; | ||
+ | } | ||
+ | private void ensureCapacity(int capacity) { | ||
+ | if (capacity <= elements.length) { | ||
+ | return; | ||
+ | } | ||
+ | Object[] newElements = new Object[2 * capacity]; | ||
+ | for (int i = 0; i < size; i++) { | ||
+ | newElements[i] = elements[i]; | ||
+ | } | ||
+ | elements = newElements; | ||
+ | } | ||
+ | public Object pop() { | ||
+ | assert size > 0; | ||
+ | return elements[--size]; | ||
+ | } | ||
+ | public int size() { | ||
+ | return size; | ||
+ | } | ||
+ | public boolean isEmpty() { | ||
+ | return size == 0; | ||
+ | } | ||
+ | public Object peek() { | ||
+ | assert size > 0; | ||
+ | return elements[size - 1]; | ||
+ | } | ||
+ | } | ||
Инвариант: | Инвариант: | ||
Строка 100: | Строка 169: | ||
Контракты: | Контракты: | ||
− | |||
− | |||
− | + | push | |
− | + | pre: element != NULL | |
+ | post: size = size' + 1 and elements[size'] == element | ||
+ | void push (Object element) | ||
+ | |||
+ | pop | ||
+ | pre: size > 0 | ||
+ | post: size == size' - 1 and result == elements[size] | ||
+ | Object pop() | ||
+ | |||
+ | peek | ||
+ | pre: size > 0 | ||
+ | post: result == elements[size - 1] | ||
+ | Object peek() | ||
− | post: | + | size |
− | + | post: result == size | |
+ | int size() | ||
− | + | isEmpty | |
− | + | post: result == size > 0 | |
+ | boolean isEmpty() |
Текущая версия на 19:23, 4 сентября 2022
Программирование по контракту обеспечивает проверку предусловий и постусловий при выполнении методов классов, пользовательских функций. Также немаловажную роль в правильности написания функций играют инварианты.
Предусловие
Определение: |
Предусловие - должно быть выполнено до исполнения действия. |
Постусловие
Определение: |
Постусловие - должно быть выполнено после исполнения действия. |
Инвариант
Определение: |
Инвариант - определяет глобальные свойства некоторого класса, которые должны соблюдаться после его создания на протяжении всего времени жизни. |
Пример
Необходимо гарантировать, что функции данного класса будут возвращать корректные данные, либо, вообще не будут работать.
public class Time { private int hours; private int minutes; private int seconds; public int getHours(); { return hours; } public int getMinutes(); { return minutes; } public int getSeconds() { return seconds; } public void setHours(int newHOURS); { hours = newHOURS; } public void setMinutes(int newMINUTES); { minutes = newMINUTES; } public void setSeconds(int newSECONDS) { seconds = newSECONDS; } }
Инвариант:
hours >= 0 and hours <= 23
minutes >= 0 and minutes < 60
seconds >= 0 and seconds < 60
Постусловия и предусловия:
int getHours() post: возвращенное значение будет являться текущим часом.
int getMinutes() post: возвращенное значение будет являться текущей минутой.
int getSeconds() post: возвращенное значение будет являться текущей секундой.
void setHours(int newHours) pre: 0 <= newHours <= 23 post: hours == newHours
void setMinutes(int newMinutes) pre: 0 <= newMinutes < 60 post: minutes == newMinutes
void setSeconds(int newSeconds) pre: 0 <= newSeconds < 60 post: seconds == newSeconds
Решение 1
Выбрасывать исключение. Имеет недостатки: неочевидность проверки, необходимость писать кучу кода вручную.
void setHours(int newHours){ if (newHours < 0 || newHours > 23) throw GREAT_Time_Exception; hours = newHours; }
Решение 2
Java поддерживает механизм аннотаций (рекомендаций компилятору, препроцессору) – метаданные, которые могут быть добавлены в исходный код программы, не влияя на него семантически, т.е. не меняя его поведение. При этом, они могут использоваться на этапе анализа кода, компиляции и выполнения.
@Contracted // говорит о том, что класс использует контракты – для отображения в IDE class Time { void setHours(int newHours); { hours = newHours; } @Requires ({“newHOURS>= 0”,“newHOURS<= 23” }) @Ensures (“hours == newHOURS”) }
@Requires – буквально означает, «Убедиться, что ДО выполнения подпрограммы («условие выполняется»)» Иначе – бросить исключение.
@Ensures – буквально означает, «Убедиться, что ПОСЛЕ выполнения подпрограммы ( «условие выполняется»)»
Здесь мы видим, что, как и в Решение 1, осуществляется проверка пред и пост условий для наших методов. В чем разница? Разница в том, что во втором случае это более наглядно и удобно.
Пример
Рассмотрим стек на массиве. У него есть переменные
число элементов
массив элементов
Методы:
добавить элемент
удалить элемент
получить элемент на вершине
число элементов
проверка на пустоту
public class ArrayStack { private int size = 0; private Object[] elements = new Object[2]; public void push(Object element){ assert element != null; ensureCapacity(size + 1); elements[size++] = element; } private void ensureCapacity(int capacity) { if (capacity <= elements.length) { return; } Object[] newElements = new Object[2 * capacity]; for (int i = 0; i < size; i++) { newElements[i] = elements[i]; } elements = newElements; } public Object pop() { assert size > 0; return elements[--size]; } public int size() { return size; } public boolean isEmpty() { return size == 0; } public Object peek() { assert size > 0; return elements[size - 1]; } }
Инвариант:
Размер не отрицателен,
Элементы заполнены
Контракты:
push pre: element != NULL post: size = size' + 1 and elements[size'] == element void push (Object element)
pop pre: size > 0 post: size == size' - 1 and result == elements[size] Object pop()
peek pre: size > 0 post: result == elements[size - 1] Object peek()
size post: result == size int size()
isEmpty post: result == size > 0 boolean isEmpty()