- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
public class Encoder {
public static void encode(final OutputStream out, Node node) throws IOException {
node.accept(new NodeVisitor() {
@Override
public void string(StringNode node) {
byte[] value = node.toByteArray();
out.write(Integer.toString(value.length).getBytes(Constants.CHARSET));
out.write(':');
out.write(value);
}
// ... другие методы для других типов нод ...
}
}
}
Решил поменять в паре-тройке модулей пачки ифов на паттерн visitor... И получил пинка от жабы ;(
write() кидает IOException, а значит и метод string() в анонимном классе тоже должен кидать, и метод string() в интерфейсе NodeVisitor тоже... Но ведь соседним посетителям этот IOException нахер не сдался...
Checked exceptions - зло.
WGH 12.10.2013 18:21 # 0
bormand 12.10.2013 18:25 # 0
Уж лучше я откачу все обратно, на версию без visitor'а. Она была понятней:
WGH 12.10.2013 18:26 # 0
bormand 12.10.2013 18:28 # +5
А я и не собираюсь добавлять его в интерфейс visitor'а, ибо это совсем уж пиздец и нарушение логики. Еще более полный, чем туннелирование через RuntimeException ;)
Я просто откачу на if'ы и затаю обиду на подлую жабу...
Dummy00001 12.10.2013 21:55 # 0
Practice makes perfect!
Dummy00001 12.10.2013 22:02 # 0
kegdan 12.10.2013 23:20 # 0
kegdan 12.10.2013 23:19 # 0
3.14159265 12.10.2013 23:18 # +2
pingw33n 14.10.2013 22:07 # +1
roman-kashitsyn 12.10.2013 20:25 # +1
LispGovno 20.11.2013 21:01 # 0
guest 22.11.2013 19:36 # −1
tirinox 12.10.2013 21:49 # +1
3.14159265 12.10.2013 23:28 # 0
>node.toByteArray();
>getBytes(Constants.CHARSET));
И самое интересное:
>out.write(':');
Неявная конвертация чара в int. Да и еще мимо чарсетов.
Сразу в первой строке OutputStream настрожил.
А теперь к тому как бороться. Во-первых не надо конвертить ничего в байты, надо юзать Writerы/CharBuffer.
Давно сделал себе статик хелпер-методы:
appendAll(Appedable a, CharSequence... str), append(Appedable a, CharSequence str), append(Appedable a, char c).
В них кинул нормальные исключения, добавил варарг - не люблю писать кучу раз write, append, хоть в Appendable и флюент-интерфейс.
Ну и Writer extends Appedable. Потому туда тоже пишем appendом.
Конечно человека который в год пишет строк десять на яве и не имеет под рукой либы с хелперами такие вещи это жопа, да.
bormand 13.10.2013 08:56 # 0
А там формат двоичный (торрентовский bencode http://bittorrent.org/beps/bep_0003.html)... И "строки" это блобы на самом деле, у которых внутри массив байтов. В которых те же sha-1 в двоичном виде могут валяться. Поэтому через writer'а они не пролезут.
> Неявная конвертация чара в int.
Ну не гонять же перекодировщик ради одного ascii символа?
3.14159265 14.10.2013 14:01 # 0
А если там не однобайтный, а джвухбайтный уникод?
Стрёмно как-то.
А еще порядки байт. Может лучше саппендить с предыдущей строкой?
bormand 14.10.2013 17:36 # 0
> А если там не однобайтный, а джвухбайтный уникод?
Протокол двоичный, и согласно спеке там должен стоять именно один байт с ascii кодом ':'. Ну можно написать его числом, чтобы не смущало, но зачем? Саппендить с длиной блоба, стоящей до ':' тоже можно... А вот с тем, что стоит справа - не стоит, ибо справа от нее блоб (в терминологии bencoding названный string).
3.14159265 14.10.2013 18:07 # 0
Тогда не пойму смысла конвертации стринга в массив байт выше:
>.getBytes(Constants.CHARSET)
Можно ебашить чары сразу в поток. Выиграем, не создавая лишний объект.
bormand 14.10.2013 18:14 # 0
Хм, но у него же нет метода для записи чаров. Циклом по одному символу разве будет эффективнее?
3.14159265 14.10.2013 19:01 # 0
А разве массив байт внутри getBytes наполняется как-то по другому:?
Плюс не тратим время на конверсию (кстати когда-то замерял, на некоторых чарсетах ощутимо ).
bormand 14.10.2013 19:22 # +1
Да UTF-8 по идее должен быть ненамного медленнее ascii. Там же конвертация из юникодных поинтов - банальное байтоебство со сдвигами и масками...
> А разве массив байт внутри getBytes наполняется как-то по другому
Там вызова write нету ;) Хотя... с другой стороны он вполне может заинлайниться.
anonimb84a2f6fd141 14.10.2013 19:59 # −1
bormand 13.10.2013 09:22 # 0
anonimb84a2f6fd141 14.10.2013 19:58 # −1
Вся суть жабы. Выплавляем сталь в каждом дворе.
wvxvw 12.10.2013 23:35 # 0
Или что-то такое.
roman-kashitsyn 12.10.2013 23:59 # 0
wvxvw 13.10.2013 00:45 # 0
Смотрим в педивикию на хрестоматийный пример, и видим как в нем все посещаемые обязуются реализовать метод вызываемый постетителем (accept). Без этого шаблон не взлетит какбы.
bormand 13.10.2013 08:50 # 0
Все реализации нод (StringNode, ListNode и т.п.) реализуют интерфейс Node, у которого, внезапно, есть метод void accept(NodeVisitor visitor) см. строку 3.
Что тут отличается от хрестоматийного примера?
wvxvw 13.10.2013 09:59 # +1
Таким образом мы не только не добьемся цели: возможности по-отдельности изменять обработку каждого посещаемого, а сделаем сопровождение такого кода более сложным: у нас будет куча лишнего кода в виде перегруженых string.
Следующий в статье лисповый вариант лучше демонстрирует работу шаблона. Там методы посещаемых объектов не вызывают дальше метод одного какого-то другого объекта, а просто уже непосредственно выполняют нужную задачу.
roman-kashitsyn 13.10.2013 10:23 # +1
Это и есть суть классического шаблона visitor.
Лисповый вариант в статье использует мультиметоды, которые не поддерживаются в мейнстримовых языках. Они как раз скрывают таблицу диспетчеризации по типам. Собственно, visitor это один из способов реализации double-dispatch engine, мультиметодов для бедных.
wvxvw 13.10.2013 10:33 # 0
bormand 13.10.2013 10:42 # 0
Научить ноду писаться в двоичном формате, писаться в json, дампаться на экран в красивом формате, танцевать кан-кан и играть на балалайке? :)
Ну идея то хорошая, вот только все это придется втащить в ноду, предусмотрев в ней все действия на все случаи жизни... А если эта нода часть чужой библиотеки - придется эту библиотеку пилить, чтобы добавить новые действия...
Вот visitor и выворачивает эту схему, позволяя добавлять действия не меняя код Node. И внося вполне понятный недостаток - новые сабклассы Node добавлять становится сложнее.
Собственно и проблема языков без множественной диспетчеризации:
- либо легко добавлять новые сабклассы Node, но сложно добавлять методы
- либо, юзая посетителя, легко добавлять методы, но сложно добавлять новые сабклассы
wvxvw 13.10.2013 10:50 # 0
и т.д. И тогда то, как каждый вид узлов сериализуется / печатается будет описан в соответствующем ему узле. Что впоследствии значит, что если нужно будет поменять то, как печатается какой-то один из узлов, не нужно будет менять класс, который их всех печатает.
wvxvw 13.10.2013 10:41 # 0
bormand 13.10.2013 10:48 # 0
???
Может быть вас смутил синтаксис жабьего анонимного класса?
NodeVisitor это интерфейс. А запись new NodeVisitor() { // перегрузки методов } это и есть одна из его реализаций (в данном случае она сохраняет дерево в поток в формате bencoding). В соседних файлах есть и другие реализации, выполняющие другие действия над нодами.
wvxvw 13.10.2013 10:52 # 0
bormand 13.10.2013 10:56 # 0
Ну скорее не по количеству, а по частоте изменений/добавлений. Если чаще добавляют методы - то visitor, если чаще добавляют сабклассы - то обычные виртуальные методы... Если часто добавляют и то и то - проще повеситься или сменить язык ;)
Все-таки отсутствие множественной диспетчеризации сказывается.
bormand 13.10.2013 10:34 # 0
Дык в том и суть паттерна visitor :) Отделить проверки типов и обход структуры от действий над нодами... Сам обход остается внутри accept'ов, проверки типов становятся ненужными за счет виртуальных вызовов, а действия реализуются снаружи в сабклассах NodeVisitor.
> лисповый вариант
А вот лиспу паттерн visitor, имхо, вообще нахрен не сдался. visitor это ж костыль для ООП языков, которые не умеют во множественную диспетчеризацию...
Lure Of Chaos 12.10.2013 23:43 # 0
anonimb84a2f6fd141 14.10.2013 22:36 # +2
А не съебать ли ему обратно на сишку?
WGH 15.10.2013 17:22 # 0
anonimb84a2f6fd141 15.10.2013 23:23 # −1
Практически в каждой проге на си, работающей с сетью, можно найти место, в котором она падает в бесконечный цикл с потреблением 99% cpu, потому, что кто-то не проверил код возврата чтения из сокета.
WGH 15.10.2013 23:40 # 0
roman-kashitsyn 16.10.2013 08:10 # +1
Они знали, что task-based concurrency лучше сочетается с явным возвратом ошибок. Кроме того, овер 9000 программистов не могут корректно обрабатывать исключения, и корректность их обработки можно легко нарушить случайным фиксом.
С другой стороны, меня лично немного напрягает постоянно долбить проверки на ошибки. Судя по гитхабу, многие вообще их игнорят.
LispGovno 16.10.2013 11:07 # 0
anonimb84a2f6fd141 17.10.2013 21:23 # −1
guest 18.10.2013 11:04 # +1
Закирой, закирой свой паганий рот, нечястивец!
TarasB 16.10.2013 11:41 # 0
guest 18.10.2013 11:05 # 0
PragramistOtBoga 13.10.2013 00:53 # −4
Stertor 13.10.2013 01:00 # −3
PragramistOtBoga 13.10.2013 03:52 # −4
TarasB 16.10.2013 11:41 # +2
Stertor 16.10.2013 11:46 # 0