如何在catch块中重复代码?

时间:2014-08-12 08:11:45

标签: java

我现在很难不在我正在研究的Java程序中重复自己。

说,我需要声明许多基本上按以下方式构建的方法:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
    EntityManager em = this.createEntityManager();

    EntityTransaction tx = null;
    try {

        /*
         * ... independent logic ...
         */

        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }

    return something;
}

所有方法的方法体都需要包含用于资源管理的元素。

“独立逻辑”本身也会相当复杂,因此将try / catch语句放在一个单独的方法中将无法正常工作。

我想避免重复此代码。在这些情况下应用哪些最佳实践?

9 个答案:

答案 0 :(得分:35)

创建一个界面:

public interface EntityManagerAction {
   public void execute(EntityManager em);
}

实用类:

public class EntityUtil {
  public static void executeWithEntityManager(EntityManagerAction action) {
    EntityManager em = someHowCreateEntityManager();

    EntityTransaction tx = null;
    try {
        action.execute(em);
        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }
  }
}

现在您可以在EntityUtil类中重复使用样板,并且代码变为:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
   Something something; 
   EntityUtil.executeWithEntityManager(new EntityManagerAction() {
        public void execute(EntityManager em ) {

        /*
         * ... independent logic ...
         */
         //use the passed in 'em' here.
        }
    });

    return something;
}

另见What is the "Execute Around" idiom?

答案 1 :(得分:8)

如果您的所有finally条款都用于关闭Stream等(实施AutoCloseable的任何内容),则可以使用try-with-resources(如其中一条所述)评论)摆脱finally条款。

但是,如果您需要更通用的解决方案,并且在Exception子句中捕获了相同类型的finally和相同类型的处理,则可以创建一个抽象类,例如:

abstract class SensitiveBlockHandler {
    public void handle() {
        try {
            doHandling();
        } catch (SomeException | AnotherException e) {
            // TODO: handle exceptions here ...
        } finally {
            // TODO: cleanup here ...
        }
    }

    protected abstract void doHandling();
}

然后,您可以创建内部类来处理不同的情况,或者作为匿名类。代码应该类似于:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
    new SensitiveBlockHandler() {
        protected void doHandling() {
            /*
             * ... independent logic ...
             */
        }
    }.handle();

    return something;
}

答案 2 :(得分:3)

我会创建一个独立逻辑的抽象,比如说Job,而doSomething()将成为processJob()类中的Service。您将针对每个处理调用processJob()以及示例中的所有代码,但independent logic只写一次。

修改 nos 在评论中建议的内容:What is the "Execute Around" idiom?

答案 3 :(得分:3)

我们最近遇到了这样的问题,并决定采用callback设计模式。

因此,如果所有方法都具有相似的代码并且它只是不同的独立逻辑,那么您可以创建一个实现处理独立代码的接口。所以像这样:

public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
       return (SomeEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl1());
}

public SomeOtherEntity doSomethingElse (String someAttribute, String anotherAttribute) {
    return (SomeOtherEntity)callbackMethod(someAttribute, anotherAttribute, new IndependentCodeInterfaceImpl2());
}

private Object callbackMethod(String someAttribute, String anotherAttribute, IndependentCodeInterface independent) {
    EntityManager em = this.createEntityManager();
    EntityTransaction tx = null;
    Object response = null;
    try {
        response = independent.execute(someAttribute, anotherAttribute, em);
        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }
    return response;
}

答案 4 :(得分:3)

如果您使用的是JavaEE应用程序服务器,那么使用无状态会话bean可以大大简化您的代码:

@Stateless
public class SomethingFactory {

    @PersistenceContext
    private EntityManager em;

    public SomeEntity doSomething (String someAttribute, String anotherAttribute) {
       /*
        * ... independent logic ...
        */
       return something;
    }

}

容器将为您管理所有事务管理语义。

答案 5 :(得分:2)

您可以使方法签名返回异常

 public SomeEntity doSomething (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}

 public SomeEntity doSomethingElse (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}

 public SomeEntity doSomethingDifferent (String someAttribute, String anotherAttribute) throws RuntimeException {
// your independent logic
}

然后你可以用一个单独的方法处理它:

   public String yourHandleMethod(){
String something = "";
EntityManager em = this.createEntityManager();

    EntityTransaction tx = null;
try{
 doSomething();
 doSomethingElse();
 doSomethingDifferent();
 tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }

    return something;

答案 6 :(得分:2)

您可以实现类似于Spring Transaction Template的机制。

首先,实现一个将为每个业务操作实现的回调,同时提供实体管理器和事务:

public static interface TransactionCallback<R> {
  R doInTransaction(EntityManager em, EntityTransaction tx);
}

然后,使用样板代码创建一个通用方法:

public <T> T execute(TransactionCallback<T> callback) {
  EntityManager em = this.createEntityManager();
  EntityTransaction tx = null;

  try {
    tx = em.getTransaction();
    return callback.doInTransaction(em, tx);
  } catch (RuntimeException e) {
    if (tx != null && tx.isActive()) { 
        tx.rollback();
    }
    throw e;
  } finally {
    em.close();
  }
}

最后,您可以创建类似于此的业务逻辑:

public SomeEntity doSomething(String someAttribute, String anotherAttribute) {
  return execute(new TransactionCallback<SomeEntity>() {
    @Override
    public SomeEntity  doInTransaction(EntityManager em, EntityTransaction tx) {
      // do something here
    }
  });
}

这种方法有几个优点。它很干净,您可以在一个地方拦截所有操作 - 例如:添加日志记录等。

我很抱歉任何语法错误,我在没有IDE的情况下编写它。但是你明白了。

编辑:也许在JDK 8中使用lambdas会更短,因为TransactionCallback可能是功能接口:)。

答案 7 :(得分:2)

为了完整性 - Template Method Pattern。它允许您从过程中抽象出所有管理代码,并只实现核心功能。当您有多个元需求(例如在正确处理异常时关闭内容)时,此模式特别有用。

我使用此模式运行数据库查询,因为所有规则都要确保关闭所有ResultSetConnection,同时仍能正确管理PreparedStatement

abstract class DoSomethingWithEntity {

    public SomeEntity doSomething(String someAttribute, String anotherAttribute) {
        SomeEntity something;
        EntityManager em = createEntityManager();

        EntityTransaction tx = null;
        try {
            tx = em.getTransaction();
            // Call the abstract stuff that can be different.
            something = doIt(em, tx);
        } catch (RuntimeException e) {
            if (tx != null && tx.isActive()) {
                tx.rollback();
            }
            throw e;
        } finally {
            em.close();
        }

        return something;
    }

    // The bit you want to write yourself. Make it abstract so you have to write it.
    abstract SomeEntity doIt(EntityManager em, EntityTransaction tx) throws Exception;
}

public void test() {
    SomeEntity e = new DoSomethingWithEntity() {

        @Override
        SomeEntity doIt(EntityManager em, EntityTransaction tx) {
            // Do your stuff here.
            return new SomeEntity();
        }

    }.doSomething("someAttribute", "anotherAttribute");
}

请注意,您不必将所有例外都设置为RuntimeException - 您可以明确定义允许抛出的异常。

答案 8 :(得分:1)

仅供参考 - JDK 1.8中新的lambda表达式功能现在是解决此问题的最佳方式。你的DoSomething方法就是这样的:

DataExecutor.execute(() -> {
    // let's say your "independent logic" is the following three statements
    statementA;
    statementB;
    statementC;
});

你的DataExecutor.execute方法看起来像这样(你可能想要一个返回类型):

public static void execute(work)
{
    EntityManager em = this.createEntityManager();

    EntityTransaction tx = null;
    try {

        // this is the "independent logic" you passed in via the "work" lambda
        work.doWork();

        tx = em.getTransaction();
    } catch (RuntimeException e) {
        if (tx != null && tx.isActive()) { 
            tx.rollback();
        }
        throw e;
    } finally {
        em.close();
    }

最后,您需要定义一个(或使用现有的)功能界面:

@FunctionalInterface
public interface DataFuncVoid {
    public abstract void doWork();
}