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"));            
        }   
        ...
    }
}

小结

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

原文地址:https://www.cnblogs.com/dt-zhw/p/5747255.html