模板模式与适配器模式组合使用之合并站点数据
考虑一下这样的一个场景,网站模板应用到网站时需要合并站点数据。
站点数据(SiteData)是由页面(SitePageData)组成的,页面是由页面片段(SiteSegmentData)组成的,页面片段是由区域(SiteRegionData)组成的,区域是由版块(SiteAppData)组成的。
第一方案
优点
思路清晰,
缺点
面向过程,合并过程逻辑十分相似,可以认为是重复代码。
第二方案
从上面方案中总结出来,需要面向对象,于是我认为数据具备合并功能,在SiteData、SitePageData、SiteSegmentData、SiteRegionData、SiteAppData类去实现合并接口DataMergeable。
调用通用合并逻辑
优点
上面这种方案已经解决了面向过程及重复代码的问题
缺点
逻辑侵入了领域模型
第三种方案
同样是DataMergable接口不变
同时写一个通用逻辑实现此接口的类叫CommonDataMerge
为SiteData、SitePageData、SiteSegmentData、SiteRegionData、SiteAppData写个适配器用于实现DataMergable接口
优点
合并代码不仅采用面向对象方法,抽象出合并的逻辑进行复用。并且无侵入原来的领域模型。可以把合并逻辑放在业务逻辑包,而不需要放到领域逻辑包里,从而解除了领域模型与数据合并的耦合代码。
使用了组合模式与适配器模式
缺点
多出几个类,并出现一些强制转换类型的代码
考虑一下这样的一个场景,网站模板应用到网站时需要合并站点数据。
站点数据(SiteData)是由页面(SitePageData)组成的,页面是由页面片段(SiteSegmentData)组成的,页面片段是由区域(SiteRegionData)组成的,区域是由版块(SiteAppData)组成的。
class SiteData{
List<SitePageData> pages;
}
class SitePageData{
List<SiteSegmentData> segments;
}
class SiteSegmentData{
List<SiteRegionData> regions;
}
class SiteRegionData{
List<SiteAppData> apps;
}
第一方案
public class SiteDataUtil {
public static void merge(SiteData from, SiteData to) {
// 判断是否需要合并
if (from != null && from.getPages() != null && !from.getPages().isEmpty()) {
// 如果要被合并的数据列表为空 , 则替换要合并过来的数据列表
if (to.getPages() == null) {
to.setPages(from.getPages());
return;
}
// 要被合并的数据列表
List<SitePageData> mergeToDatas = to.getPages();
// 要合并过来的数据列表
List<SitePageData> mergeFromDatas = from.getPages();
// 构造一个map进行一一对应合并
Map<String, SitePageData> map = new HashMap<String, SitePageData>();
// 循环当前每个数据到map中
for (SitePageData dataTo : mergeToDatas) {
String key = dataTo.getName();
map.put(key, dataTo);
}
// 循环每个要合并过来的每个数据, 一一对应地合并
for (SitePageData dataFrom : mergeFromDatas) {
String key = dataFrom.getName();
SitePageData dataTo = map.get(key);
// 如果map中存在相同key的数据,则进行合并
if (dataTo != null) {
merge(dataFrom, dataTo);
// 如果map中不存在则直接添加来源的data
} else {
to.getPages().add(dataFrom);
}
}
}
}
public static void merge(SitePageData from, SitePageData to) {
// 判断是否需要合并
if (from != null && from.getSegments() != null && !from.getSegments().isEmpty()) {
// 如果要被合并的数据列表为空 , 则替换要合并过来的数据列表
if (to.getSegments() == null) {
to.setSegments(from.getSegments());
return;
}
// 要被合并的数据列表
List<SiteSegmentData> mergeToDatas = to.getSegments();
// 要合并过来的数据列表
List<SiteSegmentData> mergeFromDatas = from.getSegments();
// 构造一个map进行一一对应合并
Map<String, SiteSegmentData> map = new HashMap<String, SiteSegmentData>();
// 循环当前每个数据到map中
for (SiteSegmentData dataTo : mergeToDatas) {
String key = dataTo.getLayoutId();
map.put(key, dataTo);
}
// 循环每个要合并过来的每个数据, 一一对应地合并
for (SiteSegmentData dataFrom : mergeFromDatas) {
String key = dataFrom.getLayoutId();
SiteSegmentData dataTo = map.get(key);
// 如果map中存在相同key的数据,则进行合并
if (dataTo != null) {
merge(dataFrom, dataTo);
// 如果map中不存在则直接添加来源的data
} else {
to.getSegments().add(dataFrom);
}
}
}
}
public static void merge(SiteSegmentData from, SiteSegmentData to) {
//合并segment
}
public static void merge(SiteRegionData from, SiteRegionData to) {
//合并region
}
}
优点
思路清晰,
缺点
面向过程,合并过程逻辑十分相似,可以认为是重复代码。
第二方案
从上面方案中总结出来,需要面向对象,于是我认为数据具备合并功能,在SiteData、SitePageData、SiteSegmentData、SiteRegionData、SiteAppData类去实现合并接口DataMergeable。
public interface DataMergable {
/**
* 合并数据
* @param from 执行数据合并的来源
*/
void mergeFrom(DataMergable from);
/**
* 获取数据源的子集
* @return
*/
List<DataMergable> getChildren();
/**
* 替换数据源的子集
* @param list
*/
void setChildren(List<DataMergable> children);
/**
* 添加数据源的子数据
* @param child
*/
void addChild(DataMergable child);
/**
* 获取标示数据源的唯一key
* @return
*/
String getKey();
}
SiteData实现接口实例,其它类同
public class SiteData implements DataMergable {
List<SitePageData> pages;
public void mergeFrom(DataMergable from) {
DataMergeUtil.merge(from, this);
}
public List<DataMergable> getChildren() {
List<DataMergable> result = new ArrayList<DataMergable>();
for (SitePageData page : pages) {
result.add(page);
}
return result;
}
public void setChildren(List<DataMergable> children) {
List<SitePageData> result = new ArrayList<SitePageData>();
for (DataMergable page : pages) {
result.add((SitePageData)page);
}
this.pages = result;
}
public void addChild(DataMergable child) {
this.addChild((SitePageData)child);
}
public String getKey() {
return this.getKey();
}
}
调用通用合并逻辑
public class DataMergeUtil {
public static void merge(DataMergable from, DataMergable to) {
// 判断是否需要合并
if (from != null && from.getChildren() != null && !from.getChildren().isEmpty()) {
// 如果要被合并的数据列表为空 , 则替换要合并过来的数据列表
if (to.getChildren() == null) {
to.setChildren(from.getChildren());
return;
}
// 要被合并的数据列表
List<DataMergable> mergeToDatas = to.getChildren();
// 要合并过来的数据列表
List<DataMergable> mergeFromDatas = from.getChildren();
// 构造一个map进行一一对应合并
Map<String, DataMergable> map = new HashMap<String, DataMergable>();
// 循环当前每个数据到map中
for (DataMergable dataTo : mergeToDatas) {
String key = dataTo.getKey();
map.put(key, dataTo);
}
// 循环每个要合并过来的每个数据, 一一对应地合并
for (DataMergable dataFrom : mergeFromDatas) {
String key = dataFrom.getKey();
DataMergable dataTo = map.get(key);
// 如果map中存在相同key的数据,则进行合并
if (dataTo != null) {
dataTo.mergeFrom(dataFrom);
// 如果map中不存在则直接添加来源的data
} else {
to.addChild(dataFrom);
}
}
}
}
}
优点
上面这种方案已经解决了面向过程及重复代码的问题
缺点
逻辑侵入了领域模型
第三种方案
同样是DataMergable接口不变
同时写一个通用逻辑实现此接口的类叫CommonDataMerge
为SiteData、SitePageData、SiteSegmentData、SiteRegionData、SiteAppData写个适配器用于实现DataMergable接口
public abstract class CommonDataMerge implements DataMergable {
/**
* 合并数据通用逻辑
*
* @param from 数据来源
*/
public void mergeFrom(DataMergable from) {
// 判断是否需要合并
if (from != null && from.getChildren() != null && !from.getChildren().isEmpty()) {
// 如果要被合并的数据列表为空 , 则替换要合并过来的数据列表
if (this.getChildren() == null) {
this.setChildren(from.getChildren());
return;
}
// 要被合并的数据列表
List<DataMergable> mergeToDatas = this.getChildren();
// 要合并过来的数据列表
List<DataMergable> mergeFromDatas = from.getChildren();
// 构造一个map进行一一对应合并
Map<String, DataMergable> map = new HashMap<String, DataMergable>();
// 循环当前每个数据到map中
for (DataMergable dataTo : mergeToDatas) {
String key = dataTo.getKey();
map.put(key, dataTo);
}
// 循环每个要合并过来的每个数据, 一一对应地合并
for (DataMergable dataFrom : mergeFromDatas) {
String key = dataFrom.getKey();
DataMergable dataTo = map.get(key);
// 如果map中存在相同key的数据,则进行合并
if (dataTo != null) {
dataTo.mergeFrom(dataFrom);
// 如果map中不存在则直接添加来源的data
} else {
this.addChild(dataFrom);
}
}
}
}
}
public class SiteDataMergeAdapter extends CommonDataMerge {
private SiteData data;
public SiteDataMergeAdapter(SiteData data){
super();
this.data = data;
}
@Override
public List<DataMergable> getChildren() {
List<DataMergable> children = new ArrayList<DataMergable>();
for (SitePageData sitePageData : data.getPages()) {
children.add(new SitePageDataMergeAdapter(sitePageData));
}
return children;
}
@Override
public void setChildren(List<DataMergable> children) {
List<SitePageData> list = new ArrayList<SitePageData>();
for (DataMergable dataMergable : children) {
list.add(((SitePageDataMergeAdapter) dataMergable).getData());
}
data.setPages(list);
}
@Override
public void addChild(DataMergable child) {
data.getPages().add(((SitePageDataMergeAdapter) child).getData());
}
@Override
public String getKey() {
return data.getSiteKey();
}
public SiteData getData() {
return data;
}
}
优点
合并代码不仅采用面向对象方法,抽象出合并的逻辑进行复用。并且无侵入原来的领域模型。可以把合并逻辑放在业务逻辑包,而不需要放到领域逻辑包里,从而解除了领域模型与数据合并的耦合代码。
使用了组合模式与适配器模式
缺点
多出几个类,并出现一些强制转换类型的代码