C#编程模式之扩展命令
前言
根据上一篇的命令模式和在工作中遇到的一些实际情况,有了本篇文章,时时都是学习的一个过程,会在这个过程中发现许多好的模式或者是一种开发方式,今天写出来的就是我工作中常用到的,自己感觉这种方式很优雅很漂亮,就自己试着实现了一下,可能原框架中不是这样的,有许多不足之处还请大家指点。
需求
我还不清楚这种方式是模式还是框架开发中用到的技术,我暂且叫它为命令控制器吧。
命令控制器的主要功能就是获取用户提供的命令,然后来执行命令。 在这里我把要执行的“命令”设计成一个函数,会对应着一个String类型的命令字符串,并且命令控制器是允许扩展的。
实现
首先我定义了一个属性类,用于在扩展的命令类、或者命令函数上,只有一个CommandName属性,作用于命令函数上的话,则代表命令名称,如果是作用于类上面的话就代表命令类别的名称,只是考虑可以作为一个分类,这部分在后面有讲到,可以自定义实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
1
/// <summary>
2
/// 命令所用代码属性类
3
/// </summary>
4 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple =
false
, Inherited =
true
)]
5
public
class
CommandAttribute : Attribute
6 {
7
private
string
commandName =
null
;
8
9
public
string
CommandName
10 {
11
get
{
return
commandName; }
12
set
{ commandName = value; }
13 }
14
15
public
CommandAttribute(
string
CommandName)
16 {
17 commandName = CommandName;
18 }
19 }
|
有了这个属性类了,我们就要把它用起来,定义一些后面要用到的命令示例类,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
1
/// <summary>
2
/// 示例:命令类以及命令函数,亦可当作为扩展
3
/// </summary>
4 [Command(
"Test"
)]
5
public
class
CommandTest : CommandMethod
6 {
7 [Command(
"MyCommandone"
)]
8
public
object
test(ICommandParameter commandparameter)
9 {
10
return
null
;
11 }
12
13 [Command(
"MyCommandone1"
)]
14
public
object
test1(ICommandParameter commandparameter)
15 {
16
return
null
;
17 }
18
19 [Command(
"MyCommandone2"
)]
20
public
object
test2(ICommandParameter commandparameter)
21 {
22
return
null
;
23 }
24
25
26 [Command(
"MyCommandone3"
)]
27
public
object
test3(ICommandParameter commandparameter)
28 {
29
return
null
;
30 }
31
32 [Command(
"MyCommandone4"
)]
33
public
object
test4(ICommandParameter commandparameter)
34 {
35
return
null
;
36 }
37 }
|
上面的示例代码中可以看到CommandTest类继承自CommandMethod类,而类里面的一些函数的签名也是一样的,函数参数都为ICommandParameter接口类型,这就是扩展命令方法时要遵循的一些规范,定义的规范:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
1
/// <summary>
2
/// 扩展命令函数的参数规范
3
/// </summary>
4
public
interface
ICommandParameter
5 {
6
7 }
8
9
/// <summary>
10
/// 扩展命令方法类基类
11
/// </summary>
12
public
class
CommandMethod
13 {
14
15 }
|
需要实现什么都是可以自定义,比如说可以在ICommandParameter接口中定义个object类型的名称为ContainerObject的属性,意思就是容器属性,可以在后面调用命令时,传入实例参数时设置容器属性的值,可以设置为任何你想设置的值到里面,然后再在命令函数里使用它,也可以根据抽象定义实现一个参数上下文对象专门用于处理扩展命令的,看需要自定义吧。
然后就是书写一个命令控制器的代码了,它呢主要负责把客户端注册进来的类型做一些处理,比如说 判断类型、反射类型获取函数命令以及函数信息、把命令函数转换成委托、维护命令列表等等一些简单的功能,还用到了一个CommandMethodActionDelegate类型的委托:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
1
public
delegate
object
CommandMethodActionDelegate(ICommandParameter commandParameter);
2
3
public
class
CommandController
4 {
5
private
static
CommandController _Instance =
null
;
6
7
public
static
CommandController Instance
8 {
9
get
10 {
11
if
(_Instance ==
null
)
12 {
13 _Instance =
new
CommandController(HostDevelopment.Instance);
14 }
15
return
_Instance;
16 }
17 }
18
19
private
HostDevelopment _CommandDevelopment = HostDevelopment.Instance;
20
21
public
CommandController(HostDevelopment commandDevelopment)
22 {
23 _CommandDevelopment = commandDevelopment;
24 }
25
26
private
Dictionary<
string
, CommandMethodActionDelegate> commandlist =
new
Dictionary<
string
, CommandMethodActionDelegate>();
27
28
29
private
List<
string
> _commandNames =
null
;
30
/// <summary>
31
/// 命令名称集合
32
/// </summary>
33
public
List<
string
> CommandNames
34 {
35
get
36 {
37
if
(_commandNames ==
null
)
38 {
39 GetCommandNames();
40 }
41
return
_commandNames;
42 }
43 }
44
45
private
void
GetCommandNames()
46 {
47
48
if
(commandlist.Count > 0)
49 {
50
if
(_commandNames ==
null
)
51 {
52 _commandNames =
new
List<
string
>();
53 }
54
foreach
(
string
name
in
commandlist.Keys)
55 {
56 _commandNames.Add(name);
57 }
58 }
59 }
60
61
public
bool
RegisterCommand(
object
instance)
62 {
63 Type t = instance.GetType();
64 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(t,
typeof
(CommandAttribute),
false
);
65
if
(cmdatt !=
null
)
66 {
67 AddCommandToModel(instance);
68
return
true
;
69 }
70
else
{
return
false
; }
71 }
72
73
private
void
AddCommandToModel(
object
instance)
74 {
75 Type t = instance.GetType();
76 MethodInfo[] methods = t.GetMethods();
77
foreach
(MethodInfo methodinfo
in
methods)
78 {
79 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(methodinfo,
typeof
(CommandAttribute),
false
);
80
if
(cmdatt !=
null
)
81 {
82
83 CommandMethodActionDelegate commanddelegate = (CommandMethodActionDelegate)Delegate.CreateDelegate(
typeof
(CommandMethodActionDelegate), instance, methodinfo.Name);
84 commandlist.Add(cmdatt.CommandName, commanddelegate);
85 }
86
87 }
88 }
89
90
91
internal
object
Execute(
string
commandName, ICommandParameter commandParameter)
92 {
93
if
(commandName ==
null
)
94 {
95
throw
new
ArgumentNullException(
"commandName"
);
96 }
97
if
(!commandlist.ContainsKey(commandName))
98 {
99
return
new
ArgumentNullException(
"不包含的命令,命令无效"
);
100 }
101 CommandMethodActionDelegate cmdaction = commandlist[commandName];
102
return
cmdaction.Invoke(commandParameter);
103 }
104 }
|
在CommandController类型中,RegisterCommand()方法为注册扩展命令到命令控制器,示例中的注册方式为手动的传入类型实例来注册的,也可以把实现方式修改为在命令控制器启动的时候获取当前系统所有依赖项的程序集,获取到所有符合类型规范的扩展命令类型,并且注册到控制器中。
上面代码中有说到的HostDevelopment类型,是我定义的一个宿主容器对象,用它来承载命令控制器,以及在这个系列的文章中,都是使用HostDevelopment来进行对控制器的承载,这是后话。现在来看一下HostDevelopment暂时的定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
1
public
delegate
object
CommandMethodActionDelegate(ICommandParameter commandParameter);
2
3
public
class
CommandController
4 {
5
private
static
CommandController _Instance =
null
;
6
7
public
static
CommandController Instance
8 {
9
get
10 {
11
if
(_Instance ==
null
)
12 {
13 _Instance =
new
CommandController(HostDevelopment.Instance);
14 }
15
return
_Instance;
16 }
17 }
18
19
private
HostDevelopment _CommandDevelopment = HostDevelopment.Instance;
20
21
public
CommandController(HostDevelopment commandDevelopment)
22 {
23 _CommandDevelopment = commandDevelopment;
24 }
25
26
private
Dictionary<
string
, CommandMethodActionDelegate> commandlist =
new
Dictionary<
string
, CommandMethodActionDelegate>();
27
28
29
private
List<
string
> _commandNames =
null
;
30
/// <summary>
31
/// 命令名称集合
32
/// </summary>
33
public
List<
string
> CommandNames
34 {
35
get
36 {
37
if
(_commandNames ==
null
)
38 {
39 GetCommandNames();
40 }
41
return
_commandNames;
42 }
43 }
44
45
private
void
GetCommandNames()
46 {
47
48
if
(commandlist.Count > 0)
49 {
50
if
(_commandNames ==
null
)
51 {
52 _commandNames =
new
List<
string
>();
53 }
54
foreach
(
string
name
in
commandlist.Keys)
55 {
56 _commandNames.Add(name);
57 }
58 }
59 }
60
61
public
bool
RegisterCommand(
object
instance)
62 {
63 Type t = instance.GetType();
64 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(t,
typeof
(CommandAttribute),
false
);
65
if
(cmdatt !=
null
)
66 {
67 AddCommandToModel(instance);
68
return
true
;
69 }
70
else
{
return
false
; }
71 }
72
73
private
void
AddCommandToModel(
object
instance)
74 {
75 Type t = instance.GetType();
76 MethodInfo[] methods = t.GetMethods();
77
foreach
(MethodInfo methodinfo
in
methods)
78 {
79 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(methodinfo,
typeof
(CommandAttribute),
false
);
80
if
(cmdatt !=
null
)
81 {
82
83 CommandMethodActionDelegate commanddelegate = (CommandMethodActionDelegate)Delegate.CreateDelegate(
typeof
(CommandMethodActionDelegate), instance, methodinfo.Name);
84 commandlist.Add(cmdatt.CommandName, commanddelegate);
85 }
86
87 }
88 }
89
90
91
internal
object
Execute(
string
commandName, ICommandParameter commandParameter)
92 {
93
if
(commandName ==
null
)
94 {
95
throw
new
ArgumentNullException(
"commandName"
);
96 }
97
if
(!commandlist.ContainsKey(commandName))
98 {
99
return
new
ArgumentNullException(
"不包含的命令,命令无效"
);
100 }
101 CommandMethodActionDelegate cmdaction = commandlist[commandName];
102
return
cmdaction.Invoke(commandParameter);
103 }
104 }
|
看了这些了,应该就大致的明白了,不过现在这样的代码还是测试不了的,因为缺少一些实体。定义一个默认实现的命令参数规范:
1
2
3
4
5
6
7
8
9
10
|
1
public
class
CommandParameterCase:ICommandParameter
2 {
3
private
string
_StrText;
4
5
public
string
StrText
6 {
7
get
{
return
_StrText; }
8
set
{ _StrText = value; }
9 }
10 }
|
然后再修改一下CommandTest类型中的第一个叫test的函数(对应的命令名称为MyCommandone),函数名称有点随意,大家不要介意这些。
1
2
3
4
5
6
7
8
9
10
|
1 [Command(
"MyCommandone"
)]
2
public
object
test(ICommandParameter commandparameter)
3 {
4
if
(commandparameter !=
null
)
5 {
6 CommandParameterCase commandparametercase = commandparameter
as
CommandParameterCase;
7
return
commandparametercase.StrText;
8 }
9
else
{
return
null
; }
10 }
|
这样所需的都定义齐了。 我们再看一下调用代码:
1
2
3
|
1 HostDevelopment.Instance.Start();
2 HostDevelopment.Instance.CommandController.RegisterCommand(
new
CommandTest());
3
var
strtext = HostDevelopment.Instance.Execute(
"MyCommandone"
,
new
CommandParameterCase() { StrText =
"test"
});
|
对了随便是打个断点或者是输出strtext都可以看到它的值。这里不作多的解释了。
最后加上对象关系图解吧,并不是什么标准的画法,有助于理解就行了。
END
本文转自jinyuan0829 51CTO博客,原文链接:http://blog.51cto.com/jinyuan/1409597,如需转载请自行联系原作者