原帖地址:http://blog.sina.com.cn/s/blog_5591c079010005pq.html
;并非线程安全的缘故, 必须用 Sync
线程程序的时候, 由于 VCL
, 越来越了解封装的好处. 可是一到写多
这个类并不复杂, 主要实现了对目录的递归搜索, 其中几个属性也很简单, 通过名字就能知道它们的作用了, 这里我就不赘述了. TFileSearchEngine 可以适用于多线程和单线程模式下, 所以现在我们就来考虑如何在多线程模式下将查找到的文件实时发送给界面(也就是主题订阅者)的实现.
首先是要定义好观察者的接口, 这里我采用了 Interface 而不是 Abstract Class 的方式. 因为 Interface 更加灵活, 能让任何控件和类支持 (只要实现该接口即可), 而不像 Abstract Class 那样固定了继承树.
1IObserverWatcher = interface (IInterface)
2[ ' {8ED26F9D-9377-4829-B305-3A825ECC231B} ' ]
3function Update(Dispatch: TCustomSubjectDispatcher): Boolean;
4end ;
这个接口很简单, 就是有个 Update 方法, 用来将 Subject 信息分发下去. 而它唯一的参数 (Dispatch) 就是拥有更新信息的 Subject 订阅类.
1TCustomSubjectDispatcher = class (TObject)
2private
3FObserverList: TSafedObjectList;
4FMultipleDispatch: Boolean;
5procedure SetMultipleDispatch( const Value: Boolean);
6protected
7function ObserverSupportDispatch(AObserver: TComponent): Boolean; virtual ;
8function DoObserverUpdate(AObserver: TComponent): Boolean; virtual ;
9public
10constructor Create(AMultipleDispatch: Boolean);
11destructor Destroy; override ;
12procedure NotifyObservers;
13function Attach(AObserver: TComponent): Integer;
14procedure Detach(AObserver: TComponent);
15property MultipleDispatch: Boolean read FMultipleDispatch write SetMultipleDispatch;
16end ;
TCustomSubjectDispatcher 实现了添加 (Attach) 和删除 (Detach) 观察者的功能, 所有的观察者都保存在 FObserverList 中, 在需要派发消息的时候, 通过 NotifyObservers 方法, 从 FObserverList 中取出每个对象, 然后调用它们的 IObserverWatcher.Update 接口, 来将信息通知给关心此主题的 Observer.
1procedure TCustomSubjectDispatcher.NotifyObservers;
2var
3I: Integer;
4begin
5FObserverList.Enter;
6try
7for I : = FObserverList.Count - 1 downto 0 do
8try
9if (DoObserverUpdate((FObserverList[I] as TComponent))) and
10( not FMultipleDispatch)
11then Break;
12except
13Continue;
14end ;
15finally
16FObserverList.Leave;
17end ;
18end ;
1function TCustomSubjectDispatcher.DoObserverUpdate(AObserver: TComponent): Boolean;
2begin
3Result : = (AObserver as IObserverWatcher).Update(Self);
4end ;
这两个方法也很简单, NotifyObservers 首先进入 FObserverList, 然后把每个对象取出来, 传给 DoObserverUpdate 方法来执行 IObserverWatcher.Update 接口. 这样做的好处是, 子类可以覆盖 DoObserverUpdate, 用别的观察者接口来调用 Observer, 使得这个架构更加灵活. 线程程序的时候, 由于 VCL
接下来, 根据我们所做的 FileSearch 功能, 从 TCustomSubjectDispatcher 继承一个 TFileFoundSubjectDispatcher 类, 用来保存 TFileSearchEngine 搜索到的文件信息. 由于祖先类 TCustomSubjectDispatcher 已经很完善了, 所以这个 TFileFoundSubjectDispatc her 并不需要什么具体实现, 只要保存下需要传递的信息即可. 下面就是该类的声明:
1type
2TSearchFoundInfo = record
3Directory: string ;
4Name: string ;
5Name_ 8 _ 3 : string ;
6FullPathName: string ;
7Size: Int64;
8Attributes: Integer;
9CreationTime,
10LastAccessTime,
11LastWriteTime: TDateTime;
12{ $IFDEF VCL10ORABOVE }
13class operator Implicit(ASearchRec:TSearchRec): TSearchFoundInfo;
14class operator Explicit(ASearchRec: TSearchRec): TSearchFoundInfo;
15{ $ENDIF }
16end ;
17
18TFileFoundSubjectStatus = (fdsBeforeSearch, fdsSearching, fdsSearchDone, fdsSearchAborted);
19
20TFileFoundSubjectDispatcher = class (TCustomSubjectDispatcher)
21private
22FSearchFoundInfo: TSearchFoundInfo;
23FStatus: TFileFoundSubjectStatus;
24public
25property SearchFoundInfo: TSearchFoundInfo read FSearchFoundInfo write FSearchFoundInfo;
26property Stauts: TFileFoundSubjectStatus read FStatus write FStatus;
27end ;
当线程执行搜索的时候, 将通过 TFileFoundSubjectDispatcher 来将界面关心的搜索到的文件信息通知到界面上. 好了, 一切准备就绪, 接下来就请您跟我一起去实现具体的线程类吧
------------------------------------------------------------------------------------------------------
先来看看这个模式的UML图:
上次介绍完了文件搜索和观察者模式的相关代码, 现在轮到线程类粉末登场了.
1type
2TThreadFileSearch = class (TThread)
3private
4FFileSearch: TFileSearchEngine;
5FIncludeSubDir: Boolean;
6FSearchRootDir: string ;
7FUpdateSubjectDispatcher: TFileFoundSubjectDispatcher;
8procedure SetIncludeSubDir( const Value: Boolean);
9procedure SetSearchRootDir( const Value: string );
10procedure OnSearch(Sender: TObject; const BaseDir: string ;
11const FoundRec: TSearchRec);
12procedure SetUpdateSubjectDispatcher( const Value: TFileFoundSubjectDispatcher);
13procedure BeforeSearch;
14procedure SearchDone;
15procedure SearchAborted;
16procedure DispatchNotification(NotifyStates: TFileFoundSubjectStatus);
17public
18constructor Create(CreateSuspended: Boolean; ASubjectDispatcher: TFileFoundSubjectDispatcher;
19const ASearchRoot: string ; AIncludeSubDir: Boolean); reintroduce ; overload ; virtual ;
20destructor Destroy; override ;
21procedure Execute; override ;
22property SearchRootDir: string read FSearchRootDir write SetSearchRootDir;
23property IncludeSubDir: Boolean read FIncludeSubDir write SetIncludeSubDir;
24property UpdateSubjectDispatcher: TFileFoundSubjectDispatcher
25read FUpdateSubjectDispatcher write SetUpdateSubjectDispatcher;
26end ;
线程通过构造函数 Create 传递一个 TFileFoundSubjectDispatcher 类进来, 并在 FUpdateSubjectDispatcher变量中保存.
给 FFileSearch.OnSearch 事件指定一个处理过程, 然后在 OnSearch 中, 把搜索到的文件信息填充到 FUpdateSubjectDispatcher
中的 SearchFoundInfo 和 Stauts 中, 然后使用Synchronize函数调用 FUpdateSubjectDispatcher.NotifyObservers 方法,
将结果和状态通知出去, 从而完成整个消息的派发.
1procedure TThreadFileSearch.DispatchNotification(
2NotifyStates: TFileFoundSubjectStatus);
3var
4DoDispatch: TFileFoundSubjectDispatcher;
5begin
6DoDispatch : = FUpdateSubjectDispatcher;
7if DoDispatch <> nil then
8begin
9DoDispatch.Stauts : = NotifyStates;
10Synchronize(DoDispatch.NotifyObservers);
11end ;
12end ;
13
14procedure TThreadFileSearch.Execute;
15begin
16BeforeSearch;
17try
18FFileSearch.Search;
19finally
20if not FFileSearch.Aborted then
21SearchDone
22else
23SearchAborted;
24end ;
25end ;
26
27procedure TThreadFileSearch.OnSearch(Sender: TObject; const BaseDir: string ;
28const FoundRec: TSearchRec);
29var
30DoDispatch: TFileFoundSubjectDispatcher;
31begin
32if not Terminated then
33begin
34DoDispatch : = FUpdateSubjectDispatcher;
35if DoDispatch <> nil then
36begin
37{ $IFDEF VCL10ORABOVE }
38DoDispatch.SearchFoundInfo : = FoundRec;
39{ $ELSE }
40DoDispatch.SearchFoundInfo : = SearchRec2SearchFoundInfo(FoundRec);
41{ $ENDIF }
42with DoDispatch.SearchFoundInfo do
43begin
44Directory : = BaseDir;
45FullPathName : = BaseDir + FoundRec.Name;
46end ;
47DispatchNotification(fdsSearching);
48end ;
49end
50else
51FFileSearch.Abort;
52end ;
好了, 一切顺利, 离大功告成仅有一步之遥. 接下来实现界面上的 Observer, 并调用 TThreadFileSearch 即可.
由于这个 Demo 十分简单, 所以就直接让 TTThreadFileSearchDemo (继承自 TForm) 直接实现了 IObserverWatcher 接口,
即 ProcessUpdate 方法. 在窗体的 OnCreate 里面, 创建一个 TFileFoundSubjectDispatcher 实例, 然后将自己 Attach
到观察者列表中以便稍侯接收通知.
1
procedure
TTThreadFileSearchDemo.FormCreate(Sender: TObject);
2
begin
3
FThreadFileSearch :
=
nil
;
4
FFIleFoundSubjectDispatcher :
=
TFileFoundSubjectDispatcher.Create(False);
5
FFIleFoundSubjectDispatcher.Attach(Self);
6
end
;
然后在点击 btnSearch 按钮的时候, 生成线程, 并传递这个主题订阅者, 然后执行搜索.
1
procedure
TTThreadFileSearchDemo.btnSearchClick(Sender: TObject);
2
![](http://img.e-com-net.com/image/product/46339e8aae704cb1a8b93e7cd90036c0.gif)
3
procedure
AppendInComboBox(
const
S:
string
; AComboBox: TComboBox);
{
$IFDEF VCL10ORABOVE
}
inline
;
{
$ENDIF
}
4
var
5
Index: Integer;
6
begin
7
if
(S
<>
''
)
and
(Trim(S)
<>
''
)
then
8
begin
9
Index :
=
AComboBox.Items.IndexOf(S);
10
if
Index
=
-
1
then
11
AComboBox.Items.Insert(
0
, S)
12
else
13
AComboBox.Items.Move(index,
0
);
14
end
;
15
end
;
16
![](http://img.e-com-net.com/image/product/46339e8aae704cb1a8b93e7cd90036c0.gif)
17
begin
18
if
(Trim(cbbSearchDir.Text)
=
''
)
or
(Length(cbbSearchDir.Text)
<
3
)
then
19
begin
20
MessageBox(Handle,
'
请输入您需要搜索的目录.
'
,
'
警告
'
,
21
MB_ICONWARNING);
22
cbbSearchDir.SetFocus;
23
cbbSearchDir.SelectAll;
24
Exit;
25
end
26
else
if
(cbbSearchDir.Text[
2
]
<>
'
:
'
)
or
(cbbSearchDir.Text[
3
]
<>
'
\
'
)
then
27
begin
28
MessageBox(Handle,
'
请输入一个有效的路径.
'
,
'
错误
'
, MB_ICONERROR);
29
cbbSearchDir.SetFocus;
30
cbbSearchDir.SelectAll;
31
Exit;
32
end
33
else
if
not
DirectoryExists(cbbSearchDir.Text)
then
34
begin
35
MessageBox(Handle,
'
请输入一个存在的目录.
'
,
'
警告
'
, MB_ICONWARNING);
36
cbbSearchDir.SetFocus;
37
cbbSearchDir.SelectAll;
38
Exit;
39
end
;
40
AppendInComboBox(cbbSearchDir.Text, cbbSearchDir);
41
if
FThreadFileSearch
=
nil
then
42
begin
43
SearchFileCount :
=
0
;
44
SearchSpace :
=
0
;
45
LockUIComponents;
46
FThreadFileSearch :
=
TThreadFileSearch.Create(True, FFIleFoundSubjectDispatcher,
47
cbbSearchDir.Text, True);
48
FThreadFileSearch.Priority :
=
FPriority;
49
FThreadFileSearch.OnTerminate :
=
ThreadOnTerminate;
50
FThreadFileSearch.FreeOnTerminate :
=
True;
51
FThreadFileSearch.Resume;
52
end
53
else
54
MessageBox(Handle,
'
请首先停止当前的搜索工作, 然后再点击"搜索"按钮.
'
,
55
'
错误
'
, MB_ICONERROR);
56
end
;
最后再在 ProcessUpdate 方法中, 写上更新界面显示的代码即大功告成.
1
procedure
TTThreadFileSearchDemo.OnSearching(
const
ASearchFoundInfo: TSearchFoundInfo);
2
begin
3
if
lblStatus.Caption
<>
'
正在搜索
'
then
4
lblStatus.Caption :
=
'
正在搜索
'
;
5
SearchFileCount :
=
SearchFileCount
+
1
;
6
SearchSpace :
=
SearchSpace
+
ASearchFoundInfo.Size;
7
edtPath.Text :
=
ASearchFoundInfo.Directory;
8
edtName.Text :
=
ASearchFoundInfo.Name;
9
lblSize.Caption :
=
Format(
'
大小: %s
'
, [FormatFileSize(ASearchFoundInfo.Size,
10
stT, True)]);
11
lblAttr.Caption :
=
Format(
'
属性: %d
'
, [ASearchFoundInfo.Attributes]);
12
lblCreateTime.Caption :
=
'
创建时间:
'
+
FormatDateTime(
'
yyyy-mm-dd hh:nn:ss
'
,
13
ASearchFoundInfo.CreationTime);
14
lblLastAccessTime.Caption :
=
'
最后访问时间:
'
+
FormatDateTime(
'
yyyy-mm-dd
'
,
15
ASearchFoundInfo.LastAccessTime);
16
lblLastWriteTime.Caption :
=
'
最后写入时间:
'
+
FormatDateTime(
'
yyyy-mm-dd hh:nn:ss
'
,
17
ASearchFoundInfo.LastWriteTime);
18
lbl8_3Name.Caption :
=
Format(
'
8.3文件名: %s
'
, [ASearchFoundInfo.Name_
8
_
3
]);
19
end
;
20
![](http://img.e-com-net.com/image/product/46339e8aae704cb1a8b93e7cd90036c0.gif)
21
function
TTThreadFileSearchDemo.ProcessUpdate(
22
ACustomSubjectDispatcher: TCustomSubjectDispatcher): Boolean;
23
var
24
Dsp: TFileFoundSubjectDispatcher;
25
begin
26
Result :
=
False;
27
if
ACustomSubjectDispatcher
is
TFileFoundSubjectDispatcher
then
28
begin
29
Dsp :
=
ACustomSubjectDispatcher
as
TFileFoundSubjectDispatcher;
30
case
Dsp.Stauts
of
31
fdsBeforeSearch: OnBeginSearch;
32
fdsSearching: OnSearching(Dsp.SearchFoundInfo);
33
fdsSearchDone: OnSearchDone;
34
fdsSearchAborted: OnSearchAborted;
35
end
;
36
Result :
=
True;
37
end
;
38
end
;
按下 F9, 点击 Search 按钮, 试试看, 呵呵非常成功!![](http://img.e-com-net.com/image/product/3674433d0d9f408b9c6a178177ad2609.gif)
,
上次介绍完了文件搜索和观察者模式的相关代![【zt】一个用 Observer 模式实现的 Thread 线程文件搜索例子](http://img.e-com-net.com/image/product/28fb4f620ad24c02a5d521c3004dd0f7.jpg)
om.cnu5591c079010005源码和 Demo 可以从这里下载 http://www.2ccc.com/article.asp?articleid=3740, 供大家参考.