TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
形式一."见兔撒鹰".
如果有一个类有一个集合成员,可以在为此成员添加元素时再把具体集合建立起来,以免浪费空间和时间. 例:
1
public
class
Company{
2
private
List members
=
null
;
3
4
public
void
addMember(Member member){
5
if
(members
=
null
){
6
members
=
new
ArrayList();
7
}
8
9
members.add(member);
10
}
11
}
12
异曲同工的另外一个例子:
1
public
class
Singleton{
2
private
static
Singleton instance
=
null
;
3
4
public
static
synchronized
Singleton getInstance(){
5
//
要用的时候再把Singleton建立起来
6
if
(instance
==
null
){
7
instance
=
new
Singleton();
8
}
9
10
return
instance;
11
}
12
}
13
形式二."只管结果,不顾过程"
从集合中取区间元素时,可以直接从上下限之间取来,让try...catch...去处理越界的事. 题设:有个数不定元素的列表(allTodoes),需要从中取N个,起始位置不限,你怎么编写程序. 很多人开始进行越界的判断,出来一堆if else,有时还需要在纸上写好思路,完毕后还有多方测试,生怕出错,即使编写好后其他人维护起来也是小心翼翼的.其实没必要这么麻烦. 例.
1
int
start
=
pageIndex
*
pageSize;
2
int
end
=
start
+
pageSize;
3
4
for
(
int
i
=
start;i
<
end;i
++
){
5
try
{
6
todoResult.addTodo((Todo)allTodoes.get(i));
7
}
8
catch
(Exception ex){
9
continue
;
10
}
11
}
题外话:分支和循环语句天生就不容易理解,尤其是在嵌套较深的时候,因为这是机器的思维特性.还是try...catch...比较贴近人类思维.
形式三."奉旨行事"
在查询中,如果把查询条件和遴选过程分开来更有益,程序也因此解耦合.这才是OO化的查询. 需求:从公司的职员列表中,找出男性且年龄大于22的成员. 传统写法:
1
List allmembers
=
company.getMembers();
//
取得所有成员
2
List results
=
new
ArrayList();
//
结果列表
3
4
for
(Iterator it
=
allmembers.iterator();it.hasNext();){
5
Member member
=
(Member)it.next();
6
7
if
(member.getAge()
>
22
&&
member.isMale()){
//
筛选,这里是把查询条件和遴选过程融合在一起,条件一变立即就得加个分支.
8
results.add(member);
9
}
10
}
11
这种写法没有错,但是不是面向对象的写法,它有以下缺陷:
1.查询条件和筛选过程没有分离.
2.这样写的后果使Company变成了一个失血模型而不是领域模型.
3.换查询条件的话,上面除了"筛选"一句有变化外其它都是模板代码,重复性很高. 真正符合OO的查询应该是这样:
1
MemberFilter filter1
=
new
MemberFilter(){
2
public
boolean
accept(Member member) {
3
return
member.isMale()
&&
member.getAge()
>
22
;
4
}
5
};
6
7
List ls
=
company.listMembers(filter1);
这段代码成功的把查询条件作为一个接口分离了出去,接口代码如下:
1
public
interface
MemberFilter{
2
public
boolean
accept(Member member);
3
}
而类Company增加了这样一个函数:
1
public
List listMembers(MemberFilter memberFilter){
2
List retval
=
new
ArrayList();
3
4
for
(Iterator it
=
members.iterator();it.hasNext();){
5
Member member
=
(Member)it.next();
6
7
if
(memberFilter.accept(member)){
8
retval.add(member);
9
}
10
}
11
12
return
retval;
13
}
这就把模板代码归结到了类内部,外面不会重复书写了.Company也同时拥有了数据和行为,而不是原来的数据容器了. 形式四."化繁为简"
这种结构将多个分支语句变换为一个查表结构,这样做对扩充程序结构,修改具体数额都很方便,使程序更易于维护.还可以把归结出的表结构放在持久介质中如XML文件,数据库等,用到的时候再取,这样做在条件变化时不需要修改程序. 原始代码(VB代码,但应该不妨碍理解):
1
Dim
count1
2
count1
=
salary.Value
+
USA.Value
*
Drate
+
JAN.Value
*
Jrate
-
4000
3
If
count1
<
500
Then
4
tax.Value
=
count1
*
0.05
5
ElseIf
count1
<
2000
Then
6
tax.Value
=
count1
*
0.1
-
25
7
ElseIf
count1
<
5000
Then
8
tax.Value
=
count1
*
0.15
-
125
9
ElseIf
count1
<
20000
Then
10
tax.Value
=
count1
*
0.2
-
375
11
ElseIf
count1
<
40000
Then
12
tax.Value
=
count1
*
0.25
-
1375
13
ElseIf
count1
<
60000
Then
14
tax.Value
=
count1
*
0.3
-
3375
15
Else
16
tax.Value
=
count1
*
0.3
-
3375
17
End
If
变换如下:
这是个税率计算的语句段,公式是确定的:税=月薪*税率-折扣,税率又和月薪有关系,月薪越高税率越高,首先这里可以归纳出一个基本类:
1
public
class
TaxItem{
2
float
limit;
//
月薪界限
3
float
ratio;
//
税率
4
float
discount;
//
折扣
5
6
public
TaxItem(
float
limit,
float
ratio,
float
discount){
7
this
.limit
=
limit;
8
this
.ratio
=
ratio;
9
this
.discount
=
discount;
10
}
11
12
public
TaxItem(){
13
this
(
0.0f
,
0.0f
,
0.0f
);
14
}
15
16
public
float
getDiscount() {
17
return
discount;
18
}
19
20
public
float
getLimit() {
21
return
limit;
22
}
23
24
public
float
getRatio() {
25
return
ratio;
26
}
27
}
28
29
然后就是税计算类:
1
public
class
TaxCaculator{
2
private
static
ArrayList list
=
new
ArrayList();
3
4
public
TaxCaculator(){
5
//
这里把各个等级加入到链表中,注意添加顺序是由小到大
6
list.add(
new
TaxItem(
500.0f
,
0.05f
,
0.0f
));
7
list.add(
new
TaxItem(
2000.0f
,
0.1f
,
25.0f
));
8
list.add(
new
TaxItem(
5000.0f
,
0.15f
,
125.0f
));
9
list.add(
new
TaxItem(
20000.0f
,
0.2f
,
375.0f
));
10
list.add(
new
TaxItem(
40000.0f
,
0.25f
,
1375.0f
));
11
list.add(
new
TaxItem(
60000.0f
,
0.3f
,
3375.0f
));
12
}
13
14
//
这个函数用来计算所得税
15
public
float
getTax(
float
salary){
16
TaxItem item
=
new
TaxItem();
17
18
for
(
int
i
=
0
;i
<
list.size();i
++
){
19
item
=
(TaxItem)list.get(i);
20
21
if
(salary
>
item.getLimit()){
22
continue
;
23
}
24
else
{
25
break
;
26
}
27
}
28
29
//
返回最终结果,当然,这个公式也可以放在TaxItem 类中,这里就见仁见智了。
30
return
salary
*
item.getRatio()
-
item.getDiscount();
31
}
32
}
调用如下 :
1
TaxCaculator taxCaculator
=
new
TaxCaculator();
2
3
float
salary
=
1000
.f;
4
System.out.println(
"
Salary=
"
+
salary
+
"
Tax=
"
+
taxCaculator.getTax(salary));
5
6
salary
=
2000
.f;
7
System.out.println(
"
Salary=
"
+
salary
+
"
Tax=
"
+
taxCaculator.getTax(salary));
8
9
salary
=
3000
.f;
10
System.out.println(
"
Salary=
"
+
salary
+
"
Tax=
"
+
taxCaculator.getTax(salary));
形式五."分而治之"
该结构将分支语句的执行部分分散到单独的类中处理,降低了系统耦合度,程序也更容易维护. 举例如下:
在日常工作中,我们经常需要解析一段字符串并交由相应的函数进行处理,例如TCP/IP通信中的命令解析和用户自定义文件解析等场合,通常的处理方法是这样: if(命令==”AAA”){
函数AAA执行;
}
else if(命令==”BBB”){
函数BBB执行;
}
.
.
.
else{
函数XXX执行;
} 这种方法在命令较少时是有效的,当命令众多时,if语句和相关的函数将会形成一个巨集,给检查,维护和扩充带来了很大的不便,久而久之将会成为系统性能提升的瓶颈。 一个成功的软件程序必须尽可能简单并易于重构和扩展,在命令模式和java反射机制的帮助下,我们可以从容解决上述问题,达到简单并易于重构和扩展的要求。以下将简要说明解决方案。 1. 制作一个命令的抽象接口.
1
public
interface
Command{
2
public
abstract
void
execute(String[] args);
3
}
2. 让每种命令都实现这个接口.
1
//
命令一
2
public
class
CommandType01
implements
Command{
3
public
void
execute(String[] args){
4
System.out.println(
"
commandType01 start!
"
);
5
System.out.print(
"
commandType01 Length=
"
+
args.length);
6
System.out.println(
"
commandType01 End!
"
);
7
}
8
}
9
10
//
命令二
11
public
class
CommandType02
implements
Command{
12
public
void
execute(String[] args){
13
System.out.println(
"
commandType02 start!
"
);
14
15
System.out.print(
"
commandType02 is:
"
);
16
for
(String item:args){
17
System.out.print(
"
"
+
item);
18
}
19
20
System.out.println(
"
commandType02 End!
"
);
21
}
22
}
23
24
//
命令三
25
public
class
CommandType03
implements
Command{
26
public
void
execute(String[] args){
27
System.out.println(
"
commandType03 start!
"
);
28
System.out.print(
"
commandType03 last Nation=
"
+
args[args.length
-
1
]);
29
System.out.println(
"
commandType03 End!
"
);
30
}
31
}
32
33
让每种命令都实现execute接口的用意是强制每个命令的执行方式一致,简化调用时的处理,但执行内容应该根据实际情况决定.
例如
命令一的执行内容是输出参数的个数
命令二的执行内容是输出参数的内容
命令三的执行内容是输出最后一个参数 3. 将命令防置到命令中心中去
命令中心类的代码如下:
1
public
class
Mediation{
2
Command cmmd;
//
命令对象的引用
3
String[] cmmdArgs;
//
参数列表
4
5
public
Mediation(){
6
7
}
8
9
public
void
fetchCommand(String strCmmd){
10
cmmdArgs
=
strCmmd.split(
"
s+
"
);
//
分析原始命令
11
12
String className
=
"
Command
"
+
cmmdArgs[
0
];
//
根据分析后命令的第一个参数得到类名
13
14
try
{
15
Class cls
=
Class.forName(className);
//
利用反射机制得到类
16
cmmd
=
(Command)cls.newInstance();
//
由类得到类实例
17
}
18
catch
(Exception ex){
19
ex.printStackTrace();
20
}
21
}
22
23
24
public
void
executeCommand(){
25
cmmd.execute(cmmdArgs);
//
执行命令对象的execute方法
26
}
27
28
}
4.执行过程
1
Mediation mediation
=
new
Mediation();
2
//
取得命令一并执行
3
mediation.fetchCommand(
"
Type01 AB CD
"
);
4
mediation.executeCommand();
5
6
//
取得命令二并执行
7
mediation.fetchCommand(
"
Type02 1 2 3 4
"
);
8
mediation.executeCommand();
9
10
//
取得命令三并执行
11
mediation.fetchCommand(
"
Type03 USA Russia China
"
);
12
mediation.executeCommand();
13
执行效果如下:
commandType01 start!
commandType01 Length=3
commandType01 End! commandType02 start!
commandType02 is: Type02 1 2 3 4
commandType02 End! commandType03 start!
commandType03 last Nation=China
commandType03 End! 由上可见,我们使用反射机制消除了庞大的分支语句,把命令的执行过程分散到了Command的各个子类中,降低了命令类和控制中心类的耦合程度,达到了简单并易于重构和扩展的要求。如果新增一种命令,只需增加Command的一个子类就可以了。
很多情况下命令的execute函数需要命令中心类或者其它类的信息,这时可以在Command接口类和Command的子类中间添加一个类CommandBase,在其中包含一个命令中心类或者其它类的引用,并增加相应的getter/setter函数,Command的子类继承这个类并实现Command的接口即可,最后在fetchCommand函数中传入中心类或者其它类的引用即可。 注意:这里对命令和Command的子类类名有特殊要求,即一种命令对应一种子类,子类类名可以由命令的首个参数简单组合而来,否则还是避免不了分支语句。 以上五种组织形式,若加以灵活运用,相信能减少一些代码臭味. |
|