本文由 ImportNew - ImportNew读者 翻译自 michaelbrameld。如需转载本文,请先参见文章末尾处的转载要求。

【感谢 李云涛(@平等的黑)的热心翻译。如果其他朋友也有不错的原创或译文,可以尝试投递到 ImportNew。】

在使用动态语言和.NET工作了若干年后,我又回到老本行–Java开发。在Ruby中,清除代码冗余是非常方便的,而在Java中则需要结合接口和泛型实现类似的功能。

原始代码

以下是这个类中的一些方法用于后续的阐述。为了使例子更简洁,我移除了些代码。

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
publicV
get(
finalK
key)
{
  Session
s;
  try{
      s
= oGrid.getSession();
      ObjectMap
map = s.getMap(cacheName);
      return(V)
map.get(key);
  }
  catch(ObjectGridException
oge)
  {
      thrownew

RuntimeException(
"Error
performing cache operation"
,
oge);
  }
  finally
  {
      if(s
!=
null)
          s.close();        
  }   
  returnnull;
}     
 
publicvoid

put(
finalK
key,
finalV
value)
{
  Session
s;
  try{
      s
= oGrid.getSession();
      ObjectMap
map = s.getMap(cacheName);
      map.upsert(key,
value);
  }
  catch(ObjectGridException
oge)
  {
      thrownew

RuntimeException(
"Error
performing cache operation"
,
oge);
  }
  finally
  {
      if(s
!=
null)
          s.close();            
  }           
}
 
publicMap<K,
V> getAll(Set<?
extendsK>
keys)
{
  finalList<V>
valueList =
newArrayList<V>();
  finalList<K>
keyList =
newArrayList<K>();
  keyList.addAll(keys);
 
  Session
s;
  try{
      s
= oGrid.getSession();
      ObjectMap
map = s.getMap(cacheName);
      valueList.addAll(map.getAll(keyList));
  }
  catch(ObjectGridException
oge)
  {
      thrownew

RuntimeException(
"Error
performing cache operation"
,
oge);
  }
  finally
  {
      if(s
!=
null)
          s.close();            
  }
 
  Map<K,
V> map =
newHashMap<K,
V>();
  for(inti
=
0;
i < keyList.size(); i++) {
      map.put(keyList.get(i),
valueList.get(i));
  }
  returnmap;
}

遇到的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Session
s;
try{
  s
= oGrid.getSession();
  ObjectMap
map = s.getMap(cacheName);
  //
Some small bit of business logic goes here
}
catch(ObjectGridException
oge)
{
  thrownew

RuntimeException(
"Error
performing cache operation"
,
oge);
}
finally
{
  if(s
!=
null)
      s.close();            
}

上面的代码段几乎存在于类的每个方法中,这违反了DRY原则 。将来如果需要改变检索Session 和 ObjectMap实例的方式,或着某天这段代码被发现有缺陷,我们就不得不修改每个(包含这段代码的)方法,因此需要找到一种方式来复用这些执行代码。

重构后的代码

为了传递包含了原方法中业务逻辑的实例,我们创建一个带有抽象方法的 Executable 接口 。execute()方法参数为我们欲操作的ObjectMap实例。

1
2
3
interfaceExecutable<T>
{
  publicT
execute(ObjectMap map)
throwsObjectGridException;
}

由于我们的目的仅仅是在每个方法中操作ObjectMap实例,可以创建executeWithMap()方法封装前述的那一大段重复代码。这个方法的参数是Executable接口的实例,实例包含着操作map的必要逻辑(译者注:这样Executable接口的实例中就是纯粹的业务逻辑,实现了解耦合)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private<T>
T executeWithMap(Executable<T> ex)
{
  Session
s;
  try{
      s
= oGrid.getSession();
      ObjectMap
map = s.getMap(cacheName);
      //
Execute our business logic
      returnex.execute(map);
  }
  catch(ObjectGridException
oge)
  {
      thrownew

RuntimeException(
"Error
performing cache operation"
,
oge);
  }
  finally
  {
      if(s
!=
null)
          s.close();            
  }
}

现在,可以用如下形式的模板代码替换掉第一个例子中的代码:这个模板创建了一个匿名内部类,实现了Executable接口和execute()方法。其中execute()方法执行业务逻辑,并以getXXX()的方式返回结果(若为Void方法,返回null)

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
publicV
get(
finalK
key)
{
  returnexecuteWithMap(newExecutable<V>()
{
      publicV
execute(ObjectMap map)
throwsObjectGridException
      {
          return(V)
map.get(key);
      }
  });             
}     
 
publicvoid

put(
finalK
key,
finalV
value)
{
  executeWithMap(newExecutable<Void>()
{
      publicVoid
execute(ObjectMap map)
throwsObjectGridException
      {
          map.upsert(key,
value);
          returnnull;
      }
  });             
}
 
publicMap<K,
V> getAll(Set<?
extendsK>
keys)
{
  finalList<K>
keyList =
newArrayList<K>();
  keyList.addAll(keys);
  List<V>
valueList = executeWithMap(
newExecutable<List<V>>()
{
      publicList<V>
execute(ObjectMap map)
throwsObjectGridException
      {
          returnmap.getAll(keyList);
      }
  });                             
 
  Map<K,
V> map =
newHashMap<K,
V>();
  for(inti
=
0;
i < keyList.size(); i++) {
      map.put(keyList.get(i),
valueList.get(i));
  }
  returnmap;
}

FunctionalInterface Annotation (功能接口注释)

Java 8 的 @FunctionalInterface annotation 使这一切变的简单。若某接口带有一个抽象方法,这个接口便可以被用作为lambda表达式的参数,称为功能接口。

1
2
3
4
@FunctionalInterface
interfaceExecutable<T>
{
  publicT
execute(ObjectMap map)
throwsObjectGridException;
}

只要接口仅仅包含一个抽象方法,便可以使用这个annotation。这样就能减少相当数量的模板代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
publicV
get(
finalK
key)
{
  returnexecuteWithMap((ObjectMap
map) -> (V) map.get(key));
}     
 
publicvoid

put(
finalK
key,
finalV
value)
{
  executeWithMap((ObjectMap
map) -> { map.upsert(key, value);
returnnull;
});
}
 
publicMap<K,
V> getAll(Set<?
extendsK>
keys)
{
  finalList<K>
keyList =
newArrayList<K>();
  keyList.addAll(keys);
  List<V>
valueList = executeWithMap((ObjectMap map) -> map.getAll(keyList));
 
  Map<K,
V> map =
newHashMap<K,
V>();
  for(inti
=
0;
i < keyList.size(); i++) {
      map.put(keyList.get(i),
valueList.get(i));
  }
  returnmap;
}

结论

实现这些重构我很开心。它比原始的代码略复杂一点,但是更简明,更DRY,所以一切都是值得的。 尽管还有提升的空间,但这是一个良好的开始。

原文链接: michaelbrameld 翻译: ImportNew.comImportNew读者

译文链接: http://www.importnew.com/6761.html

避免代码冗余,使用接口和泛型重构Java代码的更多相关文章

  1. 重构Java代码的既有设计-影片出租店

    案例:计算每位顾客的消费金额并打印详细信息.顾客租赁了哪些影片,租期多长,根据租赁时间和影片类型计算出费用.影片分为3类:儿童片,新片,普通片.此外需计算该顾客的积分. Movie: public c ...

  2. 让JAVA代码跑得更快

    本文简单介绍一下在写代码过程中用到的一些让JAVA代码更高效的技巧. 1.   将一些系统资源放在池中(如数据库连接, 线程等) 在standalone的应用中, 数据库连接池可以使用一些开源的连接池 ...

  3. 使用非java代码编程

    使用非JAVA代码     JAVA语言及其标准API(应用程序编程接口)应付应用程序的编写已绰绰有余.但在某些情况下,还是必须使用非JAVA编码.例如,我们有时要访问操作系统的专用特性,与特殊的硬件 ...

  4. Android逆向-java代码基础

    作者:I春秋作家——HAI_ 0×00 前言 看这篇可以先看看之前的文章,进行一个了解.Android逆向-java代码基础(1)Android逆向-java代码基础(2) 之前看到有大佬用smali ...

  5. 【Android】通过Java代码替换TabHost中的drawableTop资源

    在博客 http://blog.csdn.net/jueblog/article/details/11837445 中的Tab选项卡中, 点击相应的Tab选项,图标没有发生改变. 这些资源图片也没有尽 ...

  6. Java代码块详解

    Java中代码块指的是用 {} 包围的代码集合,分为4种:普通代码块,静态代码块,同步代码块,构造代码块 普通代码块: 定义:在方法.循环.判断等语句中出现的代码块 修饰:只能用标签修饰 位置:普通代 ...

  7. java代码块的理解

    最近在复习java基础,在看到java代码块的时候,忽然发现自己貌似对于java代码块一无所知,于是赶紧对着一些资料实战演练了一把. 对于java代码块,不难根据名称看出其实就是一些java语句的集合 ...

  8. 阿里官方Java代码规范标准《阿里巴巴Java开发手册 终极版 v1.3.0》

    终极版 v1.3.0 2017年开春之际,阿里诚意献上重磅大礼:<阿里巴巴Java开发手册>,首次公开阿里官方Java代码规范标准.这套Java统一规范标准将有助于提高行业编码规范化水平, ...

  9. 在js中嵌套java代码

    jsp中有时候在js中操作某些java后台传递过来的数据逻辑比较复杂,比如list内容的遍历,可以直接在页面上添加java脚本来执行内容,代码如下: //在js中插入java代码操作 <% // ...

  10. 阿里官方Java代码规范标准《阿里巴巴Java开发手册》下载

    https://bbs.aliyun.com/read/306592.html?page=e 2017年开春之际,诚意献上重磅大礼:阿里巴巴Java开发手册,首次公开阿里官方Java代码规范标准. 这 ...

随机推荐

  1. C语言浮点数转字符串实现函数

    C语言浮点数转字符串可用库函数sprintf,此处为编写的简单函数. 小数部分最多显示六位. pOut:输出字符串缓冲区 f:浮点数值 isize:输出字符串缓冲区大小 char * Funftoa( ...

  2. 必应每日壁纸API封装

    简介 这个类封装了必应首页的每日壁纸查看功能,提供了查看.保存壁纸的方法,最大支持查看近8天的壁纸 使用方法 async Task Main() { try { var bing = BingWall ...

  3. ServiceMesh 1:大火的云原生微服务网格,究竟好在哪里?

    1 关于云原生 云原生计算基金会(Cloud Native Computing Foundation, CNCF)的官方描述是: 云原生是一类技术的统称,通过云原生技术,我们可以构建出更易于弹性扩展. ...

  4. AI实战 | 领克汽车线上营销助手:全面功能展示与效果分析

    助手介绍 我就不自我介绍了,在我的智能体探索之旅中,很多人已经通过coze看过我的教程.今天,我专注于分享我所开发的一款助手--<领克汽车线上营销>. 他不仅仅是一个销售顾问的替身,更是一 ...

  5. CSS – Box Shadow & Text Shadow

    前言 之前在 CSS – W3Schools 学习笔记 (3) 介绍过这个功能, 但一直不熟练. 每次用都卡卡的, 估计是没有写一篇独立的笔记的缘故. 特此写一篇. Text Shadow 下面这个是 ...

  6. 为了更好的 Flutter | 2021 第二季度开发者调研

    自 Flutter 2 发布以后,诸多振奋人心的开发特性 也随之解锁,从 加速应用开发 到 流畅地开发多平台应用 都已经有了长足的进步.我们也从社区收集到了不少大家关心的问题,并进行了 解答.毫无疑问 ...

  7. linux内核调试痛点之函数参数抓捕记

    1.linux内核调试工具crash并不能直接显示函数参数,而这个对调试又非常重要 下面是工作中一个实际的问题,我们的进程hang在如下一个内核栈中了,通过栈回溯可知是打开了一个nfs3的网盘文件或者 ...

  8. USB-DFP UFP DRP模式

    USB Type-C 接口支持三种模式:DFP(Downstream Facing Port).UFP(Upstream Facing Port)和 DRP(Dual Role Port).虽然这些术 ...

  9. 《Programming from the Ground Up》读后感

    之所以看这本书,是想了解一些跟汇编相关的知识,打开这本书后就被作者的观点--"If you don't understand something the first time, reread ...

  10. kotlin更多语言结构——>选择加入要求

    1.3.70 中引入了 @RequireOptIn 与 @OptIn 注解以取代先前使用的 @Experimental 与 @UseExperimental;同时 -Xopt-in 编译器选项也取代了 ...