понедельник, декабря 25, 2006

О пользе каментов в коде

/**
 * Retrieves model by its username.
 * @param username Model username or dysplayname
 * @return Model dataobject
 * @exception IndexOutOfBoundsException if no model with given username
 */
public static Model retrieveByUsername(String username)
 throws TorqueException {
    ...
     уже не важно
    ...
}

Использование

/**
 * Check if model with username or displayname exists
 * @param username
 * @return boolean
 */
public static boolean isExists(final String username) {
    try {
        retrieveByUsername(username);
        return true;
    } catch (final TorqueException te) {
        return false;
    } catch (final IndexOutOfBoundsException ioobe) {
        return false;
    }
}

пятница, декабря 08, 2006

URL

"Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку." (с) adontz

среда, ноября 22, 2006

Читатели пишут.
Образец пиздатого кода в ентерпрайз продукте:

bool flag = (AvtConfig::getCapMethod() == Avt::NetworkClient ) ? false : true;
dtmfCtrl = new AvtDTMFCtrl( !flag, flag, flag);

Конструктор мощнецкий, да и лишняя переменная никогда не помешает:

$ grep flag avt_window.cpp
bool flag = (AvtConfig::getCapMethod() == Avt::NetworkClient ) ? false : true;
dtmfCtrl = new AvtDTMFCtrl( !flag, flag, flag );
return ( screenSurface()->flags & SDL_FULLSCREEN) != 0;
int flags = SDL_HWACCEL | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWPALETTE | SDL_ANYFORMAT;
flags = flags | SDL_FULLSCREEN;
app.InitScreen( width, height, 32, flags );

среда, октября 04, 2006

Вот так вот надо манипулировать с датой...
Дело то нехитрое. Надо взять строку и конвертнуть в дату обрезать до дней (убрать часы, минуты, секунды...) и конвертнуть из TimeZone "GMT-8" в "GMT".
Вот пример реализации такой логики:

/**
* Convert formated string from request to Date
* @param startTimeStr formated string "yyyy-MM-dd z"
* @return Date
*/

public Date getStartTime(String startTimeStr) {
    Date result = null;
    // if period parameter init
    if (startTimeStr == null) {
        // get current time in GMT
        final Calendar calStart = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        logger.info("Current start time: " + calStart.getTime());
        calStart.set(Calendar.HOUR, 0);
        calStart.set(Calendar.MINUTE, 0);
        calStart.set(Calendar.SECOND, 0);
        calStart.set(Calendar.MILLISECOND, 0);
        // convert time from GMT to models TZ
        final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
        startTimeStr = sdf.format(calStart.getTime());
        startTimeStr += " " + model.getTimezone();
    }
    logger.info("Start time as string: " + startTimeStr);
    startTimeStr = addStartTime(startTimeStr);
    result = TimeUtil.parseDate(startTimeStr);
    logger.info("Start time: " + result);
    return result;
}

....
/**
* Add time to formated date string
* @param date "yyy/MM/dd z"
* @return
*/

public String addStartTime(final String date) {
    return date.replace("GMT", "00:00:00 GMT");
}

.....
/**
* Translate formated string to date
* @param dateStr formated string
* @return date in GMT
*/

public static Date parseDate(String dateStr) {
     Date result;
     try {
         String dateFormatStr;
         if (Pattern.matches(".*\\d\\d:\\d\\d:\\d\\d.*", dateStr)) {
            dateFormatStr = "yyyy-MM-dd HH:mm:ss";
         } else {
            if (Pattern.matches(".*\\d\\d:\\d\\d.*", dateStr)) {
                dateFormatStr = "yyyy-MM-dd HH:mm";
            } else {
                dateFormatStr = "yyyy-MM-dd";
            }
         }
         final DateFormat sdf = new SimpleDateFormat(dateFormatStr);
         if (dateStr.indexOf("GMT") != -1) {
            final String timeZoneStr = dateStr.substring(dateStr.trim()
                 .lastIndexOf(' '));
            sdf.setTimeZone(TimeZone.getTimeZone(timeZoneStr.trim()));
            dateStr = dateStr.replace(timeZoneStr, "");
         } else {
            sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
         }
         result = sdf.parse(dateStr.trim());
     } catch (final Exception e) {
         return null;
     }
     return result;
}

Результат вполне не очевиден. Был даже юнит тест. Кроме того, по технологии копи+паст был сделан метод getEndTime().

среда, сентября 27, 2006

Что-то там in Action

Есть серия книг типа Spring in Action, Struts in Action.
Но настоящий Struts in Action - это Еклипс + дебаггер...

вторник, сентября 26, 2006

[10:59:54 AM] Я конечно свой язык изобретать не собираюсь, но я сторонник Java-подобного языка, который сочетает в себе все возможности языка Java и необходимые свои фичи.

Вот наш общий знакомый завернул :)

среда, сентября 20, 2006

Тыщу лет таму назад...

Во фантазия у народа :)
...
private Date translateDateAfter(String strValue) {
int value = Integer.parseInt(strValue);
Date returnDate = null;
long secondsInMonth = 2592000; // number of seconds in a month

Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);

switch (value) {
case StaticsDataManager.TODAY: {
returnDate = today.getTime();
break;
}
case StaticsDataManager.PREVIOUS_DAY: {
int dayOfWeek = today.get(Calendar.DAY_OF_WEEK);

if (dayOfWeek == Calendar.MONDAY)
today.add(Calendar.DAY_OF_YEAR, -3);
else if (dayOfWeek == Calendar.SUNDAY)
today.add(Calendar.DAY_OF_YEAR, -2);
else
today.add(Calendar.DAY_OF_YEAR, -1);

returnDate = today.getTime();
break;
}
case StaticsDataManager.LAST_ONE_MONTH: {
returnDate = new Date(System.currentTimeMillis()
- (secondsInMonth * 1000));
break;
}
case StaticsDataManager.LAST_THREE_MONTH: {
returnDate = new Date(System.currentTimeMillis()
- (secondsInMonth * 1000 * 3));
break;
}
case StaticsDataManager.LAST_SIX_MONTH: {
returnDate = new Date(System.currentTimeMillis()
- (secondsInMonth * 1000 * 6));
break;
}
case StaticsDataManager.ALL: {
returnDate = new Date(System.currentTimeMillis()
- (secondsInMonth * 1000 * 1000));
break;
}
}

log.info("After Date = " + returnDate);

return returnDate;
}

вторник, сентября 12, 2006

Болезнь кода или программиста?

Ну вообщем-то вот рабочий код, сделанный скорее всего по технологии "копи-паст":

/**
* Check country and state of member (need for Experian L1 auth).
* @param member
* @return
* @throws TorqueException
*/
private boolean checkTerritory(final Member member) throws TorqueException {
if (!member.getCountry().getAbbreviation().equalsIgnoreCase("US")) {
return false;
}
if (!member.getState().getAbbreviation().equals("AL")
&& !member.getState().getAbbreviation().equals("AK")
&& !member.getState().getAbbreviation().equals("AR")
&& !member.getState().getAbbreviation().equals("AZ")
&& !member.getState().getAbbreviation().equals("CA")
&& !member.getState().getAbbreviation().equals("CO")
&& !member.getState().getAbbreviation().equals("CT")
&& !member.getState().getAbbreviation().equals("DE")
&& !member.getState().getAbbreviation().equals("FL")
&& !member.getState().getAbbreviation().equals("GA")
&& !member.getState().getAbbreviation().equals("HI")
&& !member.getState().getAbbreviation().equals("ID")
&& !member.getState().getAbbreviation().equals("IL")
&& !member.getState().getAbbreviation().equals("IN")
&& !member.getState().getAbbreviation().equals("IA")
&& !member.getState().getAbbreviation().equals("KS")
&& !member.getState().getAbbreviation().equals("KY")
&& !member.getState().getAbbreviation().equals("LA")
&& !member.getState().getAbbreviation().equals("ME")
&& !member.getState().getAbbreviation().equals("MD")
&& !member.getState().getAbbreviation().equals("MA")
&& !member.getState().getAbbreviation().equals("MI")
&& !member.getState().getAbbreviation().equals("MN")
&& !member.getState().getAbbreviation().equals("MS")
&& !member.getState().getAbbreviation().equals("MO")
&& !member.getState().getAbbreviation().equals("MT")
&& !member.getState().getAbbreviation().equals("NE")
// && !member.getState().getAbbreviation().equals("NV")
&& !member.getState().getAbbreviation().equals("NH")
&& !member.getState().getAbbreviation().equals("NJ")
&& !member.getState().getAbbreviation().equals("NM")
&& !member.getState().getAbbreviation().equals("NY")
&& !member.getState().getAbbreviation().equals("NC")
&& !member.getState().getAbbreviation().equals("ND")
&& !member.getState().getAbbreviation().equals("OH")
&& !member.getState().getAbbreviation().equals("OK")
&& !member.getState().getAbbreviation().equals("OR")
&& !member.getState().getAbbreviation().equals("PA")
&& !member.getState().getAbbreviation().equals("RI")
&& !member.getState().getAbbreviation().equals("SC")
&& !member.getState().getAbbreviation().equals("SD")
&& !member.getState().getAbbreviation().equals("TN")
&& !member.getState().getAbbreviation().equals("TX")
&& !member.getState().getAbbreviation().equals("UT")
&& !member.getState().getAbbreviation().equals("VT")
&& !member.getState().getAbbreviation().equals("VA")
&& !member.getState().getAbbreviation().equals("WA")
&& !member.getState().getAbbreviation().equals("WV")
&& !member.getState().getAbbreviation().equals("WI")
&& !member.getState().getAbbreviation().equals("WY")) {
return false;
}
return true;
}

четверг, сентября 07, 2006

Основная проблемма рефакторинга.
Опытные инженеры (нетолько 'тире' программисты) говорят:
если что-то работает, то не трогай это!
Это идет вразрез с позицией Мартина Фаулера, который утверждает, что если что-то "пахнет",
то надо переделать.
ИМХО - здесь он прав:
"не тронь - не воняет".
А уж если завоняло, то как-то самому неприятно становится.
Все бы хорошо, но вот код
/**
* Formats decimal number (sum) down to cents
* @param number Number that is formatted
* @return Double formated number
*/
public static double format(final double number) {
return Double.parseDouble(String.format(new Locale("en"), "%04.2f",number));
}
из числа строку из строки в число - и все одной строкой. - "Запах" не очень приятный
А я человек чувствительный к такого рода "запахам".
Проблем возникающих в случае если делать хотябы так:
public static double format(final double number) {
return Math.round(number*100)/100;
}
я не нашел.
Но код писал явно человек знающий компьютер и языки программирования - а значит...
Ну человек разумный в конце концов. "А вдруг здесь какая-то хитрость" - подумал я.
На этом мысль застопорилось. Теперь с одной стороны "запах" - с другой "возможная хитрость".
Может кто подскажет как быть в таком случае?

вторник, сентября 05, 2006

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

Русский программист


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

А в это время, в соседних четырех кубиках, будет ни на секунду не утихать работа китайских программистов, непостижимым образом умудряющихся прийти раньше русского программиста, уйти позже, и при этом сделать примерно втрое меньше. Эта четверка давно не пишет никакого кода, а только поддерживает код, написанный в свое время индусом, и дважды переписанный двумя разными русскими. В этом коде не просто живут баги. Здесь их гнездо. Это гнездо постоянно воспроизводит себя при помощи любимой китайской технологии реиспользования кода - copy/paste. Отсюда баги расползаются в разные стороны посредством статических переменных и переменных, переданных по ссылке (поскольку, китайский программист не может смириться с неудобствами вызванными тем, что он не может изменить значение внешней переменной переданной в его функцию модулями, которые переписывает русский программист). Вспоминая об этой функции русский программист, как правило, на время теряет дар английской речи, и переходит к какой-то помеси русского и китайского. Он давно мечтает переписать весь кусок, над которым работают китайцы, но у него нет времени.

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

О, канадский программист это особый тип. Он, ни на минуту не задумываясь, как рыцарь без страха и упрека, бросится фиксить самый свирепый баг китайского кода. Этот Баг живет там уже три года, и китайцы уже четырежды (каждый по разу) сообщали начальству, что он пофиксен. Но Баг каждый раз возвращался, как Бетмен в свой Готхем.

Итак, канадский программист сделает то, чего китайцы не рисковали делать в течении трех долгих лет. Он, при помощи дебагера, отследит место, где статическая переменная приняла значение -1 вместо правильного 0, и решительным движением заведет рядом вторую переменную с правильным значением. Баг погибнет в неравной схватке с канадским программистом. Но победа будет достигнута тяжелой ценой. Работать перестанет все, включая только что переписанный русским программистом код. Это повергнет русского программиста в задумчивость на целых два дня, после чего он сделает, в общем-то, предсказуемый вывод о том, что дизайн с самого начала был неправильным, и все надо переписать. На это нам нужна неделя. Да, неделя, не больше.

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

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