業務場景
前端使用一個data屬性接收數據,而Highchart中對於不同的圖表類型,接收數據的數據格式是不同的。
柱狀圖類型需要的是
{
xData:["蘋果","香蕉","梨子","橘子"],
yData:[12,3,4,5]
}
那麼前端賦值將data.xData、data.yData賦值給對應屬性
而餅圖需要的數據格式是
{
{
name:"蘋果",
y:12
},
{
name:"香蕉",
y:3
},
{
name:"梨子",
y:4
},
{
name:"橘子",
y:5
},
}
這些圖表實例的數據都保存在一個數據庫表中,從表中取出來的數據的字段屬性都是一致的,僅通過一個圖表類型進行區分不同的策略類,具體策略會將同樣一份數據封裝爲不同的數據格式供前端使用
實現
(1)定義策略類父接口
public interface ChartStrategy {
Object recordHandleToDisplay(List<ChartRecord> records);
}
(2)具體策略類
柱狀圖
@Component("singleBaseChartStrategy")
public class SingleBaseChartStrategyImpl implements ChartStrategy{
@Autowired
private PctSeriesService pctSeriesService;
@Override
public Object recordHandleToDisplay(List<ChartRecord> records) {
Map<String,Object> dataMap = new HashMap<>(4);
//x軸信息集合(封裝x軸數據)
List<String> categories = new ArrayList<>();
//查詢出來該圖表下擁有的所有數據列信息
PctSeries pctSeries = pctSeriesService.selectBySingleChartId(records.get(0).getChartId());
//數據列裏的數據
List<Double> dataList = new ArrayList<>();
//將ChartRecord集合中的x、y軸數據存入集合
for (ChartRecord chartRecord:records) {
categories.add(chartRecord.getXValue());
dataList.add(chartRecord.getYValue());
}
//封裝series數據
List<Map<String,Object>> series = new ArrayList<>();
Map<String,Object> map = new HashMap<>(4);
map.put("name",pctSeries.getName());
map.put("colorByPoint",true);
map.put("data",dataList);
series.add(map);
dataMap.put("categories",categories);
dataMap.put("series",series);
return dataMap;
}
}
這裏是使用map進行數據封裝,其實最好應該定義一個業務領域模型類來替代Map,可以避免硬編碼。這個策略類裏data屬性是一個List<Double>格式
注意:該類使用@Component註解,以注入到IOC容器中
餅圖
@Component("basePieChartStrategy")
public class BasePieChartStrategyImpl implements ChartStrategy{
@Autowired
private PctSeriesService pctSeriesService;
@Override
public Object recordHandleToDisplay(List<ChartRecord> records) {
//指定哈希Map集合長度
int hashMapLength = 2;
//得到該餅圖的數據列
PctSeries pctSeries = pctSeriesService.selectBySingleChartId(records.get(0).getChartId());
//定義數據列集合
List<Map<String,Object>> seriesList = new ArrayList<>();
//定義數據列集合中的對象
Map<String, Object> seriesMap = new HashMap<>(hashMapLength);
//定義數據值集合
List<Map<String, Object>> data = new ArrayList<>();
//循環遍歷賦值
for (int i = 0; i < records.size(); i++) {
Map<String, Object> map = new HashMap<>(hashMapLength);
map.put("name", records.get(i).getXValue());
map.put("y", records.get(i).getYValue());
data.add(map);
}
seriesMap.put("name", pctSeries.getName());
seriesMap.put("data", data);
seriesList.add(seriesMap);
return seriesList;
}
}
這裏將數據分別存儲到name、y屬性中
(3)策略上下文
它維護所有具體策略類型,向外暴露一個訪問的接口,然後決定使用哪一種策略類
@Service("chartStrategyContext")
public class ChartStrategyContext {
private static final String NO_CHART_RECORDS = "未找到圖表數據";
private static final String NO_CHART_TYPE_ENUM = "未找到對應ChartTypeEnum實例";
private static final String NO_CHART_STRATEGY = "未找到對應圖表策略實例";
/**
* context維護所有策略實現對象集
*/
private final Map<String, ChartStrategy> strategyMap = new ConcurrentHashMap<>();
/**
*
*
* @date 14:15 2020/4/7
* @author 李文龍
* @param strategyMap: 容器中所有實現了ChartStrategy接口的實現類對象都會注入到該形參中
* k=beanId,v=全限定類名-實現類對象
* @return
**/
@Autowired
public ChartStrategyContext(Map<String, ChartStrategy> strategyMap) {
this.strategyMap.clear();
strategyMap.forEach((k, v) -> this.strategyMap.put(k, v));
}
public Map<String, ChartStrategy> getStrategyMap() {
return this.strategyMap;
}
/**
* 根據不同的圖表類型-typeEn值,去Context中獲取對應的策略實例
* 去對源數據-records進行處理
*
* @date 14:33 2020/4/7
* @author 李文龍
* @param typeEn: Chart.typeEn值。圖表類型
* @param records:圖表源數據,來自數據庫表:PCT_RECORD等表
* @return Object:不同的圖表需要不同格式數據
**/
public Object recordHandleToDisplay(Long typeEn, List<ChartRecord> records) {
ChartTypeEnum chartTypeEnum = ChartTypeEnum.getById(typeEn);
ChartStrategy strategy;
if(CollectionUtil.isEmpty(records)){
return NO_CHART_RECORDS;
}
if (chartTypeEnum == null) {
return NO_CHART_TYPE_ENUM;
}
strategy = strategyMap.get(chartTypeEnum.getBeanId());
if (strategy == null) {
return NO_CHART_STRATEGY;
}
return strategy.recordHandleToDisplay(records);
}
}
注意:所有的具體策略類都使用了@Component註解注入到IOC容器中,該上下文類也使用了@Service來將其注入到IOC中。而該類的構造器有一個strategyMap形參,