持续重构,其乐无穷。
一:发现问题
先来说如何重构业务层的try{}catch{}finally{}代码块,我看过很多代码,异常处理这一块大致分为两种情况,一种是每个方法都大量的充斥着try{}catch{}finally{ },这种方式的编程已经考虑到了异常处理,还有一种就是没有try{ }catch{ }finally{ }的代码,因为根本就没有考虑代码的异常处理。
每当我看到这样的代码,我都很忧伤。从程序的健壮性来看第一种还是要比第二种情况好,至少在编程意识中,随时想到了异常情况,有一种基本的编程思想。比如:一个业务单据的多表插入,关联修改,虚拟删除等都是一些基本的操作,但是又是比较容易引起错误的操作,在这些方法上都会加上try{ }catch{ }finally{}对代码进行有效的防错处理。早期的代码是这样的。
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
|
public
Boolean
Save
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
try
{
//TODO ...
result
=
true
;
}
catch
{
}
finally
{
}
return
result
;
}
public
Boolean
Edit
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
try
{
//TODO ...
result
=
true
;
}
catch
{
}
finally
{
}
return
result
;
}
public
Boolean
VirDelete
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
try
{
//TODO ...
result
=
true
;
}
catch
{
}
finally
{
}
return
result
;
}
|
仅仅定义了添加,修改,删除几个空方法,就写了三四十行代码,如果业务稍微复杂些,异常处理的代码很快就会突破百行大关。虽然复制,粘贴try{}catch{}finally{}很好使,但是业务逻辑代码大量充斥着这样的try{}catch{}finally{}代码,确实显得做事不够利落。
二:解决问题
那怎样来解决这件棘手的事呢,首先定义一个公用的try{}catch{}finally{},如下如示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public
class
Process
{
public
static
bool
Execute
(
Action
action
)
{
try
{
action
.
Invoke
(
)
;
return
true
;
}
catch
(
Exception
ex
)
{
//1,异常隐藏
//2,异常替换
//3,异常封装
//写日志
return
false
;
}
finally
{
}
}
}
|
上边的代码定义了公用的try{}catch{}finally{},最关键是怎么运用起来,如下代码:
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
|
protected
void
Page_Load
(
object
sender
,
EventArgs
e
)
{
AccountModel
accountData
=
new
AccountModel
(
)
;
//准备传入的参数
Boolean
result
=
false
;
//接收返回的值
Process
.
Execute
(
(
)
=
>
result
=
Save
(
accountData
)
)
;
//执行方法
}
public
Boolean
Save
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
//TODO ...
result
=
true
;
return
result
;
}
public
Boolean
Edit
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
//TODO ...
result
=
true
;
return
result
;
}
public
Boolean
VirDelete
(
AccountModel
accountData
)
{
Boolean
result
=
false
;
//TODO ...
result
=
true
;
return
result
;
}
|
这样的精简过的代码,是不是感觉心情很舒畅。
三:提升与扩展
对于知足者常乐的人来说,到第二个步骤就可以洗洗睡了。但是对于精益求精的人来说,问题仍然没有完。我们来说一个应用场景,在WCF中的应用,我们知道WCF服务端的异常,不经过的设置,服务端的异常是无法抛到客户端的。但是在正式环境中,不可能对进行serviceDebug的配置。正确的处理是在服务端对异常进行隐藏,替换,或者封装。
比如我们在服务端捕获了一个已知异常,但是这个异常会暴露一些敏感的信息,所以我们对异常进行替换,抛出新的异常后,我们还要把这个异常怎样传输给客户端。首先们要明确WCF中的一些基本常识,就是WCF中的数据传递要遵循WCF的数据契约,服务端抛到客户端的异常(异常其实也是数据),所以必须要给异常定义异常契约。
1
2
3
4
5
6
7
8
9
10
11
12
|
[
DataContract
(
Name
=
"WCFException"
)
]
public
class
WCFException
{
[
DataMember
(
Name
=
"Type"
)
]
public
String
Type
{
get
;
set
;
}
[
DataMember
(
Name
=
"StackTrace"
)
]
public
String
StackTrace
{
get
;
set
;
}
[
DataMember
(
Name
=
"Message"
)
]
public
String
Message
{
get
;
set
;
}
}
|
然后处理异常的公共方法改写为:
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
|
public
static
bool
Execute
(
Action
action
)
{
try
{
action
.
Invoke
(
)
;
return
true
;
}
catch
(
Exception
ex
)
{
//1,异常隐藏
//2,异常替换
//3,异常封装
//写日志
WCFException
exception
=
new
WCFException
{
Type
=
"Error"
,
StackTrace
=
ex
.
StackTrace
,
Message
=
ex
.
Message
}
;
throw
new
FaultException
(
exception
,
new
FaultReason
(
"服务端异常:"
+
ex
.
Message
)
,
new
FaultCode
(
ex
.
TargetSite
.
Name
)
)
;
}
finally
{
}
}
|
这样在服务端抛出的异常,就能在客户端捕捉到。现在是不是感觉自己又提升了一些,想成为编程高手是指日可待了。
四:举一反三
异常的处理也不过如此,那是不是应该举一反三,看看事务的处理应该怎么办?比如现在大量的访求都用到了事务,如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
Boolean
Save
(
AccountModel
accountData
)
{
OracleConnection
conn
=
new
OracleConnection
(
"连接字符串"
)
;
IDbTransaction
trans
=
conn
.
BeginTransaction
(
)
;
Boolean
result
=
false
;
try
{
//TODO ...
trans
.
Commit
(
)
;
result
=
true
;
}
catch
{
trans
.
Rollback
(
)
;
}
finally
{
}
return
result
;
}
|
特别是 trans.Commit(); trans.Rollback(); 这样的代码出现在每个与事务相关的方法中, 让我感觉到代码的臃肿,以及隐陷约约的失望。
经过我几天的翻阅资料终于实现了事务的公用访求提取。使用方法如下代码所示:
1
2
3
4
5
6
7
8
|
[
TransactionAttribute
]
[
ExceptionAttribute
]
public
bool
Save
(
DataContext
dContext
,
Dictionarystring
,
string
>
dtoPara
)
{
Boolean
returnVal
=
true
;
//TODO ...
return
returnVal
;
}
|
就是在一个方法上加[TransactionAttribute]就表示这个方法写在了事务中,反之,不在事务中,加[ExceptionAttribute]就表示这个方法作了异常处理,反之,不作异常处理。通过反射或者AOP都能实现Attribute编程的效果。最后,还有什么疑惑,可以在评论中给我留言