2013-06-12

Про коммуникабельность, умение договариваться и странные вещи

Постоянно записываю мысли, которые надо подумать. Иногда поток сознания созревает до чего-то осмысленного. Вот и сейчас читаю: "Бесят люди которые не находят в себе силы признать неправоту и после тотального слива заканчивают фразу: всё разговор окончен, почему ты споришь и т.д.". Перечитываю. Где-то я это уже видел. Ищу. Восемь месяцев назад. Раз за разом, одни и те же проблемы.

Про коммуникабельность

Не смотря на это, я достаточно эффективно общаюсь с большинством людей вокруг себя, особенно с теми, кто мне близок, важен или интересен. Если был неправ стараюсь явно это признать, если человек обиделся - извиниться. Пока не доказано обратное считаю, что все люди хорошие и адекватные, любое действие содержит внутренний смысл и продиктовано какими-то обстоятельствами, даже если для меня и, в худшем случае, для окружающих это крайне странно.

Про решение проблем

До сих пор считаю, что абсолютно любую проблему можно решить. Рассказ про 7 перпендикулярных линий у меня вызывает яростное отторжение. Есть способы. Нерешаемых проблем нет. Начиная, с проактивности из 7 навыков, и кончая тяжелой артиллерией - картами реальности из Целей и Критической цепи. Инструменты на любой вкус. Всё решаемо. Если есть воля, мотивация, эмоциональная энергия - куча слов - всё возможно.

Но однажды возникает проблема. Я вижу, что она решаема, даже вижу как - совсем не проблема, ерунда. Но воли сделать даже минимальное усилие нет. Есть понимание, что было затрачено достаточно усилий, время вспомнить про феномен Базермана и сказать стоп. Спросить себя "а не херню ли я делаю", или сказать: "делай как считаешь нужным, но без меня", или уйти, просто в другую комнату или совсем - варианты по обстоятельствам.

Странные вещи

Иногда люди делают странные вещи. Попытка доказать бредовость затеи, аппеляция к физическим законам, математике, здравому смыслу натыкается на яростный отпор "всё разговор окончен...". Если это меня никак не касается, то в этот момент разговор же и заканчивается. Но иногда требуют от меня делать странные вещи. В этот момент встает дилемма: продолжать делать по своему, провоцируя конфликт, или делать бредовую вещь, с ясным осознанием того, что делаешь бред. Диалог крайне затруднителен. И, обычно, после нескольких итераций заканчивается: пофиг, мне это не трудно. Но иногда люди делают опасные вещи. Если они опасные для себя, то мне в общем-то поровну: взрослые люди должны сами за себя отвечать. Гораздо хуже, когда своей глупостью, они начинают представлять опасность для окружающих. Здесь уже приходится быть начеку, чтобы вовремя вмешаться.

P.S.

Кстати, возвращаясь к тому Петрову из 7 линий, быть может ему уже всё равно?

2013-06-09

Bleeding Edge

Очень интересное ощущение - оказаться на переднем краю (во временных масштабах Java) развития какой-то технологии (JavaFX) после работы со стабильной, читай мертвой (Swing). Стабильный API, JavaDoc, книги, стопицот ответов на StackOverflow в конце концов. Для любой проблемы известно не одно решение, боле того "после двух релизов баг становится фичей", а следовательно ждать исправления нет смысла. Первое что замечаешь - нет JavaDoc, в том, что есть - нет примеров. Второе - есть баги. Тысячи их, а багтрекер почему-то не просто доступен только после регистрации, более того, он закрыт для индексации. Когда делал что-то на Swing у меня ни разу не было желания засабмитить багрепорт, сейчас - постоянно. Третье - всё самое вкусное и желанное будет в версии next. Восьмерка выйдет весной следующего года, если опять не отложат.

Инкапсуляция говнокода

When a small hack could solve all my problems
Рано или поздно все сталкиваются с нерешаемой красивыми путями проблемой, а чем активнее развитие технологии, тем чаще это случается. У всех, правда, разный порог нерешаемости. Приняв неизбежность, пишем свою обёртку, если код написан с принципом Open-Closed в голове, то перегружаем пару методов, если нет - то копируем всё. После чего исправляем ту самую проблему, пишем обоснование и ссылку на багрепорт. Когда-нибудь это можно будет удалить.

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

2013-06-01

Вопросы читабельности кода

Схороненный псто из умершего базза.

PEP-20
Наткнулся на интересную статью у avva, про избыточность кода с целью повышения читабельности. А потом и +Евгений Доронин запостил про законы Мерфи для разработки. Пару лет назад я тоже задавался подобным вопросом. До сих пор не нашел однозначного ответа, хотя и //do nothing с тех пор эволюционировал в log.error("Inconsistent state {}",...) или throw new IllegalStateException, чтобы сильно умный статический анализатор заткнулся и не предлагал убрать пустые условия и максимально быстро упасть если забыли, после добавления enum, также добавить обработку этого значения.

Тернарные операторы по возможности избегаю, а уж если там сложные выражения, с подусловиям, то гораздо читабельней становится замена их на обычный if-else.

Родовая память

Продолжаю читать "Идеальные команды", и там Гради Буч высказал интересную мысль про неявные решения.

И теперь, поскольку мир не стоит на месте, встает интересный вопрос – та архитектура не была подробно отражена в документации, зачастую никакой документации вообще не было, и хотя код верен, он
не может быть истиной в последней инстанции, потому что не  отражает ни логического обоснования, ни аспектов, по которым принималось компромиссное решение, ни любых других вещей, которые невозможно вывести из отдельных строк кода; есть схемы, выходящие за пределы самих этих строк. Такие вещи обычно сохраняются в «родовой памяти». И в любой организации есть патриархи, которые работают там уже давно и лично помнят эти обоснования и возникавшие междисциплинарные вопросы. Сложность здесь в том, что организация продолжает расти, люди перемещаются, и этот адресат может, фактически, уже на ходиться в другом месте.
То есть помимо проблемы временного и географического распределения есть проблема утечки интеллектуальной собственности, потому что хранить ее в «родовой памяти» – страшно дорого, точнее, память эта ничего не стоит, но извлечь из нее информацию может оказаться очень затратным, и чем меньше этой памяти, тем она дороже.


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

Порой решение принять нельзя потому, что не хватает информации, либо времени на детальный анализ. Такие места описываются через TODO или FIXME с описанием проблемы и текущей ситуации:

// TODO Нужно ли это внести в контейнер?
// TODO подумать как прикрутить локализацию

// FIXME не уверен, что первоначальное заполнение списка должно выполняться по событию
// FIXME без этой строчки список почему-то не инициализируется
// FIXME when resolved https://javafx-jira.kenai.com/browse/RT-19089
// FIXME пока нет нормального чего-то, делаем так-то

Когда случается проблема, то такие места выделенные ядовито зеленым цветом сразу привлекают внимание.

2013-05-31

Тестирование файловой системы

Симуляция реального мира в песочнице
Май выдался богатым на новую и полезную информацию по тестированию: цикл статей от Сергея Теплякова, интересное обсуждение в Радио-Т.

В это же время появилась интересная задача связанная с доступом к файловой системе. Алгоритм:
1. получаем список файлов
2. каким-то образом фильтруем
3. обрабатываем отфильтрованные файлы.

Работа с ФС (по крайней мере в 6-й java) сосредоточена в классе File: list, delete, работа с путями. По сути эти методы относятся к сервису. Хочется иметь интерфейс чтобы убрать зависимость и получить возможность для подмены реализации, ну или хотя бы подмены mock-ами для тестирования.

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

Постановка задачи

По заданному пути
удалить пять файлов
с учетом сортировки по возрастанию
имя которых начинается с цифр
содержащие первой строкой ШИББОЛЕТ.

Решение

Реализация находится здесь, тесты здесь.

Сервис

FileService представляет собой фактически обертку с некоторыми удобными методами

public class FileService {
    /**
     * Get files in path
     */
    public Collection dir(File path) {
        return Arrays.asList(path.listFiles());
    }

    /**
     * Delete path
     */
    public void delete(File file) {
        if (!file.delete()) {
            log.debug("Cannot delete " + file.getPath());
        }
    }

    /**
     * Open file
     */
    public InputStream openStream(File path) throws FileNotFoundException {
        return new FileInputStream(path);
    }

    public Reader openReader(File path) throws FileNotFoundException {
        return new FileReader(path);
    }

    public boolean isDir(File path) {
        return path.isDirectory();
    }
}

Фильтрация

Фильтрация осуществляется при помощи удобнейшего FluentIterable:

public static Iterable filter(FileService fileService, File dir, int count, String mask, String prefix) {
        return FluentIterable
                .from(Ordering.natural().immutableSortedCopy(fileService.dir(dir)))
                .filter(new NamePredicate(mask))
                .filter(new ContentPredicate(fileService, prefix))
                .limit(count)
                .toImmutableList();
    } 

И пары предикатов для фильтрации по имени и содержимому. В предикат фильтрации по содержимому передается сервис файловой системы, плюс в Java 7 можно использовать очень удобный try для работы с Closeable ресурсами:
public class ContentPredicate implements Predicate {
    private FileService fileService;
    private String prefix;

    public ContentPredicate(FileService fileService, String prefix) {
        this.fileService = fileService;
        this.prefix = prefix;
    }

    @Override
    public boolean apply(@Nullable File file) {
        try (BufferedReader br = new BufferedReader(fileService.openReader(file))) {
            return prefix.equals(br.readLine());
        } catch (IOException ioe) {
            ContentPredicate.log.debug("Cannot read file " + file);
        }
        return false;
    }
}

Тестирование

С помощью mockito подменяем возвращаемые значения при тестировании предикатов и фильтра. Сами тесты получаются очень простыми и быстрыми:

@Test
    public void FilterTest() throws FileNotFoundException {
        FileService fileService = Mockito.mock(FileService.class);
        File dir = new File("/test/dir");
        List files = Arrays.asList(
                new File(dir, "100file.name"), // 0
                new File(dir, "bl"), // 1
                new File(dir, "099file.name"), // 2
                new File(dir, "bla"), // 3
                new File(dir, "098file.name"), // 4
                new File(dir, "blah"), // 5
                new File(dir, "097file.name")); // 6
        Mockito.when(fileService.dir(dir)).thenReturn(files);
        Mockito.when(fileService.openReader(files.get(6))).thenReturn(new StringReader("TEST"));
        Mockito.when(fileService.openReader(files.get(4))).thenReturn(new StringReader("TEST"));
        Mockito.when(fileService.openReader(files.get(2))).thenReturn(new StringReader("!!!!"));
        Mockito.when(fileService.openReader(files.get(0))).thenReturn(new StringReader("TEST"));

        Iterable result = Application.filter(fileService, dir, 3, "^\\d+.*$", "TEST");
        Iterator i = result.iterator();
        Assert.assertEquals(files.get(6), i.next());
        Assert.assertEquals(files.get(4), i.next());
        Assert.assertEquals(files.get(0), i.next());
        Assert.assertFalse(i.hasNext());
    }

Итог

Из минусов: имеется обертка, которая нужна лишь для тестирования "DIP головного мозга". Плюсы: классы становятся простыми и легко модифицируемым, самая важную логику становится легко тестировать.

2013-05-26

Экипировка к походу

В этом году планируется совершить несколько походов, поэтому активно озаботился экипировкой.