• 使用接口和泛型重构Java代码(译文)


    原文地址: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,所以一切都是值得的。 尽管还有提升的空间,但这是一个良好的开始。

  • 相关阅读:
    mybatis学习坑路
    一文理解class.getClassLoader().getResourceAsStream(file)和class.getResourceAsStream(file)区别
    servlet的坑
    class.getResource()方法的更新 坑
    utf8和字节数组的转换
    finally模块的各种情况
    C++ 动态对象数组的知识总结
    Notepad++正则表达式语法
    诸子百家55句
    给初始化为NULL的结构体指针赋值报错问题
  • 原文地址:https://www.cnblogs.com/LIS2011/p/3406181.html
Copyright © 2020-2023  润新知