NOTE

Simple Factory Pattern

Simple-factory notes in Java and Go, followed by a generic template-method + simple-factory + strategy example.

Software Architecture & Engineering1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

Separate class creation from class use.

1. Java

1.1. Client

public class Client
{
    public static void main(String[] args)
    {
        Car car = CarFactory.getCart("Audi");
    }
}

1.2. Products

public interface Car
{
}
public class AudiCar implements Car
{
}
public class QQCar implements Car
{
}

1.3. Factory

public class CarFactory
{
    public static Car getCart(String name)
    {
        // The problem is that this introduces too many if/else branches
        if("Audi".equals(name))
            return new AudiCar();
        else if ("QQ".equals(name))
            return new QQCar();
        else
            throw new RuntimeException("No such car");
    }

}

2. Golang

2.1. Products

/* Car interface */
type Car interface {
	Run()
}

// QQ car
type QQCar struct {

}

func (Q QQCar) Run() {
	fmt.Println("I am QQ car")
}

// Audi car
type AodiCar struct {

}

func (Q AodiCar) Run() {
	fmt.Println("I am Aodi car")
}

2.2. Factory


/* A simple factory can be implemented as a constructor function */
func NewCar(name string) Car {
	if name == "QQ" {
		return &QQCar{}
	} else if name == "Aodi" {
		return &AodiCar{}
	} else {
		return nil
	}
}

2.3. Client

func main() {
	qqCar := simplefactory.NewCar("QQ")
	aodiCar := simplefactory.NewCar("Aodi")
	qqCar.Run()
	aodiCar.Run()
}

3. Example

Template Method + Simple Factory + Strategy Pattern

  • pb.go
package pb

type Request struct {

}

type Response struct {

}
  • abstract_template.go
package template

import (
	"context"
	"fmt"
	"example/template/pb"
)

/* Implements IFetchDataService */
type abstractFetchDataService struct {
	IFetchDataSteps // internal processing steps
	req             *pb.Request
	rsp             *pb.Response
	basicInfo       interface{} // basic data
	extraInfo       interface{} // extra data
}

func (t *abstractFetchDataService) checkParam(ctx context.Context) error {
	fmt.Println("default checkParam")
	return nil
}

func (t *abstractFetchDataService) getBasicInfo(ctx context.Context) error {
	fmt.Println("default getBasicInfo")
	return nil

}

func (t *abstractFetchDataService) getExtraInfo(ctx context.Context) error {
	fmt.Println("default getExtraInfo")
	return nil

}

//newTemplate
func newTemplate(iSteps IFetchDataSteps) *abstractFetchDataService {
	return &abstractFetchDataService{IFetchDataSteps: iSteps}
}

/* Fixed steps of the template method */
func (t *abstractFetchDataService) HandleRequest(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
	t.req = req
	t.rsp = rsp
	if err := t.IFetchDataSteps.checkParam(ctx); err != nil {
		return err
	}
	if err := t.IFetchDataSteps.getBasicInfo(ctx); err != nil {
		return err
	}
	if err := t.IFetchDataSteps.getExtraInfo(ctx); err != nil {
		return err
	}
	return nil
}
  • source_a_template_impl.go
package template

import (
	"context"
	"fmt"
)

const TypeSourceAService = 2

var sourceAServiceConstructorFunc fetchDataServiceConstructor = func() IFetchDataService {
	return NewFetchDataFromSourceAService()
}

func init() {
	GlobalServiceFactory.RegisterService(TypeSourceAService, sourceAServiceConstructorFunc)
}

/* source_a service */
type FetchDataFromSourceAService struct {
	*abstractFetchDataService
}

func (f *FetchDataFromSourceAService) checkParam(ctx context.Context) error {
	fmt.Println("checkParam")
	return f.abstractFetchDataService.checkParam(ctx)
}

func (f *FetchDataFromSourceAService) getBasicInfo(ctx context.Context) error {
	fmt.Println("getBasicInfo")
	return f.abstractFetchDataService.getBasicInfo(ctx)

}

//func (f *FetchDataFromSourceAService) getExtraInfo(ctx context.Context) error {
//	fmt.Println("getExtraInfo")
//	return f.abstractFetchDataService.getExtraInfo(ctx)
//
//}


//NewFetchDataFromSourceAService
func NewFetchDataFromSourceAService() *FetchDataFromSourceAService {
	service := &FetchDataFromSourceAService{}
	service.abstractFetchDataService = newTemplate(service)
	return service
}
  • factory.go
package template

import "fmt"

var GlobalServiceFactory = newServiceFactory()

// Custom function type used to create service instances
type fetchDataServiceConstructor = func() IFetchDataService

// service function factory
type serviceFactory struct {
	serviceMap map[int]fetchDataServiceConstructor
}

//newServiceFactory
func newServiceFactory() *serviceFactory {
	return &serviceFactory{serviceMap: make(map[int]fetchDataServiceConstructor)}
}

// Get a service by service type
func (s *serviceFactory) GetServiceByType(serviceType int) (IFetchDataService, error) {
	serviceConstructor, ok := s.serviceMap[serviceType]
	if ok {
		return serviceConstructor(), nil
	}
	err := fmt.Errorf("GetServiceByType: serviceType %v invalid", serviceType)
	return nil, err
}

// Register serviceType and serviceConstructor
func (s *serviceFactory) RegisterService(serviceType int, serviceConstructor fetchDataServiceConstructor) {
	s.serviceMap[serviceType] = serviceConstructor
}
  • itemplate.go
package template

import (
	"context"
	"example/template/pb"
)

/* External interface for fetching a data list */
type IFetchDataService interface {
	HandleRequest(ctx context.Context, req *pb.Request, rsp *pb.Response) error
}

/* Internal processing steps */
type IFetchDataSteps interface {
	// validate parameters
	checkParam(ctx context.Context) error
	// fetch basic data
	getBasicInfo(ctx context.Context) error
	// fetch extra data
	getExtraInfo(ctx context.Context) error
}
  • template_test.go
package template

import (
	"context"
	"testing"
)

func TestTemplate(t *testing.T) {
	service, _ := GlobalServiceFactory.GetServiceByType(TypeSourceAService)
	service.HandleRequest(context.Background(), nil, nil)
}
// output
checkParam
default checkParam
getBasicInfo
default getBasicInfo
default getExtraInfo

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub