вторник, 19 мая 2009 г.

Мысли о роли ТЗ в программном проекте

Так, когда-то написал, отходя от проекта, в котором вовлеченности непосредственного заказчика по сути не было

Основная задача ТЗ в программном проекте - не фиксация объема работ, не защита от переработки, не инструмент "заморозки" требований. Нет, это просто возможность заказчику(пусть зачастую и при помощи разработчиков) подумать над тем, что он вообще хочет.

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

Другими словами ТЗ - это "рыба" проекта, которую можно делать сразу, просто и очень дешево менять. А письменный он должен быть потому, что иначе мозг может и не включиться. П%;:ть, как грится, не мешки ворочать.


PS термин "техническое задание" спорный, но выбран как самый простой, для обобщения

PPS да, agile конечно штука, но не каждый заказчик готов по нему работать

UPD: мысль оказалась далеко не новой - Джоэль о том же пишет.

четверг, 19 марта 2009 г.

Указатель на метод интерфейса

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

Но реальность оказалась не столь оптимистичной - на текущий момент Delphi такого функционала не поддерживает:
  • http://groups.google.com.ua/group/borland.public.delphi.objectpascal/browse_thread/thread/4c5dcb094b82522f?pli=1
  • http://qc.embarcadero.com/wc/qcmain.aspx?d=941 (а судя по датам и не будет поддерживать)
Самому раньше в таком функционале необходимости не было - проще передавать весь интерфейс, но если необходимо взаимодействие с чужим кодом, где могут требовать именно указатель на функцию, то прийдется изобретать не очень стройные решения:
  • метод в интерфейсе напрямую возвращающий адрес метода объекта
IAnIntf = interface
function GetPointerToProc: TAnObjectProc:
end
  • объект-обертку над интерфейсом
TWrapObj = class
private
AnIntf: ITheIntf;
protected
procedure AnProc(const Value: string);
end;

procedure TWrapObj.AnProc(const Value: string);
begin
AnIntf.AnProc(Value);
end;
  • возможно как-то можно будет выкрутиться через анонимные функции? нельзя, ибо
    TAnProc = procedure (S: string) of object;
    TAnProc2 = reference to procedure (S: string);
    это разные типы
Хотя и в реализации проблем более чем - вычислять статический адрес, на этапе компиляции, для такого указателя не получится, т.к. один и тот же интерфейс может реализовываться разными объектами.

> Still I think making (procedure of object) and (procedure of Interface) equivalent
> typeswould be the better solution.

Like I said, the semantics for the compiler would be quite different, so
it would make sense to make them different types (even if both
represented internally by a TMethod). Actually, if the interface
instance variable is nil, it is impossible to get the method address.
This is not a problem for objects, since there the static address is
taken.

пятница, 6 февраля 2009 г.

Создание индексов "на ходу" в Firebird

>> А вот с индексами как? Как насчет CREATE INDEX "на ходу"? Да еще и на
>> таблицу, с которой постоянная работа на UPDATE идет?
>
> Если 2.0 и старее, то получишь рассинхронизированный с таблицей индекс.
> Если 2.1 и новее, то все апдейты будут курить, пока индекс не будет создан.

- DDL тр-ция должна быть wait. Иначе - создание индекса обломится с большой вероятностью.
- Отпрепарированные запросы не увидят новый индекс. Это и процедур, и триггеров касается.

> и создание индексов, и ALITER INDEX INACTIVE делал на ходу...
> и что требуется теперь сделать, что бы за БД не переживать?

gfix -v -f

(с) Dmitry Yemanov, Хорсун Влад

пятница, 9 января 2009 г.

Borland, умолчания и поддержка старого

Компания меняет не только названия, но и простым разработчикам не дает скучать.
Вводная - при неизменных внешних условиях перестала выполняться сборка релиза из командной строки при переходе с 2007 на 2009 - упорно попадал туда код дебага. В то время как из среды - все ок.

Проведенное в два этапа(первый был еще на 2007 и текущей проблемы не касался см.) расследование показало, что:

- в импортируемом проектами файле CodeGear.Delphi.Targets дефолтный таргет изменен с Build на Make. А последний при смене конфигурации Debug|Release не пересобирает dcu

- группы проектов, в отличии от проектов, не обновляются до соотв. версии и импортируют $(MSBuildBinPath)\Borland.Group.Targets, который существует только при инсталяции 2007(или когда там msbuild прикрутили?)

- таргет Build для группы проектов не задает таргет для конечных проектов(т.е. используется умолчательный, который теперь Make, да)

Выходом может быть использование таргета BuildAll для группы проектов.

Delphi 2009 и приведение к PAnsiString

Часть winapi функций требуют передачи себе в кач-ве параметров структур с PAnsiChar строками (например mapi). В предыдущих версиях приведение строк посредством PChar(SomeString) работало, используя область памяти выделенную под SomeString, а в 2009 результат PAnsiString(AnsiString(SomeString)), уже как вычисление обычной строки из юникодной, размещается в локальной области памяти, что чревато тем, что старый код может перестать работать на, казалось бы, ровном месте (хотя конечно прямое приведение к pchar тоже имеет кучу особенностей).

Например код приведенный ниже возвращает разные результаты на 2007 и 2009 Делфи

program array_error_test;

{$APPTYPE CONSOLE}

uses
SysUtils,
StrUtils,
Classes;

procedure DoMain;
const
Count = 5;

type
RecType = record
AnString: PAnsiChar;
end;
var
RecArr: array of RecType;
I: Integer;
SomeStrings: TStringList;
begin

SetLength(RecArr, Count);

SomeStrings := TStringList.Create;
try
for I := 0 to Length(RecArr) - 1 do
begin
SomeStrings.Add(DupeString(IntToStr(I), 10));
RecArr[I].AnString := PAnsiChar(AnsiString(SomeStrings[I]));
end;

for I := 0 to Length(RecArr) - 1 do
WriteLn(RecArr[I].AnString);
finally
SomeStrings.Free;
end;
end;

begin
DoMain;
ReadLn;
end.

четверг, 8 января 2009 г.

Delphi 2009 и InternetQueryOption

Переносится в одном из проектов код на 2009 Делфи и соотвественно кушаются все прелести первопроходчества.

Для получения адреса прокси сервера пользовалась функция InternetQueryOption. После сборки в 2009 стала она возвращать мусор. Ничего криминального замечено не было, замена на InternetQueryOptionW не помогала.

Решение нашлось в
http://akirabbq.spaces.live.com/blog/cns!CEB8A04DC43BCEE9!264.entry
кто виноват не совсем понятно, но неприятно :(

По сути - в структуре INTERNET_PROXY_INFO lpszProxy должно быть не LPCTSTR(которое в 2009 уже PWideChar), а просто LPCSTR, хотя в исходных сишных заголовках она объявлена как LPCTSTR.

Решить проблему можно создав необходимую структуру у себя.

пятница, 12 сентября 2008 г.

Оптимизатор в Firebird 2.1

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

Selectivity Maintenance per Segment
D. Yemanov, A. Brinkman
Index selectivities are now stored on a per-segment basis. This means that, for a compound index on columns
(A, B, C), three selectivity values will be calculated, reflecting a full index match as well as all partial matches.
That is to say, the selectivity of the multi-segment index involves those of segment A alone (as it would be if it
were a single-segment index), segments A and B combined (as it would be if it were a double-segment index)
and the full three-segment match (A, B, C), i.e., all the ways a compound index can be used.
This opens more opportunities to the optimizer for clever access path decisions in cases involving partial index
matches.
The per-segment selectivity values are stored in the column RDB$STATISTICS of table RDB
$INDEX_SEGMENTS. The column of the same name in RDB$INDICES is kept for compatibility and still
represents the total index selectivity, that is used for a full index match.


и если была таблица documents(...doc_state int, doc_date ... ) c индексом по (doc_state, doc_date), то теперь, при условиях по doc_state, будет браться селективность не всего композита(а она могла из-за даты быть достаточно неплохой), а только поля doc_state.