Обновить

Комментарии 18

НЛО прилетело и опубликовало эту надпись здесь

только у него есть одна проблема: в реальной жизни, в реальных
приложениях такие требования встречаются чуть чаще, нежели никогда

Так выбор сортировок это всего лишь пример применения паттерна "Стратегия". Сам паттерн используется довольно обширно, хоть и не в чистом виде. А факториалы и фиббоначи обычно показывают как работать с рекурсией в языке, так что не очень понимаю ваше негодование.

Больше меня смущает, что их называют паттернами поведения, а не программирования. Чай не про людей речь.

НЛО прилетело и опубликовало эту надпись здесь

ну так и можно в примерах приводить нечто реалистичное, а не натянутое.

Зачем? Что-то реалистичное требует заметно больше кода, да ещё и специфичного. Кто станет читать эти примеры, если они будут размером с "Войну и Мир"?

чуть чаще чем никогда.

теории программирования утверждают что циклы и рекурсия между собой гомоморфны, так что можно утверждать, что рекурсия используется крайне часто. Опять же примеры с сериализацией будут требовать заметно больше бойлерплейта и продвинутой лексики языка в случае с растом, какой новичок будет это читать, когда у него там по два десятка констрейнтов навешано? Плюс, это паттерн "Посетитель", что вроде как вне темы статьи.

НЛО прилетело и опубликовало эту надпись здесь

Тут в статье происходит очередное натягивание совы на глобус. Если посмотреть выше, то в impl SortStrategy for BubbleSort вообще нет никакого обращения к внутреннему состоянию BubbleSort (self). Т.е. в чем смысл присвоения трейта BubbleSort – загадка.

А зачем ему обращаться к self, если структура пустая сама по себе? Оно используется только как инстанциатор типа.

В таком случае логичнее сделать трейт BubbleSort с методом bubble_sort и имплементировать его сортируемому объекту, который тогда будет `mut self`. Но, это в моей картине мира, которую я никому не навязываю.

Трейты/интерфейсы это про описание поведения, то бишь ЧТО делать - сортировать, итерировать, манипулировать типами и прочие. Сортировка пузырьком - это конкретный инстанс поведения, то бишь КАК это делать.

Если сделать как вы предлагаете, то всё равно нужно будет для какого-нибудь типа имплементировать этот самый BubbleSort, что звучит как минимум странно.

// завидев такое могут справиться о вашем ментальном здравии
impl BubbleSort<'a T> for std::collections::HashSet<'a T> {
  fn bubble_sort(&mut self) { todo!() } 
}

Само название трейта в статье выбрано не очень удачно, да. Лучше б было бы если б называлось что-то вроде Sortable. И интерфейс надо было void сделать, а данные в конструкторы отдавать.

Да нет, название как раз удачное, Sortable означает нечто, что можно отсортировать, а на самом деле оно само сортирует других.

В то же время существует громадное количество реализаций (например стандартные листы в C#) где стратегия все же используется как раз в сортировке.
Только задается не алгоритм сортировки, а лямбда с алгоритмом сравнения элементов.
Соответственно типичная задача - указать листу, по какому полю объекта сортировать.

Для такого есть sort_by(doc). Но оно сортировать всё равно будет timsort. Глобально на алгоритм сортировки смена компаратора не влияет.

Все так
Я про то, что пример такой лучше, потому что задача более прикладная.
Люди обычно используют стандартные реализации быстрой сортировки, изобретать вариации нужно только в очень редаких случаях.

А вот компаратор наоборот, задается очень часто, потому что для разных прикладных задач используются разные данные с разными правилами "что является приоритетом для сортировки в этой структуре данных"

В таком примере стратегию проще (и возможно полезнее) показать как лямбду

Ok, вы решили затащить ООП паттерны в не совсем ООП язык. Но зачем их реализовывать так прямолинейно (и насколько я понимаю, не оптимально)?

Rust предлагает более изящные и оптимальные решения, посмотреть можно здесь:
https://rust-unofficial.github.io/patterns/patterns/index.html

Вот, например, реализация стратегии без Box<dyn SortStrategy>:

use std::collections::HashMap;

type Data = HashMap<String, u32>;

trait Formatter {
    fn format(&self, data: &Data, buf: &mut String);
}

struct Report;

impl Report {
    // Write should be used but we kept it as String to ignore error handling
    fn generate<T: Formatter>(g: T, s: &mut String) {
        // backend operations...
        let mut data = HashMap::new();
        data.insert("one".to_string(), 1);
        data.insert("two".to_string(), 2);
        // generate report
        g.format(&data, s);
    }
}

Стратегию в Rust принято реализовывать при помощи trait-а Fn. Внутри он реализует практически тот же механизм, что и описан в статье (трейт описывающий сигнатуру метода и отдельные пустые структуры для каждой его реализации), но он является нативным типом для лямбда-функций, что позволяет использовать его намного удобнее

Когда вы пишите

"Vec<Rc<RefCell<dyn Observer>>>"

Это должно само по себе наталкивать на мысль что что-то идет не так и возможно надо найти либо другое решение либо использовать тут не Rust, а язык с GC

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS