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"));
}
...
}
}
小结
未完待续。
这个例子主要是进行对象的构造,是比较有通用性的。
我们可以看到,面向对象设计中,主要是靠不同对象的特性来实现变化。
而对于对象的构造会需要一些规则来驱动,这种规则我们一般抽象为接口来标识,
处理这些规则的往往都是工厂方法,也是工厂的一个非常重要的作用。