Spark小课堂Week7 从Spark中一个例子看面向对象设计

Spark小课堂Week7

从Spark中一个例子看面向对象设计

今天我们讨论了个问题,来设计一个Spark中的常用功能。

功能描述:数据源是一切处理的源头,这次要实现下加载数据源的方法load()

初始需求

需求:支持Json数据源加载

具体:输入一个path,需要返回一个Relation,

Relation中提供scan()和write()两个方法

示意代码:

class Context{
public Relation json(String path){
return new Relation(path);
}
} class Relation{
public Relation(path);
public scan();
public write();
}

第一次需求变化

现在需要添加jdbc数据源,输入的是url和tableName,需要有自己的Relation实现

简单实现:

class Context{
public JsonRelation json(String path){
return new JsonRelation(path);
}
public JdbcRelation jdbc(String url,String tableName){
return new JdbcRelation(url,tableName);
}
} class JsonRelation{
public JsonRelation(String path);
public scan();
public write();
}
class JdbcRelation{
public JdbcRelation(String url,String tableName);
public scan();
public write();
}

这个实现明显违反了开放封闭原则,增加一种文件格式,需要对核心类Context进行修改!!!

方法整合

所以首先,需要对json和jdbc方法进行合并为format,参数也进行合并,

source参数表示数据源,是一个字符串,比如:"json"、"jdbc"

逻辑整合起来

class Context{
public Relation format(String source,Map param){
if(source == "json") return new JsonRelation(param.get("path"));
if(source == "jdbc") return new JdbcRelation(param.get("url"),param.get("tableName"));
...
}
} class JsonRelation extends Relation{
public JsonRelation(String path);
public scan();
public write();
}
class JdbcRelation extends Relation{
public JdbcRelation(String url,String tableName);
public scan();
public write();
}

方法通用化。

format方法还是违反了开放封闭原则。我们可以使用反射对其进行通用化。

class Context{
public Relation format(String source,Map param){
Class c = Class.forName(source + "Relation");
Constructor constructor= c.getDeclaredConstructor(Map.class);
return constructor.newInstance(param);
...
}
}
class JsonRelation extends Relation{
public JsonRelation(Map param){
this.path = param.get("path");
};
public scan();
public write();
}
class JdbcRelation extends Relation{
public JdbcRelation(Map param){
this.url = param.get("url");
this.tableName = param.get("tableName");
}
public scan();
public write();
}

第二次需求变化

需求:再增加一种文件类型csv,同样是输入path

按照之前的思路,我们可以增加一个Relation类

class CsvRelation extends Relation{
public CsvRelation(Map param){
this.path = param.get("path");
};
public scan();
public write();
}

这里有一个问题,我们其发现构造方法和JsonRelation是完全重复的,有没有办法消除这种重复

消除重复,第一点是需要把重复的部分拆离出来。我们把Relation类拆分为Relation类和Provider类。其中Provider是一个工厂方法,通过反射来调用。

class Context{
public Relation format(String source,Map param){
Class c = Class.forName(source + "Provider");
Method method= c.getMethod("getRelation",Map.class);
return method.invoke(null,param);
...
}
}
class JsonProvider{
public static createRelation(Param param){
return new JSonRelation(param.get("path"));
};
}
class JsonRelation extends Relation{
public JsonRelation(String path);
public scan();
public write();
}
class CsvProvider{
public static createRelation(Param param){
return new CsvRelation(param.get("path"));
};
}
class CsvRelation extends Relation{
public CsvRelation(String path);
public scan();
public write();
}
class JdbcProvider{
public static Relation createRelation(Param param){
return new JdbcRelation(param.get("url"),param.get("tableName"));
};
}
class JdbcRelation extends Relation{
public JdbcRelation(String path,String tableName);
public scan();
public write();
}

Json和csv采用path构造,而jdbc使用url+table,这个可以认为是一个固有的规则,我们可以把构造中间重复的逻辑再单独抽取出来

class Context{
public Relation format(String source,Map param){
Class c = Class.forName(source + "Provider");
if(hasIntereface(c,PathProvider.class)){
Method method= c.getMethod("getRelation",String.class);
return method.invoke(null,param.get("path"));
}
if(hasIntereface(c,UrlProvider.class)){
Method method= c.getMethod("getRelation",String.class,String.class);
return method.invoke(null,param.get("url"),param.get("tableName"));
}
...
}
}
class JsonProvider implements PathProvider{
public static createRelation(String path){
return new JSonRelation(path);
};
}
class CsvProvider implements PathProvider{
public static createRelation(String path){
return new CsvRelation(path);
};
}
class JdbcProvider implements UrlProvider{
public static Relation createRelation(String url,String tableName){
return new JdbcRelation(url,tableName);
};
}

然后,我们发现format方法中的实现有些太复杂,所以单独抽取出来

class Context{
public Relation format(String source,Map param){
return Provider.getRelation(source,param);
}
}
class Provider{
public Relation getRelation(String source,Param,param){
Class c = Class.forName(source + "Provider");
if(hasIntereface(c,PathProvider.class)){
Method method= c.getMethod("getRelation",String.class);
return method.invoke(null,param.get("path"));
}
if(hasIntereface(c,UrlProvider.class)){
Method method= c.getMethod("getRelation",String.class,String.class);
return method.invoke(null,param.get("url"),param.get("tableName"));
}
...
}
}

小结

未完待续。

这个例子主要是进行对象的构造,是比较有通用性的。

我们可以看到,面向对象设计中,主要是靠不同对象的特性来实现变化。

而对于对象的构造会需要一些规则来驱动,这种规则我们一般抽象为接口来标识,

处理这些规则的往往都是工厂方法,也是工厂的一个非常重要的作用。

上一篇:【百度地图API】建立全国银行位置查询系统(三)——如何在地图上添加银行标注


下一篇:2021-10-13