LSP

Явное грубое нарушение LSP (простой пример)

function f(Shape p) {
    if (p isinstance Circle) {
       drowCirle(p);
    }
    if (p instance Rectangle) {
        drowRectangle(p);
    }
}

Заключается в том, что мы вынуждены работать с объектом одного супертипа по-разному, кроме этого при добавлении новых видов фигур придется модифицировать этот код.
Этот пример показывает что когда мы говорим про LSP совсем не обязательно речь будет идти про входящие параметры для функции (про сигнатуру, хотя часто когда кто-то рассказывает об этом, то речь идет именно о наследовании и о переопределении методов и неизменности сигнатуры), а речь идет о поведении программы при использовании пришедшего типа данных. LSP призывает нас писать код так, чтобы вызывающий его код не заботился о возможных side-effect, а работал одинаково для любого подтипа супертипа. Если программа при этом сломается, то принцип считается нарушенным, если программа будет корректно работать, можно сказать что принцип соблюдается.
Разберем более сложный пример.

class Rectangle() {
    property Width;
    property Height;
    virtual function setWidth(int Value);
    virtual function setHeight(int Value);
    function getWidth() {return self.Width;}
    function getHeight() {return self.Height;}
}

class Square() ext Rectangle {
    function setWidth(inv Value) {
        self.Width = Value;
        self.Height = Value;
    }
    function setHeight(inv Value) {
        self.Width = Value;
        self.Height = Value;
    }
}
Есть функция f которая работает с этим кодом
function f(Rectangle t) {
    t.setWidth(5);
    t.setHeight(4);
    assert(t.getWidth() * t.getHeight(), 20)
}

Очевидно, что для случая с квадратом тут произойдет ошибка, ведь программист функции f делает предположение что если у прямоугольника установить стороны в определенные значения, то площадь будет равной произведению сторон. Эта функция показывает нарушение принципа LSP допущенного в коде класса Square.
В статье http://www.webcitation.org/6AJYJLSEa есть продолжение, но я бы хотел остановится пока на этом месте. Вчера всплыл вопрос: может ли переопределение конструктора дочернего класса быть нарушением LSP? В данном примере, наверное, нет. Мы видим что тут вообще не идет речь о инстанцировании этих классов, еще очевидно что их конструкторы могут быть разными, прямоугольнику нужны значения сторон, квадрату — только одной стороны. Нарушение создается в методах класса Square и проявляется в функции f при использовании супертипа Rectangle. Но это только в данном примере именно так, а что если нам нужно написать функцию которая будет с помощью порождающего шаблона создавать класс? Сразу кажется что было бы удобно иметь единый набор и порядок параметров для инстанцирования объектов фабрикой. Предположим что функция будет работать именно с нашими классами Прямоугольник и Квадрат. Теперь вспомним что нарушение LSP проявится именно тогда, когда функция оборачивающая фабрику сломается при получении некоторого подходящего типа данных и оперировании им. Что вообще может получить такая функция на вход? Допустим, название класса и массив параметров, это довольно типичный набор, получается тут не идет речь о каком-то сложном типе данных (как в академических примерах) на входе и вся работа будет заключатся в том чтобы написать команду инстанцирования класса с массивом параметров на входе, причем даже не понадобится большого switch case. По-моему тут вообще не идет речи о нарушении принципа подстановки, ведь смысл принципа в том, чтобы писать новые подклассы так, чтобы те кто используют базовый класс при получении подкласса работали как и прежде, если же они упадут, значит в своем подклассе вы нарушили принцип подстановки.
Можно придумывать еще примеры: хотим работать с ХранилищеИнтерфейс, при этом сами хранилища бывают РедисХранилище, МемкешХранилище, ПостгресХранилище, очевидно что они должны быть сконфигурированы разными наборами параметров и использующему коду должно быть все равно как они создавались, важно что вызывающий код не должен заметить никакой разницы между ними при подстановке любого из них.

про биткойн

Откуда берутся нули в начале каждого хеша блока? Очень просто: задача состоит в том чтобы найти хеш значение которого меньше самого маленького хеша в блокчейне. Например:
>>> hexdec('00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048');
=> 1.3859490975361E+67
>>> hexdec('000000006a625f06636b8bb6ac7b960a8d03705d1ace08b1a19da3fdcc99ddbd');
=> 1.1203570477736E+67

видно что родительский хеш больше дочернего. Это и влияет на вычислительную сложность, она увеличивается потому что ограничение на результат ужесточается.
Сколько транзакций в блоке? Вот тут непонятно, пишут что более 500, хотя сейчас есть график в котором среднее значение от 1000 до 2000. Не знаю почему оно такое, почему нельзя использовать 2-3 транзакции. Но я догадываюсь что за включение транзакций в блок можно получить награду, потому что есть комиссия за это. Она может быть и нулевая, тогда твою транзакцию не возьмут из кучи неподтвержденных транзакций очень долгое время — это не выгодно. Единственный ли это способ майнинга? Похоже что нет, кажется система сама начисляет тебе биткойны когда ты собираешь блок. Т.е. в этот момент в экономику биткойна попадает актуальное на данный момент значение количества, оно было сначала 50 монет, затем 25 и так далее, каждые 210 тыс блоков. Сейчас уже блокчейн вроде как содержит около 0.5 млн блоков, т.е. уже за майнинг должны давать 12.5 (ну или скоро это случится).
Получается майнер получает этот крупный выигрыш, а что с комиссиями? Он их тоже получает? Выходит X биткойнов от системы + еще все комиссии со всех транзакций?
Отрывок из книги
А вот график количества транзакций в блоке на котором видно, что количество растет с годами, причем имеет очень большой разброс: иногда удается собрать блок с малым количеством, иногда с большим. Интересно почему? Их в целом больше стало, нужно больше обрабатывать, но может уже не удается собрать нужный хеш из малого количества транзакций? В итоге нужно брать больше?

Космонавт №34. От лучины до пришельцев

Ракета сначала не хотела летать, но она была очень красивая. И, как показала время, она останется самой надёжной ракетой всех времён и народов. Красивая конструкция всегда работает лучше.

Гречко о ракете Р-7
Прочитав это, я подумал о программном коде, о том, что красивый код работает лучше.

скрипты для сервисов google

Для меня стало открытием возможность быстро писать скрипты для гуглосервисов с помощью https://developers.google.com/apps-script
С их помощью можно автоматизировать разные задачи, мне требовалось удалять письма приходящие от системы видеонаблюдения xeoma, они должны были попадать в корзину, где я еще 30 суток смогу их посмотреть.
На самом деле это можно сделать обычными фильтрами гуглопочты, но попробовать сделать скрипт работающий с API интересней.
Скрипт может запускаться через различные промежутки времени, например я сделал 12 часов.
Вот пример:

function deleteOldXeoma() {
var msgThread = GmailApp.search("subject:(Motion detected*)");
var maxDate = new Date();
var delayDays = 1;
maxDate.setDate(maxDate.getDate()-delayDays);
for (var i = 0; i < msgThread.length; i++) { if (msgThread[i].getLastMessageDate() < maxDate) { msgThread[i].moveToTrash(); } } }

Суть: если я не прочел письма от xeoma в течение суток, они попадают в корзину.
Кстати, найти сами скрипты можно открыв drive.google.com и набрав в поиске type:script