Текущий архив: 2006.09.03;
Скачать: CL | DM;
Вниз
Ошибка проектирования.. как исправить... Найти похожие ветки
← →
Kolan © (2006-08-04 00:36) [0]Здравствуйте,
Осознал почему так мучаюсь при необходимости ввода изменений в программу :)
Вот как я пследнее время организую программы:
Задача проста - составить и отослать команду(куда не важно, в итоге комманда - массив байт). В общем виде делаю это так:
1. Есть класс непосредственно посылающий массив.
2. Есть класс который "знает" протокол. Он составляет массив байт по параметрам.
3. Есть менеджер, который "содержит" в себе экземпляры классов (1) и (2). Его задача такая получая неполный набор параметров, он подставляет недостающие(например номер комманды) использую класс (2) получает массив и отсылает его с пом класса (1).
4. И есть еще главный менеджер который запрявляет другими объектами в том числе и экземпляром (3). У него есть теже ф-ции, что и в (3), но параметров надо еще меньше.
Какие тут плюсы:
Кокда я описал функции ими оч. легко управлять. Например, чтобы послать
запрос "Включить устройство №1" надо написать:FHeadManager.Switch(1);
Далее будет вызвана соотв. вункция экземпляра класса (3), он "Знает" что номер этой коммады "1". Далее он все это подставит в экземпляр класса (2) и отправит.
Думаю что в плане пользования - оч. удобно.
Если бы не надо было менять существующие и добавлять новые ф-ции :).
Так при добавлении новай ф-ции мне надо добавить соотв ф-цию в класс (2). Затем в класс (3) и наконец в (4). Причем не забыть ничего короче .....
Как можно исправить так, чтобы для добавления новой ф-ции её нужно было бы добавлять только в одном месте? Но при этом На верхних уровнях(класс (4)) вызовы ф-ций остались бы такими же протыми.
Как ие использовать паттерны? Что еще?
← →
jack128 © (2006-08-04 00:58) [1]ничего не понял.
почему бы так не сделать:
type
// 1)
TCommandSender = class
public
procedure SendCommand(Data: string);
end;
// 2 + 3)
TDevice = class
public
property CommandSender: TCommandSender;
procedure Switch;
end;
procedure TDevice.Switch;
begin
CommandSender.SendCommand(#1);
end;
4) - я так понял, просто Owner для 1) и 2+3)
← →
Германн © (2006-08-04 01:06) [2]Да. Тут действительно ошибка в проектировании! Имхо.
Не понял. 3 и 4 те, которые менеджеры, тоже классы?
Я бы в классе 2 заранее бы продумал функцию "преобразования данных в соответствии с текущим протоколом", которая бы принимала вышеуказанные данные по-максимуму в произвольном виде. И более того. Эту функцию я бы поместил в DLL.
Это если я правильно интерпретировал сабж.
← →
Kolan © (2006-08-04 01:12) [3]
> почему бы так не сделать:
Да коммады сложные ... :Сообщение канального уровня содержит следующие поля:
1. адрес назначения (1 байт);
2. адрес отправителя (1 байт);
3. порядковый номер сообщения (2 байта);
4. длина поля пользователя (2 байта);
5. контрольная сумма заголовка (1 байт);
6. поле пользователя.
Сообщение пользователя инкапсулируется в поле пользователя сообщения канального уровня. Сообщение пользователя имеет следующие поля:
1. тип пакета (1 байт); в данной версии всегда равен 1
2. команда (1 байт);
3. обязательные параметры команды (длина зависит от команды);
4. не обязательные параметры (длина зависит от обязательных параметров);
5. контрольная сумма (2 байта);
> ничего не понял.
Так я изясняюсь... :) Вoт пример, теперь пойду от верхнего к нижниму(Это простейшая запись коэффициента):
Пользовательнажимает на кнопку:procedure TMainForm.BitBtn1Click(Sender: TObject);
var
Value1: Double;
begin
FHeadManager.WriteAmplifierOutputKoeff(1, Value1);
end;
FHeadManager = класс 4.procedure THeadManager.WriteAmplifierOutputKoeff(Address: Byte; Koeff: Double);
begin
FDeviceManager.WriteAmplifierOutputKoeff(Address, Round(Koeff * $7fffffff));
end;
FDeviceManager = класс 3.procedure TDeviceManager.WriteAmplifierOutputKoeff(Address: Byte;
Koeff: Cardinal);
var
Package: TByteArray;
StaffedPackage: TByteArray;
begin
{Если порт открыт.}
if FComPort.IsPortOpened then
begin
{Составляем пакет.}
FPackageComposer.ComposeAmplifierOutputKoeffWriteRequest(Address, Koeff,
Package, FSendCommandsCount);
{Добавляем стаффинг.}
FSendStaffer.StaffByteArray(Package, StaffedPackage);
{Пишем это дело в порт.}
FComPort.Write(StaffedPackage, Length(StaffedPackage));
{Увеличиваем счетчик посланных комманд.}
FSendCommandsCount := FSendCommandsCount + 1;
{Последня команда - запрос FFT.}
FLastCommand := ctAmplifierOutputKoeffWriteRequest;
{Пишем в протокол.}
ProtocolManager.WriteString("Запись коэффициента: " +IntToStr(Koeff));
end;
end;
FPackageComposer = Класс 2.procedure TPackageComposer.ComposeAmplifierOutputKoeffWriteRequest(
Address: Byte; AmplifierKoeff: Cardinal; var Answer: TByteArray;
PackageCount: Word);
var
UserData: TByteArray;
B: array[0..3] of Byte;
TempValue: Cardinal absolute B;
begin
TempValue := AmplifierKoeff;
{Составляем поле пользоватея.}
MakeUserData(ptUsual, ctAmplifierOutputKoeffWriteRequest, [Address, B[0], B[1],
B[2], B[3]], [], UserData);
{Добовляем заголовок.}
AddHeader(Address, UserData, Answer, PackageCount);
end;
Ну и шлем.
Вот такое для каждой комманды :(
← →
Kolan © (2006-08-04 01:21) [4]
> "преобразования данных в соответствии с текущим протоколом"
Те 1 ф-ция, которая все подготавливает, для любой комманды? Как же её написать...
← →
jack128 © (2006-08-04 01:47) [5]Kolan © (04.08.06 1:12) [3]
Да коммады сложные ... :
и что здесь сложного???
ну если тя так напрягает, можно конечно написать мелкий классик
var
CommandBuilder: TCommandBuilder;
begin
CommandBuilder := TCommandBuilder.Create;
try
CommandBuilder.Dest := 12;
CommandBuilder.Source := 5;
CommandBuilder.UserData := "Данные пользователя";
CommandSender.SendCommand(CommandBuilder.BuildCommand())
finally
CommandSender.Free;
end;
end;
но извне им не кто пользоваться не будет. Или у тя одно и тоже устройство умеет по размым протоколам общаться? ;-)
← →
SPeller © (2006-08-04 02:35) [6]Начни повторяющиеся куски кода отделять в процедуры или методы, и вызывать их из разных мест. Например, если в твоем случае у каждой команды свой собственный формат, то можно сделать так:
procedure TMainForm.BitBtn1Click(Sender: TObject);
var
Value1: Double;
begin
FDeviceManager.SendCommand(1, Round(Value1 * $7fffffff), BuildAmplifierOutputKoeffPacketProc);
end;
procedure TDeviceManager.SendCommand(Address: Byte;
Koeff: Cardinal; BuildProc: TMyProcedure);
var
Package: TByteArray;
StaffedPackage: TByteArray;
begin
{Если порт открыт.}
if FComPort.IsPortOpened then
begin
{Составляем пакет.}
if (@MyProc <> nil)
then
MyProc(StaffedPackage, LogMessage, ...); // Процедура сбора конкретного пакета
else
// Какая-то стандартная процедура сборки пакета
FComPort.Write(StaffedPackage, Length(StaffedPackage));
{Увеличиваем счетчик посланных комманд.}
FSendCommandsCount := FSendCommandsCount + 1;
{Последня команда - запрос FFT.}
FLastCommand := ctAmplifierOutputKoeffWriteRequest;
{Пишем в протокол.}
ProtocolManager.WriteString(LogMessage);
end;
end;
procedure BuildAmplifierOutputKoeffPacketProc(...)
begin
// сборка пакета и прочие специфические операторы
end;
← →
SPeller © (2006-08-04 02:37) [7]Итого для каждой команды надо будет написать одну процедуру сборки пакета и вставить ее в новый вызов FDeviceManager.SendCommand()
← →
jack128 © (2006-08-04 03:11) [8]SPeller © (04.08.06 2:37) [7]
все это будет работать, только если BuildAmplifierOutputKoeffPacketProc , BuildSameOtherCommandProc и все остальные будут иметь одинаковую сигнатуру. А это - маловероятно.
← →
SPeller © (2006-08-04 03:16) [9]Я думаю, что можно стандартизировать параметры функции. В крайнем случае - можно завести класс для хранения произвольного числа параметров.
← →
07BB (2006-08-04 08:11) [10]Kolan © (04.08.06 00:36)
А попробывать реализовать что нибудь на теории конечных автоматов?
Данная теория может использоваться не только для оиска совпадений в тексте но и для реализации подобных задач!
← →
atruhin © (2006-08-04 10:04) [11]Например:
Любое устройство поддерживает определенное кол-во комманд. В классе протокола(2) реализуем процедуру:
Procedure SendCommand(Command : integer; Fields : array of string; Values : array of variant);
Она принимает номер комманды и задываемые поля. Остальные заполняет по дефолту.
Класс №3 выкидываем. В классе №4 (обертке) вызываем:
procedure THeadManager.WriteAmplifierOutputKoeff(Address: Byte; Koeff: Double);
begin
FDeviceManager.SendCommand(cmAmplifier, ["address","koef"],[Address, Round(Koeff * $7fffffff)]);
end;
И все.
← →
Kolan © (2006-08-04 10:50) [12]
> все это будет работать, только если BuildAmplifierOutputKoeffPacketProc
> , BuildSameOtherCommandProc и все остальные будут иметь
> одинаковую сигнатуру. А это - маловероятно.
Разная конечно...
> 07BB (04.08.06 08:11) [10]
Немного я конечно знаю про автоматы, но как они здесь могут помочь
... пример можно?
> Любое устройство поддерживает определенное кол-во комманд.
Честно нео чень понял как надо сделать, и как это поможет..
Это не так. Завтра прийдут и скажут что мы тут подумали и ввели еще 2 комманды....
> Или у тя одно и тоже устройство умеет по размым протоколам
> общаться? ;-)
Нет не умеет.
← →
Slym © (2006-08-04 11:48) [13]
type
TCommandProc=function(const Args: array of const;out Command:pointer):integer;
var
FCommandList:TStrings=nil;
procedure RegisterCommand(const Cmd:string;CommandProc:TCommandProc);
begin
if not assigned(FCommandList) then
FCommandList:=TStringList.Create;
FCommandList.AddObject(Cmd,@CommandProc);
end;
function MakeCommand(const Cmd:string;const Args: array of const;out Command:pointer):integer;
var i:integer;
begin
i:=FCommandList.IndexOf(Cmd);
if i<0 then raise Exception.Create("Unsupported command");
result:=TCommandProc(FCommandList.Objects[i])(Args,Command);
end;
function WriteAmplifierOutputKoeff(const Args: array of const;out Command:pointer):integer;
var
UserData: TByteArray;
Long:LongRec;
AmplifierKoeff:TVarRec;
begin
Application.MessageBox(PChar(IntToStr(Args[0].vinteger)),"");
Long:=LongRec(Args[0].vinteger);
result:=4;
GetMem(Command,result);//скока надо столько выделяем;
try
PByteArray(Command)[0]:=10;//заполняем
except
FreeMem(Command);
result:=0;
raise;
end;
end;
procedure SendCommand(Address: Byte;const Cmd:string;const Args: array of const);
var
command,packet:pointer;
CmdSize:integer;
cmdcrc:word;
begin
CmdSize:=MakeCommand("WriteAmplifierOutputKoeff",[1],command);
try
{cmdcrc:=MakeCRC(command,CmdSize);
packet:=MakePacket(Address);
MakeCRC(packet,packetsize);
Send(packet,packetsize);}
finally
FreeMem(command);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
SendCommand(10,"WriteAmplifierOutputKoeff",[1]);
end;
initialization
RegisterCommand("WriteAmplifierOutputKoeff",WriteAmplifierOutputKoeff);
finalization
if assigned(FCommandList) then
FreeAndNil(FCommandList);
← →
Slym © (2006-08-04 11:50) [14]Функции добавлять проще некуда: RegisterCommand("Name",Name);
← →
Slym © (2006-08-04 12:06) [15]можно plugавую схему забацать:
TProc=function(Buf:pointer;BufSize:integer;Args:array of pointers):integer;stdcall;
cmd:="WriteAmplifierOutputKoeff.dll";
arg:integer=100;
LoadLib(cmd) else raise NotSupportedException;
proc:=GetProcAddress(hlib,"Proc");
TProc(Proc)(buf,size,[@arg,@arg2,@strarg]);
FreeLib;
← →
Slym © (2006-08-04 12:09) [16]основное направление мысли - открытый массив параметров (array of)
← →
Kolan © (2006-08-04 15:35) [17]
> Slym © (04.08.06 12:09) [16]
Буду разбираться, пока не понятно ничего... :)
← →
GrayFace © (2006-08-04 16:06) [18]Германн © (04.08.06 1:06) [2]
Я бы в классе 2 заранее бы продумал функцию "преобразования данных в соответствии с текущим протоколом", которая бы принимала вышеуказанные данные по-максимуму в произвольном виде. И более того. Эту функцию я бы поместил в DLL.
В сабже вроде не было ни слова про dll. А наворачивать dll на пустом месте - глупость абсолютная.
Kolan © (04.08.06 1:12) [3]
Вот такое для каждой комманды :(
Если для всех команд такая же кухня и параметры сходные, то просто сделай общие процедуры, которые будут принимать в качестве параметра конечную процедуру (как procedure of object). Если параметры у функций разные, но есть несколько общих для всех параметров, то специфические можно засунуть в record, в промежуточной(ых) ф-ции сделать var Params без типа, в конечный var Params:TТакой-тоRecord.
07BB (04.08.06 8:11) [10]
А попробывать реализовать что нибудь на теории конечных автоматов?
Данная теория может использоваться не только для оиска совпадений в тексте но и для реализации подобных задач!
Слабо представляю ее использование для поиска совпадений и тем более для данной задачи.
← →
Slym © (2006-08-07 09:15) [19]Kolan © (04.08.06 15:35) [17]
Буду разбираться, пока не понятно ничего... :)
Что не понятно?
1. Список имен функций с указателями на них
2. Все функции одного типа с открытым массивом параметров.
3. Расширение функционала реализуется добавлением имени функции и ее указателя в список.
Единственное неудобство, контроля типов параметров нет, функция сама должна все проверить.
← →
GrayFace © (2006-08-08 11:35) [20]С record-ами контроль типов параметров будет и накладных расходов меньше.
Страницы: 1 вся ветка
Текущий архив: 2006.09.03;
Скачать: CL | DM;
Память: 0.52 MB
Время: 0.032 c