Denis Gladkikh

outcoldman

My personal blog about software development

  • 31 Mar 2012
  • MVC, MVP, MVVM, Legacy

Я теперь понимаю, почему отличные книги по программированию получаются у людей, которые проработали в огромных компаниях, существовавшие ни один десяток лет. Ничто так не учит, как Legacy (старый) код. Он заставляет совсем по-новому смотреть на добавление функционала и исправление ошибок методом «по-быстрому».

Несколько раз подумайте, прежде чем делать быстрый «fix»

Предположим, что вы отображаете на экране список людей с контактами. Вы их откуда то импортируете, а потом читаете, например, из базы данных. Контакты у вас отображаются на нескольких представлениях: карточка контакта, список контактов и т.п.

К вам приходят требования, что, когда у контакта нет телефона, то нужно отображать надпись «Нет телефона». Все достаточно просто, как решать?

Решение 1.

Мы храним все в БД – можно просто все NULL в колонке телефонов контактов заменить на «Нет телефона». И при каждом импорте просто заменять так же NULL на «Нет телефона». Самое простое решение – время на добавление функционала – ну 20 минут максимум. Заказчик и начальство довольные, а вы уже можете пойти заниматься любимым домашним проектом. Но у этого подхода есть очевидные проблемы – а что если нам придется эту «Нет телефона» поменять на «Телефон отсутствует» или локализовать эту надпись? Проблема в том, что получить это новое требование вы можете уже через пару месяцев, когда ваш функционал уже давно и хорошо используется.

Решение 2.

Напоминаю, что представлений много, а вот методов GetContact и GetContacts всего два. Можно добавить строку «Нет телефона» в ресурсы, и при чтении данных просто проверять если свойство Phone пустое, то подставлять туда строку из ресурсов. Опять же 20 минут на добавление функционала. Все довольны. Ресурсы можно локализовать, строку можно легко поменять.

Итак, а чем же так ужасно решение 2? Ну, во-первых, мы убиваем возможность узнать, а есть ли телефон у контакта или нет? Ведь на это может быть завязано много логики, например, показывать или нет кнопку «позвонить» на карточке контакта. Да-да, это же можно решить достаточно просто, добавить свойство к модели Контакта, которое будет хранить информацию, а есть ли телефон у контакта или нет, а в методах GetContact и GetContacts выставлять это свойство. А потом к вам приходит еще одно требование, что все-таки на карточке контакта вместо «Нет Телефона» нужно просто писать «Отсутствует». И что теперь? Модель будет всегда содержать «Нет телефона», на одном из представлений мы будет проверять свойство «а есть ли телефон», и отображать «Отсутствует» вместо того, что хранится в поле телефон, если его нет.

Ну и не забывайте, что код проверки «существует ли телефон» может уже присутствовать до того, как вы будете добавлять свой функционал. Существовать он может в каком-нибудь потайном месте, которое очень редко тестируется (legacy код, ага).

Решение 3. Правильное.

Читаем еще раз требование. Видим слово «Отображать» - идем на представления – сколько бы их не было, и добавляем там эту логику. Такое решение может потребовать у вас намного больше изменений в коде (зависит от количества различных представлений контакта), но теперь вы точно знаете, что не портите модель данных, а так же вероятность того, что вы испортите существующую систему – минимальное. Максимум – вы забудете добавить эту логику на какое-то представление.

Технологии

К хорошему привыкаешь достаточно быстро. Только после того, как тебе нужно сделать что-то на MFC или WinForms – ты вспоминаешь насколько же классная технология WPF, насколько же нужны и полезны все эти ViewModel/BindingModel, где ты можешь решить вышеописанную проблему достаточно легко и правильно.

Have a question? Want to follow up? Send a comment? Or just ask for help or consultation? Send me an email at public[at]denis[dot]gladkikh[one more dot]email.