Автор: xpin2013
Дата сообщения: 18.02.2015 22:24
У меня вопрос, суть которого проще понять после моего, возможно корявого пояснения (на случай поспорить - я пас, пояснение оно только для вопроса).
Простите новичка, недавно столкнулся с необходимостью узнать, что такое MVC (Model View Controller), в руках исходники с MVC. Я под впечатлениями, как от исходников, так и от того что в интернете бросилось в глаза.
1) Модель - обёртка данных. Смысл её существования в обеспечении отдельного развития модели от конечных реализаций. Неоднократно делаются замечания, что хороший тон, это когда бизнес-логика закладывается в модели. Ну это понять просто, например, - бизнес-логика в триггерах, а никак не в событиях датасета. Зачем же Модель, то есть обёртки? Использование модели уже отменяет технологию DB->DataSet->DBControl и обязывает продублировать работу с данными - данные изменяются и в модели и в файле DB. Оказывается Controller умеет строить модель независимо, - либо он читает данные из датасета/базы и заполняет коллекцию/список, либо строит её не связанно с базами, - на момент существования процесса или задачи. Остальной код не знает как заполнять обёртки и использует для этого контроллер. (сомневаюсь, что ради этого можно согласиться на дублирование данных, когда нам не хочется использовать ClientDataSet мы его не используем).
2) View - представление. Тут понятно, если мы добавили поле в модель, мы его добавляем в гридину, всё так же как обычно за исключением - это не DBControl, нет датасетов/датасурсов. Мы из обёрток вытаскиваем строки и передаём их в грид. Если не ошибаюсь в сишарпе это метод Fill тоже какого-то контроллера, но вызывает его именно представление. Почему View мы связываем с моделью, а не с датасетом? Чем можно заменить технологию событий протекающих через DataSource? Ну есть пассивный MVC - обёртки есть, событий их изменений нет. Есть активный, в классическом варианте MVC - это активный MVC. В этом случае View подписывается на события модели, у меня десяток View - 5 подписалось на события одного справочника, а 5 подписалось на события другого. Там в сишарпе событие это не один обработчик OnChanged, а сколько угодно все происходят пачкой последовательно. В шарпе это реализовано на уровне языка и понятно, почему можно согласиться на MVC, для чего пассивная модель может быть использована в Delphi совершенно теряюсь, для активной модели приведу свой суррогат/заменитель
[more]
Код: [no]type
TEventList = class(TComponent)
private
FList: array of TMethod;
FLock: TRTLCriticalSection;
FUnpackList: array of TMethod;
procedure Delete(Index: Integer);
function IndexOf(const AEvent: TMethod): Integer;
protected
function AddEvent(const AEvent: TMethod): Integer;
function Count: Integer;
function Event(Index: Integer): TMethod;
function IsEmpty(Index: Integer): Boolean;
procedure LockList;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure RemoveEvent(const AEvent: TMethod);
procedure RemoveEventOf(AComponent: TObject);
procedure UnlockList;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TNotifyEventList = class(TEventList)
public
procedure Add(AEvent: TNotifyEvent);
procedure Execute(Sender: TObject);
procedure Remove(AEvent: TNotifyEvent);
end;
{ TEventList }
function TEventList.AddEvent(const AEvent: TMethod): Integer;
var
L: Integer;
begin
if (AEvent.Code = nil) and (AEvent.Data = nil) then
raise EListError.Create('Invalid method value');
EnterCriticalSection(FLock);
try
Result := IndexOf(AEvent);
if Result < 0 then
begin
L := Length(FList);
SetLength(FList, Succ(L));
FList[L] := AEvent;
if TObject(AEvent.Data) is TComponent then
TComponent(AEvent.Data).FreeNotification(Self);
end;
finally
LeaveCriticalSection(FLock);
end;
end;
function TEventList.Count: Integer;
begin
Result := Length(FUnpackList);
end;
constructor TEventList.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
InitializeCriticalSection(FLock);
end;
procedure TEventList.Delete(Index: Integer);
var
I: Integer;
M: TMethod;
begin
I := Length(FList);
if (Index < 0) and (Index >= I) then
raise EListError.CreateFmt('List index out of bounds (%d)', [Index]);
Dec(I);
M := FList[Index];
if Index < I then
System.Move(FList[Index + 1], FList[Index],
(I - Index) * SizeOf(TMethod));
SetLength(FList, I);
for I := 0 to High(FUnpackList) do
if (FUnpackList[I].Code = M.Code) and (FUnpackList[I].Data = M.Data) then
begin
FUnpackList[I].Code := nil;
FUnpackList[I].Data := nil;
Exit;
end;
end;
destructor TEventList.Destroy;
begin
SetLength(FList, 0);
DeleteCriticalSection(FLock);
inherited Destroy;
end;
function TEventList.Event(Index: Integer): TMethod;
begin
if (Index < 0) or (Index >= Length(FUnpackList)) then
FillChar(Result, Sizeof(Result), 0)
else
Result := FUnpackList[Index];
end;
function TEventList.IndexOf(const AEvent: TMethod): Integer;
var
I: Integer;
begin
Result := -1;
for I := Pred(Length(FList)) downto 0 do
if (FList[I].Code = AEvent.Code) and (FList[I].Data = AEvent.Data) then
begin
Result := I;
Exit;
end;
end;
function TEventList.IsEmpty(Index: Integer): Boolean;
begin
if (Index < 0) and (Index >= Length(FUnpackList)) then
raise EListError.CreateFmt('List index out of bounds (%d)', [Index]);
Result := (FUnpackList[Index].Code = nil) and
(FUnpackList[Index].Data = nil);
end;
procedure TEventList.LockList;
var
L: Integer;
begin
EnterCriticalSection(FLock);
L := Length(FList);
SetLength(FUnpackList, L);
System.Move(FList[0], FUnpackList[0], L * SizeOf(TMethod));
end;
procedure TEventList.Notification(AComponent: TComponent;
Operation: TOperation);
begin
if Operation = opRemove then
RemoveEventOf(AComponent);
inherited Notification(AComponent, Operation);
end;
procedure TEventList.RemoveEvent(const AEvent: TMethod);
var
I: Integer;
begin
EnterCriticalSection(FLock);
try
for I := Pred(Length(FList)) downto 0 do
if (FList[I].Code = AEvent.Code ) and (FList[I].Data = AEvent.Data) then
begin
Delete(I);
Break;
end;
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TEventList.RemoveEventOf(AComponent: TObject);
var
I: Integer;
begin
EnterCriticalSection(FLock);
try
for I := Pred(Length(FList)) downto 0 do
if FList[I].Data = AComponent then
Delete(I);
finally
LeaveCriticalSection(FLock);
end;
end;
procedure TEventList.UnlockList;
begin
SetLength(FUnpackList, 0);
LeaveCriticalSection(FLock);
end;
{ TNotifyEventList }
procedure TNotifyEventList.Add(AEvent: TNotifyEvent);
begin
AddEvent(TMethod(AEvent));
end;
procedure TNotifyEventList.Execute(Sender: TObject);
var
I: Integer;
begin
LockList;
try
for I := 0 to Count - 1 do if not IsEmpty(I) then
TNotifyEvent(Event(I))(Sender);
finally
UnlockList;
end;
end;
procedure TNotifyEventList.Remove(AEvent: TNotifyEvent);
begin
RemoveEvent(TMethod(AEvent));
end;[/no]