简述

工厂方法模式(Factory Method Pattern)跟上一篇讨论的建造者模式类似,都是将对象创建的逻辑封装起来,为使用者提供一个简单易用的对象创建接口。两者在应用场景上稍有区别,建造者模式常用于需要传递多个参数来进行实例化的场景;工厂方法模式常用于不指定对象具体类型的情况下创建对象的场景。

UML 结构

代码实现

示例

SidecarSidecarSocket
SidecarSocketSidecarSocket
// demo/network/http/http_client.go
package http

// 创建一个新的HTTP客户端,以Socket接口作为入参
func NewClient(socket network.Socket, ip string) (*Client, error) {
  ... // 一些初始化逻辑
	return client, nil
}

// 使用NewClient时,我们可以传入Sidecar来给Http客户端附加额外的流控功能
client, err := http.NewClient(sidecar.NewFlowCtrlSidecar(network.DefaultSocket()), "192.168.0.1")
http.NewClient
type ServiceMediator struct {
  ...
	server *http.Server
}

// Forward 转发请求,请求URL为 /{serviceType}+ServiceUri 的形式,如/serviceA/api/v1/task
func (s *ServiceMediator) Forward(req *http.Request) *http.Response {
    ...
    // 发现下游服务的目的IP地址
    dest, err := s.discovery(svcType)
    // 创建HTTP客户端,硬编码sidecar.NewFlowCtrlSidecar(network.DefaultSocket())
    client, err := http.NewClient(sidecar.NewFlowCtrlSidecar(network.DefaultSocket()), s.localIp)
    // 通过HTTP客户端转发请求
    resp, err := client.Send(dest, forwardReq)
    ...
}
http.NewClientsidecar.NewFlowCtrlSidecar(network.DefaultSocket())Sidecar
ServiceMediatorSocketForwardhttp.NewClientSocketServiceMediatorSidecarServiceMediator
type ServiceMediator struct {
  ...
	server *http.Server
  // 依赖Socket抽象接口
  socket network.Socket
}

// Forward 转发请求,请求URL为 /{serviceType}+ServiceUri 的形式,如/serviceA/api/v1/task
func (s *ServiceMediator) Forward(req *http.Request) *http.Response {
    ...
    // 发现下游服务的目的IP地址
    dest, err := s.discovery(svcType)
    // 创建HTTP客户端,将s.socket抽象接口作为入参
    client, err := http.NewClient(s.socket, s.localIp)
    // 通过HTTP客户端转发请求
    resp, err := client.Send(dest, forwardReq)
    ...
}

// 在ServiceMediator初始化时,将具体类型的Sidecar注入到ServiceMediator中
mediator := &ServiceMediator{
  socket: sidecar.NewFlowCtrlSidecar(network.DefaultSocket())
}

上述的修改,从原来依赖具体,改成了依赖抽象,符合了开闭原则。

ForwardSocket/SidecarServiceMediatorSocket/Sidecar
ForwardSocket/Sidecar

实现

// demo/sidecar/sidecar_factory.go

// 关键点1: 定义一个Sidecar工厂抽象接口
type Factory interface {
  // 关键点2: 工厂方法返回Socket抽象接口
	Create() network.Socket
}

// 关键点3: 按照需要实现具体的工厂


// demo/sidecar/raw_socket_sidecar_factory.go
// RawSocketFactory 只具备原生socket功能的sidecar,实现了Factory接口
type RawSocketFactory struct {
}
func (r RawSocketFactory) Create() network.Socket {
	return network.DefaultSocket()
}

// demo/sidecar/all_in_one_sidecar_factory.go
// AllInOneFactory 具备所有功能的sidecar工厂,实现了Factory接口
type AllInOneFactory struct {
	producer mq.Producible
}
func (a AllInOneFactory) Create() network.Socket {
	return NewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()), a.producer)
}
FactoryRawSocketFactoryAllInOneFactoryServiceMediatorFactoryForwardFactorySocket/Sidecar
// demo/service/mediator/service_mediator.go

type ServiceMediator struct {
  ...
	server *http.Server
  // 关键点4: 客户端依赖Factory抽象接口
  sidecarFactory sidecar.Factory
}

// Forward 转发请求,请求URL为 /{serviceType}+ServiceUri 的形式,如/serviceA/api/v1/task
func (s *ServiceMediator) Forward(req *http.Request) *http.Response {
    ...
    // 发现下游服务的目的IP地址
    dest, err := s.discovery(svcType)
    // 创建HTTP客户端,调用sidecarFactory.Create()生成Socket作为入参
    client, err := http.NewClient(s.sidecarFactory.Create(), s.localIp)
    // 通过HTTP客户端转发请求
    resp, err := client.Send(dest, forwardReq)
    ...
}

// 关键点5: 在ServiceMediator初始化时,将具体类型的sidecar.Factory注入到ServiceMediator中
mediator := &ServiceMediator{
  sidecarFactory: &AllInOneFactory{}
  // sidecarFactory: &RawSocketFactory{}
}

下面总结实现工厂方法模式的几个关键点:

sidecar.Factorynetwork.SocketCreateRawSocketFactoryAllInOneFactory

扩展

Go 风格的实现

前文的工厂方法模式实现,是非常典型的面向对象风格,下面我们给出一个更具 Go 风格的实现。

// demo/sidecar/sidecar_factory_func.go

// 关键点1: 定义Sidecar工厂方法类型
type FactoryFunc func() network.Socket

// 关键点2: 按需定义具体的工厂方法实现,注意这里定义的是工厂方法的工厂方法,返回的是FactoryFunc工厂方法类型
func RawSocketFactoryFunc() FactoryFunc {
	return func() network.Socket {
		return network.DefaultSocket()
	}
}

func AllInOneFactoryFunc(producer mq.Producible) FactoryFunc {
	return func() network.Socket {
		return NewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()), producer)
	}
}

type ServiceMediator struct {
  ...
	server *http.Server
  // 关键点3: 客户端依赖FactoryFunc工厂方法类型
  sidecarFactoryFunc FactoryFunc
}

func (s *ServiceMediator) Forward(req *http.Request) *http.Response {
    ...
    dest, err := s.discovery(svcType)
    // 关键点4: 创建HTTP客户端,调用sidecarFactoryFunc()生成Socket作为入参
    client, err := http.NewClient(s.sidecarFactoryFunc(), s.localIp)
    resp, err := client.Send(dest, forwardReq)
    ...
}

// 关键点5: 在ServiceMediator初始化时,将具体类型的FactoryFunc注入到ServiceMediator中
mediator := &ServiceMediator{
  sidecarFactoryFunc: RawSocketFactoryFunc()
  // sidecarFactory: AllInOneFactoryFunc(producer)
}
interfacestruct
关键点2AllInOneFactoryFunc
// 并非FactoryFunc类型,无法实现多态
func AllInOneFactoryFunc(producer mq.Producible) network.Socket {
    return NewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()), producer)
}

简单工厂

switch-case/if-else
// demo/sidecar/sidecar_simple_factory.go

// 关键点1: 定义sidecar类型
type Type uint8

// 关键点2: 按照需要定义sidecar具体类型
const (
	Raw Type = iota
	AllInOne
)

// 关键点3: 定义简单工厂对象
type SimpleFactory struct {
	producer mq.Producible
}

// 关键点4: 定义工厂方法,入参为sidecar类型,根据switch-case或者if-else来创建产品
func (s SimpleFactory) Create(sidecarType Type) network.Socket {
	switch sidecarType {
	case Raw:
		return network.DefaultSocket()
	case AllInOne:
		return NewAccessLogSidecar(NewFlowCtrlSidecar(network.DefaultSocket()), s.producer)
	default:
		return nil
	}
}

// 关键点5: 创建产品时传入具体的sidecar类型,比如sidecar.AllInOne
simpleFactory := &sidecar.SimpleFactory{producer: producer}
sidecar := simpleFactory.Create(sidecar.AllInOne)

静态工厂方法

静态工厂方法是 Java/C++ 的说法,主要用于替代构造函数来完成对象的实例化,能够让代码的可读性更好,而且起到了与客户端解耦的作用。比如 Java 的静态工厂方法实现如下:

public class Packet {
    private final Endpoint src;
    private final Endpoint dest;
    private final Object payload;

    private Packet(Endpoint src, Endpoint dest, Object payload) {
        this.src = src;
        this.dest = dest;
        this.payload = payload;
    }

    // 静态工厂方法
    public static Packet of(Endpoint src, Endpoint dest, Object payload) {
        return new Packet(src, dest, payload);
    }
		...
}

// 用法
packet = Packet.of(src, dest, payload)

Go 中并没有静态一说,直接通过普通函数来完成对象的构造即可,比如:

// demo/network/packet.go
type Packet struct {
	src     Endpoint
	dest    Endpoint
	payload interface{}
}

// 工厂方法
func NewPacket(src, dest Endpoint, payload interface{}) *Packet {
	return &Packet{
		src:     src,
		dest:    dest,
		payload: payload,
	}
}

// 用法
packet := NewPacket(src, dest, payload)

典型应用场景

&Packet{src: src, dest: dest, payload: payload}

优缺点

优点

  1. 代码的可读性更好。
  2. 与客户端程序解耦,当实例化逻辑变更时,只需改动工厂方法即可,避免了霰弹式修改。

缺点

  1. 引入工厂方法模式会新增一些对象/接口的定义,滥用会导致代码更加复杂。

与其他模式的关联

很多同学容易将工厂方法模式和抽象工厂模式混淆,抽象工厂模式主要运用在实例化“产品族”的场景,可以看成是工厂方法模式的一种演进。