Парадигмы программирования

Реализация ООП

Описание стека

  • Переменные
    • size — число элементов
    • elements — массив элементов
  • Методы
    • push — добавить элемент
    • pop — удалить элемент
    • peek — получить элемент на вершине
    • size — число элементов
    • isEmpty — проверка на пустоту

Модель

  • Последовательность чисел
    • $a[1], a[2], \ldots, a[n]$
    • Операции с последним элементом
  • Инвариант
    • $n \ge 0$
    • $∀i=1..n: a_i ≠ null$
  • Определения
    • $immutable(k) = ∀i=1..k : a[i]' = a[i]$

Контракты (1)

  • push
    • // Pred: element ≠ null
      // Post: n' = n + 1 ∧ immutable(n) ∧ a'[n'] = element
      void push(Object element)
      
  • pop
    • // Pred: n > 0
      // Post: ℝ = a[n] ∧ n = n' − 1 ∧ immutable(n')
      Object pop()
      
  • peek
    • // Pred: n > 0
      // peek: ℝ = a[n] ∧ n = n' ∧ immutable(n)
      Object peek()
      

Контракты (2)

  • size
    • // Post: ℝ = n ∧ n = n' ∧ immutable(n)
      int size()
      
  • isEmpty
    • // Post: ℝ = n > 0 ∧ n = n' ∧ immutable(n)
      boolean isEmpty()
      

Заголовок

  • Объявление модуля
    public class ArrayStackModule {
    
  • Данные
    static int size;
    static Object[] elements = new Object[5];
    

Функция push

  • static void push(Object element) {
        Objects.requireNonNull(element);
        ensureCapacity(size + 1);
        elements[size++] = element;
    }
    
  • static void ensureCapacity(int capacity) {
        if (capacity > elements.length) {
            elements = 
                Arrays.copyOf(elements, 2 * capacity);
        }
    }
    

Функции pop и peek

  • static Object pop() {
        assert size > 0;
        return elements[--size];
    }
    
  • static Object peek() {
        assert size > 0;
        return elements[size - 1];
    }
    

Функции size и isEmpty

  • static int size() {
        return size;
    }
    
  • static boolean isEmpty() {
        return size == 0;
    }
    

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

  • Добавление элементов
    for (int i = 0; i < 10; i++) {
        ArrayStackModule.push(i);
    }
    
  • Получение элементов
    while (!ArrayStackModule.isEmpty()) {
        System.out.println(
            ArrayStackModule.size() + " " +
            ArrayStackModule.peek() + " " +
            ArrayStackModule.pop()
        );
    }
    

Инкапсуляция

  • Сокрытие деталей реализации
    • Доступ на уровне модуля
    • Способ поддерживать контракт
  • Пример
    • private static int size
      private static Object[] elements
      public push(Object element)
      public Object pop()
      public Object peek()
      public int size()
      public boolean isEmpty()
      private void ensureCapacity()
      

Заголовок

  • Объявление структуры
    public class ArrayStackADT {
    
  • Данные
    private /*static*/ int size;
    private /*static*/ Object[] elements =
            new Object[5];
    

Преобразование

  • Добавление аргументов функций
    • Первый аргумент — ссылка на структуру
    • ArrayStackADT stack
  • Доступ к переменным
    • Добавление префикса stack.
  • Вызов функций
    • Передача первым аргументом ссылки

Функция push

  • public static void push(ArrayStackADT stack, Object element) {
        Objects.requireNonNull(element);
        ensureCapacity(stack, stack.size + 1);
        stack.elements[stack.size++] = element;
    }
    
  • private static void ensureCapacity(
        ArrayStackADT stack, int capacity
    ) {
        if (stack.elements.length < capacity) {
            stack.elements = 
                Arrays.copyOf(stack.elements, capacity * 2);
        }
    }
    

Функции pop и peek

  • public static Object pop(ArrayStackADT stack) {
        assert stack.size > 0;
        return stack.elements[--stack.size];
    }
    
  • public static Object peek(ArrayStackADT stack) {
        assert stack.size > 0;
        return stack.elements[stack.size - 1];
    }
    

Функции size и isEmpty

  • public static int size(ArrayStackADT stack) {
        return stack.size;
    }
    
  • public static boolean isEmpty(
        ArrayStackADT stack
    ) {
        return stack.size == 0;
    }
    

Инициализатор (конструктор)

  • public static ArrayStackADT create() {
        ArrayStackADT stack = new ArrayStackADT();
        stack.elements = new Object[10];
        return stack;
    }
    

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

  • Добавление элементов
    ArrayStackADT stack = ArrayStackADT.create();
    for (int i = 0; i < 10; i++) {
        ArrayStackADT.push(stack, i);
    }
    
  • Получение элементов
    while (!ArrayStackADT.isEmpty(stack)) {
        System.out.println(
            ArrayStackADT.size(stack) + " " +
            ArrayStackADT.peek(stack) + " " +
            ArrayStackADT.pop(stack)
        );
    }
    

Заголовок

  • Объявление класса
    public class ArrayStack {
    
  • Данные
    private int size;
    private Object[] elements = new Object[10];
    

Преобразование функций в методы

  • Изменение модификаторов
    • Убрать static
  • Замена первого аргумента
    • Замена на аргумента на неявный (this)
  • Доступ к переменным
    • Замена префикса stack. на this.
    • Префикс this. можно опускать
  • Вызов метода
    • method(stack, ...)stack.method(...)

Метод push

  • public void push(Object element) {
        Objects.requireNonNull(element);
        ensureCapacity(size + 1);
        elements[size++] = element;
    }
    
  • private void ensureCapacity(int capacity) {
        if (elements.length < capacity) {
            elements = 
                Arrays.copyOf(elements, capacity * 2);
        }
    }
    

Методы pop и peek

  • public Object pop() {
        assert size > 0;
        return elements[--size];
    }
    
  • public Object peek() {
        assert size > 0;
        return elements[size - 1];
    }
    

Методы size и isEmpty

  • public int size() {
        return size;
    }
    
  • public boolean isEmpty() {
        return size == 0;
    }
    

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

  • Добавление элементов
    ArrayStack stack = new ArrayStack();
    for (int i = 0; i < 10; i++) {
        stack.push(i);
    }
    
  • Получение элементов
    while (!stack.isEmpty()) {
        System.out.println(stack.size() + " " +
            stack.peek() + " " + stack.pop());
    }
    

Конструкторы

Содержание

Элемент связного списка

  • Объявление класса
    // inv: value != null
    public class Node {
       Object value;
       Node next;
    }
    
  • Инициализация
    public init(Object value, Node next) {
        assert value != null;
        this.value = value;
        this.next = next;
    }
    

Инициализация

  • Значения по умолчанию
    value = null;
    next = null;
    
    • Инвариант нарушен
  • Метод init
    assert value != null;
    this.value = value;
    this.next = next;
    
    • Инвариант соблюдается

Конструктор

  • Соблюдение инварианта при создании
  • Пример конструктора
    public Node(Object value, Node next) {
        assert value != null;
        this.value = value;
        this.next = next;
    }
    
  • Вызов конструктора
    Node node = new Node(1, null);
    

Как работало раньше?

  • Конструктор по умолчанию
    • Без аргументов
    • Ничего не делает
    • Создается, если нет других

Устройство

  • Исходное состояние
  • push(v¹)
  • push(v²)
  • pop()

Реализация

  • Объявление класса
    public class LinkedStack {
    
  • Данные
    private int size;
    private Node head;
    

Метод push

  • // Pred: element ≠ null
    // Post: n = n' + 1 ∧ ∀i=1..n' : a[i]' = a[i]∧
    //          ∧ a[n] = element
    public void push(Object element) {
        assert element != null;
        size++;
        head = new Node(element, head);
    }
    

Метод pop

  • // Pred: n > 0
    // Post: ℝ = a[n + 1] ∧ n = n' − 1 ∧
    //          ∧ ∀i=1..n : a[i]' = a[i]
    public Object pop() {
        assert size > 0;
        size--;
        Object result = head.value;
        head = head.next;
        return result;
    }
    

Метод peek

  • // Pred: n > 0
    // Post: ℝ = a[n] ∧ immutable
    public Object peek() {
        assert size > 0;
        return head.value;
    }
    

Методы size и isEmpty

  • // Post: ℝ = n ∧ immutable
    public int size() {
        return size;
    }
    
  • // Post: ℝ = n > 0 ∧ immutable
    public boolean isEmpty() {
        return size == 0;
    }
    

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

  • Добавление элементов
    LinkedStack stack = new LinkedStack();
    for (int i = 0; i < 10; i++) {
        stack.push(i);
    }
    
  • Получение элементов
    while (!stack.isEmpty()) {
        System.out.println(stack.size() + " " +
            stack.peek() + " " + stack.pop());
    }
    

Проблема

  • Одинаковый контракт стеков
  • Одинаково использование
    • Почти одинаковый код
  • Код знает тип списка
    • ArrayStack
    • LinkedStack

Решение

  • Интерфейс
    • Набор методов
    • Контракт
  • Пример
    public interface Stack {
        /*public*/ void push(Object element);
        /*public*/ Object pop();
        /*public*/ Object peek();
        /*public*/ int size();
        /*public*/ boolean isEmpty();
    }
    

Реализация интерфейса

  • Синтаксис
    public class ArrayStack implements Stack { ... }
    public class LinkedStack implements Stack { ... }
    
  • Реализация методов
    • Проверяется компилятором
  • Выполнение контракта
    • Гарантируется программистом

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

  • Добавление элементов
    public static void fill(Stack stack) {
        for (int i = 0; i < 10; i++) {
            stack.push(i);
        }
    }
    
  • Получение элементов
    public static void dump(Stack stack) {
        while (!stack.isEmpty()) {
            System.out.println(stack.size() + " " +
                stack.peek() + " " + stack.pop());
        }
    }
    

Проверка типа

  • a instanceof T
    • a может быть приведено к T
  • Примеры
    • new ArrayStack() instanceof ArrayStacktrue
    • new ArrayStack() instanceof LinkedStackfalse
    • new ArrayStack() instanceof Stacktrue
    • new ArrayStack() instanceof Objecttrue
    • new Object() instanceof Stackfalse

Реализация на основе instanceof

class StackImpl {
    public static void pop(Stack this) {
        if (this instanceof ArrayStack) {
            return ArrayStack.pop(this);
        } else if (this instanceof LinkedStack) {
            return LinkedStack.pop(this);
        } else {
            // ?
        }
    }
    …
}

Свойства

  • Достоинства
    • Простота реализации
  • Недостатки
    • Разбухание кода
    • Линейное время

Таблицы виртуальных функций

Свойства

  • Достоинства
    • Константные трудозатраты
  • Недостатки
    • Затраты памяти
    • Проблемы с множественным наследованием

Полиморфизм

  • Один код, разные типы
  • Ad-hoc
    • Для каждого типа свое поведение
  • Универсальный
    • Одинаковое поведение для всех типов

Пример полиморфизма (1)

  • Операция сложения (+)
  • Примеры использования
    • 1 + 1
    • 1.0 + 1
    • 1 + 1.0
    • 1.0 + 1.0
  • Перегрузка времени компиляции по обоим параметрам
    • Ad-hoc полиморфизм
  • Перегрузка для разных типов + автоматизированное приведение типов
    • Ad-hoc полиморфизм

Пример полиморфизма (2)

  • Перегрузка функций
    void test(LinkedStack stack) { … }
    void test(ArrayStack stack) { … }
    …
    test(new LinkedList());
    test(new ArrayList());
    
  • Ad-hoc полиморфизм

Пример полиморфизма (3)

  • Функция printf
  • Примеры использования
    • printf("%d", 10);
    • printf("%s", "hello");
  • Перегрузка времени исполнения по всем параметрам, кроме первого + явная передача информации о типах
    • Ad-hoc полиморфизм

Пример полиморфизма (4)

  • Наследование в ООП
    • drawWithColor(Shape shape, Color color) {
          shape.setColor(color);
          shape.draw()
      }
      
  • drawWithColor – полиморфизм включения по первому параметру
    • Параметрический полиморфизм
  • draw(this) – перегрузка времени исполнения по первому параметру
    • Ad-hoc полиморфизм

Пример полиморфизма (5)

  • Определение типа во время исполнения
  • Пример использования
    • void draw(Shape shape) {
          if (shape instanceof Rect) …
          if (shape instanceof Circle) …
      }
      
  • draw() – перегрузка времени исполнения
    • Ad-hoc полиморфизм

Наследование

Содержание

Общее поведение

  • Управление размером
    • size
    • isEmpty
    • push
    • pop
  • Обработка null
    • push
  • Обработка пустого стека
    • pop
    • peek

Вынос общего поведения

  • Полное дублирование
    • public isEmpty() {
          return size == 0;
      }
      
    • Общая функция/метод
  • Частичное дублирование
    • public peek() {
          assert size > 0;
          // Получить элемент
          // Зависит от реализации
      }
      
    • Что делать?

Устранение дублирования

  • Метод для дублирующейся части
    • Вызывает методы для различных частей
    • public peek() {
          assert size > 0;
          doPeek();
      }
      
    • protected abstract Object doPeek();
      
  • Методы для различающихся частей
    • protected doPeek() { // ArrayStack
          return elements[size - 1];
      }
      
    • protected doPeek() { // LinkedStack
          return head.value;
      }
      
  • Как реализовать?
    • Указатели на функции
    • Таблица функций

Память и сборка мусора

Содержание

Интерфейс Copiable

  • Определение
    public interface Copiable {
        public Copiable makeCopy();
    }
    

Стек на массиве

  • Множественная реализация интерфейсов
    public class ArrayStack
        implements Stack, Copiable {
    
  • Реализация
    public Copiable makeCopy() {
        final ArrayStack copy = new ArrayStack();
        copy.size = size;
        copy.elements = elements;
        return copy;
    }
    

Проблема

  • Общий массив элементов
  • makeCopy()
  • pop()

Копирование массива

public Copiable makeCopy() {
    final ArrayStack copy = new ArrayStack();
    copy.size = size;
    copy.elements = Arrays.copyOf(elements, size);
    return copy;
}

Соглашения Java

  • Ссылка передается по значению
    • Объект – не копируется
    • При изменении – изменяется по всем ссылкам
  • Поверхностное копирование
    • Только ссылок на объекты
  • Глубокое копирование
    • Создание копий объектов
    • В явном виде

Сборка мусора

  • Получение ссылки
    • new
    • присваивание
    • вызов метода
  • Мусор
    • Нет ссылок
    • ⇒ никогда не будет
    • ⇒ можно удалить объект
  • Сборщик мусора
    • Удаляет мусор
    • delete не нужен

Уточнение возвращаемого значения

  • Проблема
    • ArrayStack copy = (ArrayStack) stack.makeCopy();
  • Решение
    public ArrayStack makeCopy() { ... }
    
  • Почему это работает?
    • ArrayStack реализует Copiable

Стек на связном списке

  • Объявление
    public class LinkedStack
        implements Stack, Copiable {
    
  • Реализация
    public LinkedStack makeCopy() {
        final LinkedStack copy = new LinkedStack();
        copy.size = size;
        copy.head = head;
        return copy;
    }
    
  • Почему это работает?
    • Node не изменяется

Наследование интерфейсов

  • Конкретный стек можно скопировать
    • Stack нельзя скопировать
  • Решение
    public interface Stack extends Copiable { ... }
    
  • Уточнение возвращаемого значения
    public interface Stack extends Copiable {
        Stack makeCopy();
        ...
    
  • Пример использования
    Stack stack = ...;
    Stack copy = stack.makeCopy();
    

Заключение

Содержание

Ссылки

Вопросы

???