原文地址:http://www.michaelbrameld.com/blog/2013/11/02/refacoring-java-generic-functional-interface/
在使用动态语言和.NET工作了若干年后,我又回到老本行--Java开发。在Ruby中,清除代码冗余是非常方便的,而在Java中则需要结合接口和泛型实现类似的功能。
原始代码
以下是这个类中的一些方法用于后续的阐述。为了使例子更简洁,我移除了些代码。
1 public V get(final K key) 2 { 3 Session s; 4 try { 5 s = oGrid.getSession(); 6 ObjectMap map = s.getMap(cacheName); 7 return (V) map.get(key); 8 } 9 catch (ObjectGridException oge) 10 { 11 throw new RuntimeException("Error performing cache operation", oge); 12 } 13 finally 14 { 15 if (s != null) 16 s.close(); 17 } 18 return null; 19 } 20 21 public void put(final K key, final V value) 22 { 23 Session s; 24 try { 25 s = oGrid.getSession(); 26 ObjectMap map = s.getMap(cacheName); 27 map.upsert(key, value); 28 } 29 catch (ObjectGridException oge) 30 { 31 throw new RuntimeException("Error performing cache operation", oge); 32 } 33 finally 34 { 35 if (s != null) 36 s.close(); 37 } 38 } 39 40 public Map<K, V> getAll(Set<? extends K> keys) 41 { 42 final List<V> valueList = new ArrayList<V>(); 43 final List<K> keyList = new ArrayList<K>(); 44 keyList.addAll(keys); 45 46 Session s; 47 try { 48 s = oGrid.getSession(); 49 ObjectMap map = s.getMap(cacheName); 50 valueList.addAll(map.getAll(keyList)); 51 } 52 catch (ObjectGridException oge) 53 { 54 throw new RuntimeException("Error performing cache operation", oge); 55 } 56 finally 57 { 58 if (s != null) 59 s.close(); 60 } 61 62 Map<K, V> map = new HashMap<K, V>(); 63 for(int i = 0; i < keyList.size(); i++) { 64 map.put(keyList.get(i), valueList.get(i)); 65 } 66 return map; 67 }
遇到的问题
1 Session s; 2 try { 3 s = oGrid.getSession(); 4 ObjectMap map = s.getMap(cacheName); 5 // Some small bit of business logic goes here 6 } 7 catch (ObjectGridException oge) 8 { 9 throw new RuntimeException("Error performing cache operation", oge); 10 } 11 finally 12 { 13 if (s != null) 14 s.close(); 15 }
上面的代码段几乎存在于类的每个方法中,这违反了DRY原则 。将来如果需要改变检索Session
和 ObjectMap
实例的方式,或着某天这段代码被发现有缺陷,我们就不得不修改每个(包含这段代码的)方法,因此需要找到一种方式来复用这些执行代码。
重构后的代码
为了传递包含了原方法中业务逻辑的实例,我们创建一个带有抽象方法的 Executable
接口 。execute()
方法参数为我们欲操作的ObjectMap
实例。
1 interface Executable<T> { 2 public T execute(ObjectMap map) throws ObjectGridException; 3 }
由于我们的目的仅仅是在每个方法中操作ObjectMap
实例,可以创建executeWithMap()方法封装前述的那一大段重复代码。这个方法的参数是Executable
接口的实例,实例包含着操作map的必要逻辑(译者注:这样Executable接口的实例中就是纯粹的业务逻辑,实现了解耦合)。
1 private <T> T executeWithMap(Executable<T> ex) 2 { 3 Session s; 4 try { 5 s = oGrid.getSession(); 6 ObjectMap map = s.getMap(cacheName); 7 // Execute our business logic 8 return ex.execute(map); 9 } 10 catch (ObjectGridException oge) 11 { 12 throw new RuntimeException("Error performing cache operation", oge); 13 } 14 finally 15 { 16 if (s != null) 17 s.close(); 18 } 19 }
现在,可以用如下形式的模板代码替换掉第一个例子中的代码:这个模板创建了一个匿名内部类,实现了Executable接口和execute()方法。其中execute()
方法执行业务逻辑,并以getXXX()的方式返回结果(若为Void方法,返回null)
1 public V get(final K key) 2 { 3 return executeWithMap(new Executable<V>() { 4 public V execute(ObjectMap map) throws ObjectGridException 5 { 6 return (V) map.get(key); 7 } 8 }); 9 } 10 11 public void put(final K key, final V value) 12 { 13 executeWithMap(new Executable<Void>() { 14 public Void execute(ObjectMap map) throws ObjectGridException 15 { 16 map.upsert(key, value); 17 return null; 18 } 19 }); 20 } 21 22 public Map<K, V> getAll(Set<? extends K> keys) 23 { 24 final List<K> keyList = new ArrayList<K>(); 25 keyList.addAll(keys); 26 List<V> valueList = executeWithMap(new Executable<List<V>>() { 27 public List<V> execute(ObjectMap map) throws ObjectGridException 28 { 29 return map.getAll(keyList); 30 } 31 }); 32 33 Map<K, V> map = new HashMap<K, V>(); 34 for(int i = 0; i < keyList.size(); i++) { 35 map.put(keyList.get(i), valueList.get(i)); 36 } 37 return map; 38 }
FunctionalInterface Annotation (功能接口注释)
Java 8 的 @FunctionalInterface
annotation 使这一切变的简单。若某接口带有一个抽象方法,这个接口便可以被用作为lambda表达式的参数,称为功能接口。
1 @FunctionalInterface 2 interface Executable<T> { 3 public T execute(ObjectMap map) throws ObjectGridException; 4 }
只要接口仅仅包含一个抽象方法,便可以使用这个annotation。这样就能减少相当数量的模板代码。
1 public V get(final K key) 2 { 3 return executeWithMap((ObjectMap map) -> (V) map.get(key)); 4 } 5 6 public void put(final K key, final V value) 7 { 8 executeWithMap((ObjectMap map) -> { map.upsert(key, value); return null; }); 9 } 10 11 public Map<K, V> getAll(Set<? extends K> keys) 12 { 13 final List<K> keyList = new ArrayList<K>(); 14 keyList.addAll(keys); 15 List<V> valueList = executeWithMap((ObjectMap map) -> map.getAll(keyList)); 16 17 Map<K, V> map = new HashMap<K, V>(); 18 for(int i = 0; i < keyList.size(); i++) { 19 map.put(keyList.get(i), valueList.get(i)); 20 } 21 return map; 22 }
结论
实现这些重构我很开心。它比原始的代码略复杂一点,但是更简明,更DRY,所以一切都是值得的。 尽管还有提升的空间,但这是一个良好的开始。