Spark涉及的设计模式-创建型-工厂方法模式
说在前面的话:
说到工厂方法模式不得不说的就是简单工厂模式(又名静态方法模式),简单工厂模式就是他的行为就很简单,就是定义一个接口用来创建对象。但是它创建工厂类的时候是通过客户端传入参数进行决定创建什么工厂的。这样如果需要添加一个工厂那么就需要改变这个工厂类。这样就违背了开闭原则。而工厂方法模式则不通,他是通过客户端就决定实例化哪个工厂进行决定的,这样需要修改的是客户端和添加新的工厂类
这里我们以RPC为参考
工厂接口
工厂方法模式肯定是要继承一个工厂接口的,在SparkRPC的工厂方法中也不会例外,当然这里的接口是特质,特质和类的不同就是一个子类中可以混入多个特质
/**
* A factory class to create the [[RpcEnv]]. It must have an empty constructor so that it can be
* created using Reflection.
*/
private[spark] trait RpcEnvFactory {
def create(config: RpcEnvConfig): RpcEnv
}
工厂实现
继承这个特质的类仅有NettyRpcEnvFactory
,也就是RpcEnvFactory
仅有一个工厂实现
private[rpc] class NettyRpcEnvFactory extends RpcEnvFactory with Logging {
def create(config: RpcEnvConfig): RpcEnv = {
val sparkConf = config.conf
// Use JavaSerializerInstance in multiple threads is safe. However, if we plan to support
// KryoSerializer in future, we have to use ThreadLocal to store SerializerInstance
val javaSerializerInstance =
new JavaSerializer(sparkConf).newInstance().asInstanceOf[JavaSerializerInstance]
val nettyEnv =
new NettyRpcEnv(sparkConf, javaSerializerInstance, config.advertiseAddress,
config.securityManager, config.numUsableCores)
if (!config.clientMode) {
val startNettyRpcEnv: Int => (NettyRpcEnv, Int) = { actualPort =>
nettyEnv.startServer(config.bindAddress, actualPort)
(nettyEnv, nettyEnv.address.port)
}
try {
Utils.startServiceOnPort(config.port, startNettyRpcEnv, sparkConf, config.name)._1
} catch {
case NonFatal(e) =>
nettyEnv.shutdown()
throw e
}
}
nettyEnv
}
}
客户端
有了工厂之后,使用改工厂的类也就是客户端,就可以很方便地就实例化一个RpcEnv的产品。下面看一下具体的客户端代码。也就是最后代码中的new NettyRpcEnvFactory().create(config)
,这里创建了一个RpcEnv的Rpc通信环境。
private[spark] object RpcEnv {
def create(
name: String,
host: String,
port: Int,
conf: SparkConf,
securityManager: SecurityManager,
clientMode: Boolean = false): RpcEnv = {
create(name, host, host, port, conf, securityManager, 0, clientMode)
}
def create(
name: String,
bindAddress: String,
advertiseAddress: String,
port: Int,
conf: SparkConf,
securityManager: SecurityManager,
numUsableCores: Int,
clientMode: Boolean): RpcEnv = {
val config = RpcEnvConfig(conf, name, bindAddress, advertiseAddress, port, securityManager,
numUsableCores, clientMode)
new NettyRpcEnvFactory().create(config)
}
}
为什么不用抽象工厂模式
那么思考一下这里为什么使用的是工厂方法模式而不是抽象工厂模式
这是因为抽象工厂模式支持的场景不同。
抽象工厂模式的产生是解决了工厂方法模式中每个工厂仅仅生产一个产品,如果存在需要两个产品混合的产品那么就需要再次创建工厂如此这样造成的工厂类冗杂问题。但是此例中我们不需要其他的产品。既是是SparkRpc从Akka转移到Netty中时也仅仅需要创建新的实现工厂而不是进行产品的整合。
就需要再次创建工厂如此这样造成的工厂类冗杂问题。但是此例中我们不需要其他的产品。既是是SparkRpc从Akka转移到Netty中时也仅仅需要创建新的实现工厂而不是进行产品的整合。
此场景中NettyRpcEnv的职责就仅仅是生产RpcEnv,而在Spark架构中我们需要的也仅仅是RpcEnv这一个产品并不需要其他的Rpc产品。