Delphi MVC模 2

推介者
推介者的概念主要是处理所有视图和模型之间的交互。为了根见清楚的明白推介者的作用,我将最先描述另一个参与在MVP框架中组件;这将会 
导致这篇文章过长,所以我将在下期更加全面的讨论。

如果我们紧紧描述MVC框架,可能所有的事件操作者都写入控制器会比在GUI窗体上面好;这将让我们和所有交互在列表MVC组件的上下文中的列表视图处理所有的逻辑关系。当我们创建好这些MVC组件时,我们在窗体上面可以简单的放置一个视图组件,创建一个控制器并用这个可视组件为控制器的视图属性赋值。

这样做我们将拦截控制器的所有这些事件并把这个视图加入进去作为Subject模型的一个观察者。

     IController = interface
     ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
     function GetModel: IModel;
     function GetView: IView;
     procedure SetModel(Value: IModel);
     procedure SetView(Value: IView);
     property Model: IModel
     read GetModel
     write SetModel;
     property View: IView
     read GetView
     write SetView;
     end;
     
我将完整的IController的申明;当然实现类也要包括任何来自视图的事件操作者并将通过那些操作者调用模型,但是随着我们看到推介者和它相关联的操作者和交互者,还有接下来篇章中的选择模式和命令模式,我这里将不作更深的详细阐述。

结束语
在这篇文章中我们开始学习关于MVP的概念;高级oo设计模式。

MVP解决一个包含了模型,视图和推介者组成的组件的建立,并使之和选择模式,命令模式,操作者模式和交互者模式相联系。

我们建立了实现IListModel接口和ISubject接口的TListModel类。让我们可以不用通过多重继承混和不同的方法行为。

在建立TListModel中,我们注意到所有实现方法都应该放在类的私有部分中因为不能通过接口被访问他们。

视图的建立讨论了怎样获得一个标准可视控件并使它具有主观意识;我们也看到了怎样解决Observer的Update方法和TControl的Update方法的 
命名冲突。

最后我们简单的接触到了推介者的概念,简单描述了控制器在MVC框架中怎样扮演它的角色。

该段落就此结束,希望你将赞同MVP在建立一个好的设计和可以轻松维护的程序中确实是最有价值的扮演者。

part 2

在第一篇文章中,我们学到了关于MVP框架的概念,高级oo设计模式。

我们创建了一个列表模型类用于实现一个列表模型接口和一个Subject接口。在设计的两个视图中,讨论了怎样获得一个标准可视控件并使它具 
有主观意识;我们也看到了怎样解决Observer的Update方法和TControl的Update方法的命名冲突。

我们仅仅是接触了一点推介者的概念,简单描述了控制器在MVC框架中怎样扮演它的角色。在我们学习一些MVP架构中地其他部分之后,我们以后将更加全面地讨论推介者。

The Latest Model
我们在上章节用到的列表模型只是简单的介绍了MVP的基本概念。现在我们需要修改IListModel的版本以便在模型的所有Item中我们都可以用接口指针。

我们可以利用接口这种方式写出起连结作用的代码,尽管实现接口有许多种方式,所以我们需要用字符串做一个接口的模型,并用一个类实现它。

     IString = interface
     ['{xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx}']
     function GetAsString: string;
     procedure SetAsString(const Value: string);
     property AsString: string
     read GetAsString
     write SetAsString;
     end;
     
     TString = class(TInterfacedObject, IString, IVisited)
     private
     fString: string;
     // IString
     function GetAsString: string;
     procedure SetAsString(Value: string);
     // IVisited
     procedure Accept(Visitor: IVisitor);
     end;
     
现在我们需要改变列表模型接口使其可以和普通的IInterface类型兼容:
     IListModel = interface
     ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
     function GetCount: Integer;
     function GetItem(Idx: Integer): IInterface;
     procedure Add(const Item: IInterface);
     procedure Clear;
     procedure Insert(const Item, Before: IInterface);
     procedure Move(const Item, Before: IInterface);
     procedure Remove(const Item: IInterface);
     property Count: Integer
     read GetCount;
     property Item[Idx: Integer]: IInterface
     read GetItem;
     end;
     
现在有了一个一般的列表模型,在这个接口里面我们可以接受任何Item类型,只要它实现IInterface。你将注意到BeginUpdate和EndUpdate方法没有出现;后面会提到:

     TListModel = class(TInterfacedObject, IListModel, ISubject)
     private
     fItems: IInterfaceList;
     fSubject: ISubject;
     protected
     property Items: IInterfaceList
     read fItems;
     // ISubject
     property Subject: ISubject
     read fSubject
     implements ISubject;
     // IListModel
     procedure Add(const Item: IInterface);
     procedure Clear;
     function GetCount: Integer;
     function GetItem(Idx: Integer): IInterface;
     procedure Insert(const Item: IInterface; Before: IInterface);
     procedure Move(const Item: IInterface; Before: IInterface);
     procedure Remove(const Item: IInterface);
     end;
     
这里我们申明了一个类,实现了IListModel接口和ISubject接口。这个类的主要部分和前一个基本相同,除了我们用IInterface和TInterfaceList代替了String和TStringList。在实现上这两个版本的区别是不言而喻的:

     procedure TListModel.Add(const Item: IInterface);
     begin
     fSubject.BeginUpdate;
     if fItems = nil then
     fItems := TInterfaceList.Create;
     fItems.Add(Item);
     fSubject.EndUpdate;
     end;
     
     procedure TListModel.Clear;
     begin
     fSubject.BeginUpdate;
     fItems.Clear;
     fItems := nil;
     fSubject.EndUpdate;
     end;
     
     function TListModel.GetCount: Integer;
     begin
     if fItems <> nil then
     Result := fItems.Count
     else
     Result := 0;
     end;
     
     function TListModel.GetItem(Idx: Integer): IInterface;
     begin
     Result := fItems[Idx];
     end;
     
     procedure TListModel.Insert(const Item, Before: IInterface);
     var
     InsertIdx: Integer;
     begin
     if fItems = nil then
     fItems := TInterfaceList.Create;
     if fItems.IndexOf(Item) < 0 then
     begin
     fSubject.BeginUpdate;
     InsertIdx := fItems.IndexOf(Before);
     if InsertIdx < 0 then
     InsertIdx := 0;
     fItems.Insert(InsertIdx, Item);
     fSubject.EndUpdate;
     end;
     end;
     
     procedure TListModel.Move(const Item, Before: IInterface);
     var
     IdxItem: Integer;
     IdxBefore: Integer;
     MoveItem: IInterface;
     begin
     if fItems <> nil then
     begin
     fSubject.BeginUpdate;
     IdxItem := fItems.IndexOf(Item);
     if IdxItem >= 0 then
     begin
     MoveItem := fItems[IdxItem];
     fItems.Delete(IdxItem);
     IdxBefore := fItems.IndexOf(Before);
     if IdxBefore >= 0 then
     fItems.Insert(IdxBefore, MoveItem);
     fSubject.EndUpdate;
     end;
     end;
     end;
     
     procedure TListModel.Remove(const Item: IInterface);
     begin
     if fItems <> nil then
     begin
     fSubject.BeginUpdate;
     fItems.Remove(Item);
     fSubject.EndUpdate;
     end;
     end;
     
控制你的Subject
你可能注意到调用BeginUpdate实际上是作用着fSubject成员。利用接口的一个好处就是你可以用一个非常有力叫做聚合的技巧用另一个类委托一个接口的实现;一个类只能被写入一次然后就可以一次一次重复使用。

在MVP种,模型通常是有一个或更多的加入其中作为观察者的视图的Subjects。我们前面已经讨论过,这意味着模型发生改变时所有这些视图都将被通告并更新他们自己。

现在我们可以为观察者模式申明一个增强的接口:

     IObserver = interface
     ['{7504BB57-65D8-4D5D-86F1-EC8FFED8ED5E}']
     procedure Update(const Subject: IInterface);
     end;
     
     ISubject = interface
     ['{7A5BDDA0-C40C-40E5-BAB0-BF27E538C72A}']
     procedure Attach(const Observer: IObserver);
     procedure Detach(const Observer: IObserver);
     procedure Notify;
     
     procedure BeginUpdate;
     procedure EndUpdate;
     end;

现在你看到BeginUpdate和EndUpdate在上篇文章的IListModel中现在放到了ISubject接口中;这是因为他们被用来作为Subject更新机制的一个必须部分。

注意IObserver.Update现在用IInterface作为参数比ISubject要好;这样使其更加具有通用型。

虽然IObserver要在每个需要更新的类中重新实现,ISubject可以被一个类完全通用的实现,如果它用作一个实现ISubject和其他接口类的子对象,例如TListModel。

     TSubject = class(TInterfacedObject, ISubject)
     private
     fController: Pointer;
     fObservers: IInterfaceList;
     fUpdateCount: Integer;
     
     function GetController: IInterface;
     
     procedure Attach(const Observer: IObserver);
     procedure Detach(const Observer: IObserver);
     procedure Notify;
     
     procedure BeginUpdate;
     procedure EndUpdate;
     public
     constructor Create(const Controller: IInterface);
     end;
     
在TSubject类中有三个明显的地方:

1、fController和GetController用来使Notify方法可以传递一个包含这个Subject委托的类的实例,比传递它本身好,因为这意味观察者对象将仅仅拥有ISubject接口的属性,比需要实际的模型更好。
     constructor TSubject.Create(const Controller: IInterface);
     begin
     inherited Create;
     fController := Pointer(Controller);
     end;

如果我们注意TSubject的构造我们将看到我们强制储存一个IInterface的引用;这样避免在控制器对象上增加引用计数并让Subject在适当的时候释放。
     function TSubject.GetController: IInterface;
     begin
     Result := IInterface(fController);
     end;
     
GetController方法用于转换指针变量为IInterface类型并不增加引用计数。

2、fObservers简单的获取一个加入的Observers列表,是加入,分离和通知的重点。

     procedure TSubject.Attach(const Observer: IObserver);
     begin
     if fObservers = nil then
     fObservers := TInterfaceList.Create;
     if fObservers.IndexOf(Observer) < 0 then
     fObservers.Add(Observer);
     end;
     
     procedure TSubject.Detach(const Observer: IObserver);
     begin
     if fObservers <> nil then
     begin
     if fObservers.IndexOf(Observer) >= 0 then
     fObservers.Remove(Observer);
     if fObservers.Count = 0 then
     fObservers := nil;
     end;
     end;
     
     procedure TSubject.Notify;
     var
     i: Integer;
     begin
     if fObservers <> nil then
     for i := 0 to Pred(fObservers.Count) do
     (fObservers[i] as IObserver).Update(GetController);
     end;
原文地址:https://www.cnblogs.com/luckForever/p/7254633.html