模块的可插拔设计(策略模式)

2019-04-13 15:38发布

业务背景:有一个套餐,里面有多个模快组成,现固定了4种套餐,套餐中的模快有重复,例如
模快:A,B,C,D
套餐:甲:A
          乙:A,B
          丙:A,C
          丁:A,B,C,D
现要求,使用者可以根据自己的需求制定专属套餐,而不是局限于在上述四个套餐中进行选择,这样,就需要我们做到套餐中模快的可插拔设计,动态的组合。看一下具体的uml设计图对每个模快,我们都要有一个module实体,一个strategy首先是module实体:
@Entity @Table(name = "module_education") public class EducationModule extends BaseModule { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; /** * 姓名 */ @Column(name = "name") private String name; /** * 毕业结论 */ @Column(name = "study_result") private String studyResult; /** * 毕业时间 */ @Column(name = "graduate_time") private String graduateTime; /** * 学校 */ @Column(name = "college") private String college; /** * 学历 */ @Column(name = "degree") private String degree; /** * 院校类型 */ @Column(name = "college_type") private String collegeType; public EducationModule() { } public EducationModule(Color color) { super(color); } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getStudyResult() { return studyResult; } public void setStudyResult(String studyResult) { this.studyResult = studyResult; } public String getGraduateTime() { return graduateTime; } public void setGraduateTime(String graduateTime) { this.graduateTime = graduateTime; } public String getCollege() { return college; } public void setCollege(String college) { this.college = college; } public String getDegree() { return degree; } public void setDegree(String degree) { this.degree = degree; } public String getCollegeType() { return collegeType; } public void setCollegeType(String collegeType) { this.collegeType = collegeType; } }用于记录使用了改模快后产生的数据,保存到数据库public class EducationModuleStrategy extends SingleAPIModuleStrategy { private static final Logger logger = LoggerFactory.getLogger(EducationModuleStrategy.class); private final static long API_ID = 9L; public EducationModuleStrategy(Report report, SupplyAPIRepository supplyAPIRepository, APISearchRepository apiSearchRepository) { super(report, supplyAPIRepository, apiSearchRepository); } @Override protected void setAPI() { super.api = supplyAPIRepository.findOne(API_ID); } @Override protected boolean isParameterComplete() { return super.report.getCustomerName() != null && super.report.getCustomerIdCard() != null; } @Override public boolean fetchData(Map pool) { JSONObject cacheData = pool.get(api.getCode()); if (cacheData != null && !EMPTY_JSON.equals(cacheData)) { super.apiResponse = cacheData; return true; } API api = new API(); api.setSupplyAPI(super.api); api.getParameters().put("name", super.report.getCustomerName()); api.getParameters().put("idcard", super.report.getCustomerIdCard()); cacheData = getCache(api); if (cacheData != null) { logger.info("缓存查询" + super.api.getCode()); } else { logger.info("即时查询" + super.api.getCode()); YoufenCallable youfenCallable = new YoufenCallable(api.getSupplyAPI(), new HashMap<>(), api.getParameters(), new HashMap<>()); cacheData = youfenCallable.call(); logger.info(super.api.getCode() + " response:" + cacheData); if (cacheData != null && "0000".equals(cacheData.getString("resCode")) && ("2012".equals((cacheData.getJSONObject("data")).getString("statusCode")) || "2007".equals((cacheData.getJSONObject("data")).getString("statusCode")))) { putCache(api, cacheData); } } if (cacheData == null) { return false; } else { super.apiResponse = cacheData; return true; } } @Override public BaseModule analyseData() { String name = super.report.getCustomerName(); String idCrad = super.report.getCustomerIdCard(); EducationModule educationModule = new EducationModule(); if (apiResponse == null) { educationModule.setColor(Color.TIMEOUT); } else { if ("0000".equals(apiResponse.getString("resCode"))) { JSONObject data = apiResponse.getJSONObject("data"); if ("2012".equals(data.getString("statusCode"))) { educationModule.setColor(Color.SUCCESS); JSONObject result = data.getJSONObject("result"); educationModule.setName(name); educationModule.setStudyResult(result.getString("studyResult")); educationModule.setGraduateTime(result.getString("graduateTime")); educationModule.setCollege(result.getString("college")); educationModule.setDegree(result.getString("degree")); educationModule.setCollegeType(result.getString("collegeCategory")); } else if ("2007".equals(data.getString("statusCode"))) { educationModule.setColor(Color.SUCCESS); } else { educationModule.setColor(Color.ERROR); } } else { educationModule.setColor(Color.ERROR); } } return educationModule; } @Override public void setModuleIntoReport(BaseModule module, Integer displayOrder) { EducationModule educationModule = (EducationModule) module; educationModule.setDisplaySort(displayOrder); super.report.setEducationModule(educationModule); } }其中主要的两个方法是用来抓取数据和分析数据的那么我们如何来使用上述的module实体和strategy呢,这里我们建了一个ModuleCallable类,继承了Callable
public class ModuleCallable implements Callable { private static final Logger logger = LoggerFactory.getLogger(ModuleCallable.class); private BaseStrategy strategy; private BaseStrategy spareStrategy; private Map pool; private final Lock lock; private Integer displayOrder; public ModuleCallable(BaseStrategy strategy, BaseStrategy spareStrategy, Map pool, Lock lock, Integer displayOrder) { this.strategy = strategy; this.spareStrategy = spareStrategy; this.pool = pool; this.lock = lock; this.displayOrder = displayOrder; } @Override public BaseModule call() throws Exception { BaseModule result = null; if (strategy != null && strategy.isAPIActive()) { boolean isWaitForOther; do { lock.lock(); isWaitForOther = strategy.isContainsAPI(pool) && strategy.isAPIUnfinished(pool); if (isWaitForOther) { lock.unlock(); try { logger.info(strategy.getClass().getSimpleName() + "其他线程在查询主接口,休眠1s"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } else { strategy.tryPutEmptyAPI(pool); lock.unlock(); } } while (isWaitForOther); try { if (strategy.fetchData(pool)) { strategy.putAPIResponseIntoPool(pool); } else { strategy.removeEmptyAPI(pool); } } catch (Exception e) { //如果发生异常的话,从pool里移出空的api strategy.removeEmptyAPI(pool); } result = strategy.analyseData(); if (result != null) { strategy.setModuleIntoReport(result, displayOrder); } } // 如果结果为null且备用接口可用 if (spareStrategy != null && result == null && spareStrategy.isAPIActive()) { boolean isWaitForOther; do { lock.lock(); isWaitForOther = spareStrategy.isContainsAPI(pool) && !spareStrategy.isAPIUnfinished(pool); if (isWaitForOther) { lock.unlock(); try { logger.info(spareStrategy.getClass().getSimpleName() + "其他线程在查询备用接口,休眠1s"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } else { spareStrategy.tryPutEmptyAPI(pool); lock.unlock(); } } while (isWaitForOther); try { if (spareStrategy.fetchData(pool)) { spareStrategy.putAPIResponseIntoPool(pool); } else { spareStrategy.removeEmptyAPI(pool); } } catch (Exception e) { //如果发生异常的话,从pool里移出空的api spareStrategy.removeEmptyAPI(pool); } result = spareStrategy.analyseData(); if (result != null) { spareStrategy.setModuleIntoReport(result, displayOrder); } } return result; } }如果对于一个模块,我们有多个方案选择(或者说是主方案无法正常工作了,要启用备用方案),那么我们就要再写一个module+strategy来充当备用方案,写法与主方案相同。