Исходный код
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_НАЦ' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.Percent' + R + ' AR_Percent';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_КАТ' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.ID_ArtCat' + R + ' AR_ID_ArtCat';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_ВЕС' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.Weight' + R + ' AR_Weight';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_ОБЪЕМ' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.Capacity' + R + ' AR_Capacity';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'МИННАЦ' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.MinDiscount' + R + ' AR_MinDiscount';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_МИНКОЛ' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.MinQuantity' + R + ' AR_MinQuantity';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_СТАТУС' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.Status' + R + ' AR_Status';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_ГТД' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'AR.GTD' + R + ' AR_GTD';
if (TableData = 1) or ((TableData = 2) and
(Pos(FieldDelim + 'Т_СТРАНА' + FieldDelim, OpArtFields) > 0)) then
sSelect := sSelect + ', ' + LMax + 'CO.Name' + R + ' AR_Country';
Что не так в этом коде
Здесь легко видеть адское дублирование реализаций:
- проверки наличия поля в списке требуемых полей
- формирования списка полей для выражения select
- формирования выражения для единичного поля
- связки «проверка на наличие поля» — «добавление поля»
- алгоритма выбора всех поле либо только указанных
Адское смешивание знаний о:
- правилах проверки наличия поля в списке требуемых полей
- способе формирования списка полей для выражения select
- способе формирования выражения для одиночного поля
- соответствии между кодовыми названиями полей и полями в таблицах БД
- полном списке всех доступных полей
Как можно улучшить исходный код
Список всех доступных полей
Самый правильный способ, как организовать список всех полей, которые должны участвовать в данном алгоритме — поместить его в константу в виде массива строк:
const
FieldCodes: array of string = ('Т_НАЦ', 'Т_КАТ', 'Т_ВЕС', 'Т_ОБЪЕМ', 'МИННАЦ', 'Т_МИНКОЛ', 'Т_СТАТУС', 'Т_ГТД', 'Т_СТРАНА');
Соответствия между кодовым названием поля и названием в БД
Лучше всего для прописывания соответствий между строковым названием и некими данными пользоваться ассоциативным массивом, однако в паскале, похоже, с таким типом данным проблема.
Поэтому воспользуюсь тупо функцией с ветвлением:
function FieldInfoByCode( FieldCode: string ): string
begin
if FieldCode = 'Т_НАЦ' then Result := 'AR.Percent AR_Percent';
elseif FieldCode = 'Т_КАТ' then Result := 'AR.ID_ArtCat AR_ID_ArtCat';
elseif FieldCode = 'Т_ВЕС' then Result := 'AR.Weight AR_Weight';
elseif FieldCode = 'Т_ОБЪЕМ' then Result := 'AR.Capacity AR_Capacity';
elseif FieldCode = 'МИННАЦ' then Result := 'AR.MinDiscount AR_MinDiscount';
elseif FieldCode = 'Т_МИНКОЛ' then Result := 'AR.MinQuantity AR_MinQuantity';
elseif FieldCode = 'Т_СТАТУС' then Result := 'AR.Status AR_Status';
elseif FieldCode = 'Т_ГТД' then Result := 'AR.GTD AR_GTD';
elseif FieldCode = 'Т_СТРАНА' then Result := 'CO.Name AR_Country';
else Result := '';
end
Отдельная функция — обязательно, так как она как раз изолирует от внешнего мира знания о соответствии названий.
В сожалению, из-за ограничений языка, полное название поля и алиас приходится возвращать в одной строке, которую потом нужно будет разбить по пробелу.
Формирование спецификации поля
Функция FieldInfoByCode
возвращает информацию о поле, которая является заготовкой. То, что попадёт в оператор SELECT
, ещё нужно сформировать. Знание о том, как именно происходит формирование спецификации поля, заключаем в отдельную функцию:
function FieldSpecByInfo( FieldIndo: string, LMax: string, R: string ): string
var
FieldInfoArr:array of string;
begin
FieldInfoArr := SplitString(FieldInfo, ' ');
Result := LMax + FieldInfoArr[0] + R + ' ' + FieldInfoArr[1];
end
Формирование списка полей
type
TFieldsSpec = array of string;
function FieldsExprBySpec( FieldsSpec: TFieldsSpec ): string
begin
Result := String.Join(', ', FieldsSpec);
end
Казалось бы, слишком примитивная функция, — а она заключает в себе знание о том, что спецификации полей в выражении select
разделяются запятыми.
Признак выбора всех доступных полей
Переменная TableData
имеет предельно ни о чём не говорящее название с ни о чём не говорящими значениями. Куда более понятный вариант — завести булеву переменную с названием SelectAllFields
.
Проверка необходимости включения поля
function ShouldSelectField( FieldCode: string, FieldList: string ): boolean
const
FieldDelim: string = ',';
begin
Result := (Pos(FieldDelim + FieldCode + FieldDelim, FieldList) > 0);
end
Сводим всё воедино
for FieldCode in FieldCodes do
begin
if SelectAllFields or ShouldSelectField( FieldCode, OpArtFields ) then
begin
FieldInfo := FieldInfoByCode( FieldCode );
FieldSpec := FieldSpecByInfo( FieldInfo, LMax, R );
SetLength( FieldsSpec, Length( FieldsSpec ) + 1 );
FieldsSpec[ High( FieldsSpec ) ] := FieldSpec;
end
sSelect := FieldsExprBySpec( FieldsSpec );
end
Функции FieldInfoByCode
и FieldSpecByInfo
вызываются друг за другом, от этого возникает большой соблазн объединить их в одну функцию. Желательно этого не делать, так как они делают сильно разную работу, это два разных знания о совершенно разных вещах.