科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道为你的基于SPRING的应用增加简单规则引擎2

为你的基于SPRING的应用增加简单规则引擎2

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

这篇文章解释了在J2EE应用中规则引擎及声明性业务逻辑的优点,并且描述如何为流行的Spring框架开发简单的规则引擎。此文需要读者对Spring有基本的了解。

来源:IT专家网 2008年5月31日

关键字: 引擎 应用 Spring java

  • 评论
  • 分享微博
  • 分享邮件
在操作中的基于Spring的规则引擎

  让我们看一下这个框架如何工作的具体实例吧。想象下面的用例:我们需要开发负责贷款申请的应用程序。我们需要满足下面的条件:

  ●检查应用的完整性否则驳回
  ●检查应用是否来自我们授权处理业务的应用。
  ●检查申请者的月收支比是否满足我们的要求。
  ●输入的申请通过我们不知道实现细节的持久服务被存储在数据库中,我们只知道他的接口(可能这个开发被外包到印度了)
  ●业务规则是可以改变的,这也是为什么需要规则引擎的设计了。

  首先,设计一个表示贷款申请的类:

public class LoanApplication {
  
   public static final String INVALID_STATE = "Sorry we are not doing business in your state";
   public static final String INVALID_INCOME_EXPENSE_RATIO = "Sorry we cannot provide the loan given this expense/income ratio";
   public static final String APPROVED = "Your application has been approved";
   public static final String INSUFFICIENT_DATA = "You did not provide enough information on your application";
   public static final String INPROGRESS = "in progress";
  
   public static final String[] STATUSES =
      new String[] {
         INSUFFICIENT_DATA, INVALID_INCOME_EXPENSE_RATIO, INVALID_STATE, APPROVED, INPROGRESS
      };

   private String firstName;
   private String lastName;
   private double income;
   private double expences;
   private String stateCode;
   private String status;
  
   public void setStatus(String status) {
      if(!Arrays.asList(STATUSES).contains(status))
         throw new IllegalArgumentException("invalid status:" + status);
      this.status = status;
   }

// 其他getters and setters已被省略

}

  我们使用的持久服务拥有如下接口:

public interface LoanApplicationPersistenceInterface {
   public void recordApproval(LoanApplication application) throws Exception;
   public void recordRejection(LoanApplication application) throws Exception;
   public void recordIncomplete(LoanApplication application) throws Exception;
}

  我们迅速开发一个什么也不做只是用来满足接口约定的MockLoanApplicationPersistence类来欺骗接口。

  我们使用下面的SpringRuleEngine类的子类来加载Spring上下文并开始处理:

public class LoanProcessRuleEngine extends SpringRuleEngine {
   public static final SpringRuleEngine getEngine(String name) {
      ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("SpringRuleEngineContext.xml");
      return (SpringRuleEngine) context.getBean(name);
   }
}

这时候,我们已经有了代码框架了,因此是时候写JUnit测试了,代码如下。其中包含一些假设:我们期望公司仅在两种州运作,德克萨斯和密歇根。而且我们只接受收支比在70%或更好的人的贷款申请。

public class SpringRuleEngineTest extends TestCase {

   public void testSuccessfulFlow() throws Exception {
      SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor");
      LoanApplication application = new LoanApplication();
      application.setFirstName("John");
      application.setLastName("Doe");
      application.setStateCode("TX");
      application.setExpences(4500);
      application.setIncome(7000);
      engine.processRequest(application);
      assertEquals(LoanApplication.APPROVED, application.getStatus());
   }
  
   public void testInvalidState() throws Exception {
      SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor");
      LoanApplication application = new LoanApplication();
      application.setFirstName("John");
      application.setLastName("Doe");
      application.setStateCode("OK");
      application.setExpences(4500);
      application.setIncome(7000);
      engine.processRequest(application);
      assertEquals(LoanApplication.INVALID_STATE, application.getStatus());
   }
  
   public void testInvalidRatio() throws Exception {
      SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor");
      LoanApplication application = new LoanApplication();
      application.setFirstName("John");
      application.setLastName("Doe");
      application.setStateCode("MI");
      application.setIncome(7000);
      application.setExpences(0.80 * 7000); //too high     
      engine.processRequest(application);
      assertEquals(LoanApplication.INVALID_INCOME_EXPENSE_RATIO, application.getStatus());
   }
  
   public void testIncompleteApplication() throws Exception {     
      SpringRuleEngine engine = LoanProcessRuleEngine.getEngine("SharkysExpressLoansApplicationProcessor");
      LoanApplication application = new LoanApplication();
      engine.processRequest(application);
      assertEquals(LoanApplication.INSUFFICIENT_DATA, application.getStatus());
   }

  显然单元测试会失败因为我们还没有实现任何的逻辑。然而,随着项目的进展,越来越多的测试通过,最后JUnit测试就全部通过了。

  让我们继续操作和规则的实现。我们希望操作与持久服务交互因此我们需要一个通用基类:

public abstract class AbstractPersistenceAwareAction extends AbstractAction {
   private LoanApplicationPersistenceInterface persistenceService;
   public void setPersistenceService(LoanApplicationPersistenceInterface persistenceService) {
      this.persistenceService = persistenceService;
   }
   public LoanApplicationPersistenceInterface getPersistenceService() {
      return persistenceService;
   }
}

  在我们的进程中第一个请求贷款申请的业务规则已经完成:

public class ValidApplicationRule extends AbstractRule {
   protected boolean makeDecision(Object arg) throws Exception {
      LoanApplication application = (LoanApplication) arg;
      if(application.getExpences() == 0 ||
            application.getFirstName() == null ||
            application.getIncome() == 0 ||
            application.getLastName() == null ||
            application.getStateCode() == null) {
         application.setStatus(LoanApplication.INSUFFICIENT_DATA);
         return false;
      }
      return true;
   }
}

  注意这个类的一些有趣的现象:他是完全自包含的。他可以由自己实例化或者任何外部的应用容器,而类中的逻辑可以独立的开发和测试。这些现象使类成为基于规则应用的完美的程序块。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章