Форум на Kuban.ru (http://forums.kuban.ru/)
-   Разработка программ (http://forums.kuban.ru/f1024/)
-   -   Красивый код (http://forums.kuban.ru/f1024/krasivyj_kod-6253169.html)

Добрых дел мастер 08.11.2014 21:34

Красивый код
 
Всех приветствую.
Я - админ с 12-летним стажем. Лет 10 назад писал на c++, делфи, даже ассемблере, но все успешно забыл. После этого писал только скрипты (perl, bash).
Решил выучать java. При чем, качественно выучить, со всеми бестпрактисами.
Пока читаю книгу по написанию "hello world", я параллельно практикуюсь - пишу программу, анализирующую состояние сервера (для aix, но это почти то же самое, что для linux).
И тут возник вопрос(собственно, постановка задачи):
я прочитал, что каждый метод должен помещаться на одном экране.
У меня есть метод "собрать информацию с сервера". Он читает разные конфиги, запускает программы и заносит различную информацию в различные переменные(которые будут проанализированы в другом методе).
Метод, мягко говоря, немаленький и я не понимаю, как правильно его уменьшить.
Естественно, все второстепенное (открыть файл, распарсить в соответствии со структурой, вынутьзначение) оформлено второстепенными приватными методами.
Логика метода весьма проста:
1. Получить список сетевых интерфейсов, запомнить их названия
2. Для каждого из сетевых интерфейсов получить их (список параметров) и запомнить
3. Получить список дисков
...

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

wayerr 09.11.2014 00:30

>Логика метода весьма проста:
>1. Получить список сетевых интерфейсов, запомнить их названия
>2. Для каждого из сетевых интерфейсов получить их (список параметров) и запомнить
>3. Получить список дисков
>...


очевидно, что самые простой вариант - объявить интерфейс подобный InfoProvider<T> ** void getInfo(Callback<T> receiver) **

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

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

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

kowalski 09.11.2014 01:08

Сферический совет в вакууме: если хочется лучших практик, почитайте за паттерны и рефакторинг. Фаулер, Банда четырёх там, Макконнелл, всякое такое. Но аккуратно, культа не делайте.

40KHYTbIU 09.11.2014 11:19

0-Добрых дел мастер > Т.к. нет полной картины что делает этот чудо-метод, будем крутить коня в томате.

1. Познать ООП, потому что ты пытаешься писать на bash "словами" из java.

2. Разделять и властвовать: каждый твой пункт вынести запределы метода, любым приемленмым способом. см 1

3. Пиши бейби-код, так чтоб любой сторонний человек знающий java смог быстро понять о чем речь.

4. Пиши камменты и тесты, поможет в 3

П.с. полезно смотреть код других проектов, github в помощь

Добрых дел мастер 09.11.2014 15:21

[quote=wayerr;37073244] очевидно, что самые простой вариант - объявить интерфейс подобный InfoProvider ** void getInfo(Callback receiver) ** который и дергать в "основном методе" а получение спсика интерфейсов и их параметров в один класс реализующий сей интерфейс, для дисков другой причем там в каждом классе отдельно выделить получение списка интерфейсов, отдельно получение их параметров[/quote]
Главная проблема в том, что трудно провести границу между интерфейсами.
Например, изучив список файловых систем, хочется понять что за диски, может быть они находятся на внешней СХД, подключенной по iSCSI, что ведет нас к изучению сетевых адаптеров...
[quote=kowalski;37073370] почитайте за паттерны и рефакторинг. Фаулер, Банда четырёх там, Макконнелл, всякое такое.[/quote]
Я пока-что не дочитал "Java: A Beginner's Guide". Дальше по плану - TDD. Паттерны и рефакторинг, насколько я понял, это уже на несколько уровней выше.
Метаться не хочется воизбежание каши в голове, но хочется хотя бы "не укоренять самые крупные ошибки". Поэтому прочитал пару статей по рефакторингу, но вопросов возникло больше, чем ответов.

[quote=40KHYTbIU;37074734]Познать ООП, потому что ты пытаешься писать на bash "словами" из java.[/quote]
Есть такое. Ну почти. Раньше я писал на c++ и делфи, так что слова class и try..catch мне знакомы.
[quote=40KHYTbIU;37074734]3. Пиши бейби-код, так чтоб любой сторонний человек знающий java смог быстро понять о чем речь.[/quote]
А что такое бейби-код? Гугл ничего не сказал.

wayerr 09.11.2014 16:48

>Главная проблема в том, что трудно провести границу между интерфейсами.

это не проблема красоты кода, а проблема проектирования

economist 09.11.2014 18:44

Рефакторинг... Скока раз порывался свой код улучшить, а то и переписать заново. Вынести нящки в классы, функции, развернуть свой git-реп итд. Став "нассяльника" - пытался заставить сделать это подчиненных. Короче... - это несбыточно. Работает - и фиг с ним. Не надо ничего уменьшать, ведь и художники бывают многословными...

Добрых дел мастер 09.11.2014 19:22

[quote=wayerr;37077135]это не проблема красоты кода, а проблема проектирования [/quote]
а есть что-нибудь короткое, где описано, как правильно проектировать приложение? Нормальные книги я, конечно, прочту, но это будет позже.
[quote=economist;37077941]Короче... - это несбыточно. Работает - и фиг с ним. Не надо ничего уменьшать, ведь и художники бывают многословными... [/quote]
Так речь не о рефакторинге, а о том, чтобы сразу учиться хорошему.

wayerr 09.11.2014 21:01

>а есть что-нибудь короткое, где описано, как правильно проектировать приложение?

не встречал, можно почитать про базовые ошибки напр. тут [url]https://ru.wikipedia.org/wiki/Антипаттерн#.D0.90.D0.BD.D1.82.D0.B8.D0.BF.D0.B0.D1.82.D1.82.D0.B5.D1.80.D0.BD.D1.8B_.D0.B2_.D0.BF.D1.80.D0.BE.D0.B5.D0.BA.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B8_.D0.9F.D0.9E[/url] но там все описано уже затейливое терминологогией

wayerr 09.11.2014 21:20

> Скока раз порывался свой код улучшить, а то и переписать заново. Вынести нящки в классы, функции, развернуть свой git-реп итд. Став "нассяльника" - пытался заставить сделать это подчиненных.

ээ, да вам нужны Программисты, а не программисты, может даже тимлид просто нормальный который таки умеет рефакторить код, ведь это тривиально.

ipp 09.11.2014 21:55

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

wayerr 09.11.2014 23:11

>И пофигу как там внутри.

потому в краснодаре и нет софтверных контор, некоторые даже без VCS работают

Добрых дел мастер 09.11.2014 23:59

[quote=wayerr;37079087]не встречал, можно почитать про базовые ошибки напр. тут [url]https://ru.wikipedia.org/wiki/Антипа...8_.D0.9F.D0.9E[/url] но там все описано уже затейливое терминологогией [/quote]
Очень непонятно описано. Но ладно, поищу что-нибудь.
[quote=ipp;37079591]люсану, идеального кода нет. Если код работает годами и заказчик доволен работой, то это и есть идеальный код. [/quote]
Как я писал выше, дело ведь не в результате, а в обучении. Наговнокодить я всегда успею, но хотя бы стремиться я должен к чему-то правильному.

wayerr 10.11.2014 00:15

>Очень непонятно описано

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

в случае :

>Логика метода весьма проста:
>1. Получить список сетевых интерфейсов, запомнить их названия
>2. Для каждого из сетевых интерфейсов получить их (список параметров) и запомнить
>3. Получить список дисков

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

так хороший код для того кто думает процедурами будет (вместо одного метода):

netdevs = get_networking_devices();
for(dev: netdevs) **
__attrr = get_netdevice_attrs(dev);
__save_attrs(dev, attrr)
**
strdevs = get_storage_devices();
for(dev: strdevs) **
__get_storage_attrs(dev);
**


школьники только постигшие ооп, напишут так


netdevs = NetDevsService.list();
for(dev: netdevs) **
__attrr = dev.getAttrs();
__PeristentService.save(dev, attrr)
**
strdevs = StorageService.list();
for(dev: strdevs) **
__attrr = dev.getAttrs();
**

более опытные индусы будут получать "сервисы" через IoC контейнер и накрутят интерфейсов

wayerr 10.11.2014 00:20

да, это будет лучше чем один жЫрный метод, но все равно не идеал, просто потому что сервисы получающие список устройств в принципе можно объединить одним интерфейсом, и перечислять их в цикле, т.к. в некоторых случаях они могут быть не в виде линейного списка, а в виде дерева\иерархии (usb устройства, контроллер-диск-разделы, или контроллер-диски и контроллер-рейд-разделы) то перечислять их стоит в колбек (для примитивных языков программирования тут вылезает паттерн visitor) и куча других тонкостей, это все требует вдумчивого подхода к проектированию, анализа требований (в т.ч. к отображению результатов) и т.п.

Добрых дел мастер 10.11.2014 09:26

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

wayerr 10.11.2014 21:47

>методы для каждой отдельной железки

суть была не в этом

ipp 11.11.2014 13:38

15-Добрых дел мастер >Жирный метод желательно конечно разбить.

[quote]
Мне кажется, если я буду отдельно реализовывать методы для каждой отдельной железки - будет намного больше дублированного кода.
[/quote]
Не плоди сущностей больше чем необходимо. А многократное дублирование кода - это еще большее зло, так как при изменениях обязательно где нибудь что нибудь забудешь обновить.
Но, если хочется можешь создать некий базовый класс и в нем реализовать нечто общее для всех, а далее создать наследников от него для каждой железяки с учетом ее особенностей. А можно объявить простой интерфейс для работы с железякой, а потом просто в классах для работы с конкретной железякой реализовывать поддержку этого интерфейса. А тот кто захочет получить инфу о железяке будет использовать этот интерфейс для работы. Только при объявлении интерфейса не переборщи, это должно быть нечто простое и понятное.

zampolit2006 13.11.2014 13:01

0-Добрых дел мастер > "[em]я прочитал, что каждый метод должен помещаться на одном экране. [/em]" - теория хорошая штука, но не стоит воспринимать ее буквально, как многие местные гуру. :)


Текущее время: 04:36. Часовой пояс GMT +3.