创建型设计模式

创建型设计模式 #

单例模式 #

单例模式提供了一种访问其唯一对象的方法,该对象可以直接被访问,无需实例化

双重检查 #

var lock = &sync.Mutex{}

type singleton struct {
}

var instance *singleton
//获取实例
func GetInstance() *singleton {
	if instance == nil {
		lock.Lock()
		if instance == nil {
			fmt.Println("创建单个实例")
			instance = new(singleton)
		} 
        lock.Unlock()
	} 
	return instance
}

sync.Once #

var once sync.Once  //只执行一次

func GetInstance() *singleton {
	once.Do(func() {
		instance = new(singleton)
		fmt.Println("创建单个实例")
	})
	return instance
}

优点 #

  • 对于内存中只存在一个对象,且需要频繁创建和销毁对象的系统,使用单例模式可以提升系统性能

缺点 #

  • 可扩展性较低
  • 若用于数据库连接池对象,则可能会导致共享连接池对象过多且没有释放的场景,从而出现连接池溢出问题。
  • 如果创建的对象长时间不使用,可能会被操作系统垃圾回收,导致对象丢失

工厂模式 #

介绍 #

工厂方法模式定义了一个用于创建对象的接口,但让子类决定实例化那个类

  • 如果开发这无法预知对象的具体类型及依赖关系,则可以使用工厂方法模式
  • 如果开发者希望其他开发者可以扩展软件库或框架的内部组件,则可以使用工厂方法模式
  • 如果一个类需要通过子类指定其创建的对象,则可以使用工厂方法模式
// 工厂接口
type Factory interface {
	FactoryMethod(owner string) Product
}

// 具体工厂
type ConcreteFactory struct {
}

// 具体工厂的工厂方法
func (cf *ConcreteFactory) FactoryMethod(owner string) Product {
	switch owner {
	case "shirdon":
		return &ConcreteProduct{}
	default:
		p := &ConcreteProduct{}
		return p
	}
}

// 产品
type Product interface {
	Use()
}

// 具体产品
type ConcreteProduct struct {
}

// 具体产品的方法
func (p *ConcreteProduct) Use() {
	fmt.Println("This is a concrete product")
}

func main() {

	//声明具体工厂对象
	var factory Factory
	factory = new(ConcreteFactory)
	//生产产品
	product := factory.FactoryMethod("shirdon")
	//使用产品
	product.Use()
}

优点 #

  • 可扩展
  • 可单独测试

缺点 #

  • 品牌越多,越复杂
  • 引入抽象层,增加理解难度

示例 #

假设你有一款服装工厂的品牌管理程序,最初只有一款服装 ANTA,想增加一个品牌,则使用工厂模式

//定义服装产品接口
type IClothes interface {
	setName(name string)
	setSize(size int)
	GetName() string
	GetSize() int
}
//定义产品类及其方法
type clothes struct {
	name string
	size int
}

func (c *clothes) setName(name string) {
	c.name = name
}

func (c *clothes) GetName() string {
	return c.name
}

func (c *clothes) setSize(size int) {
	c.size = size
}

func (c *clothes) GetSize() int {
	return c.size
}
//定义具体服装产品类及初始化函数
type ANTA struct {
	clothes
}

func newANTA() IClothes {
	return &ANTA{
		clothes: clothes{
			name: "ANTA clothes",
			size: 4,
		},
	}
}

type PEAK struct {
	clothes
}

func newPEAK() IClothes {
	return &PEAK{
		clothes: clothes{
			name: "PEAK clothes",
			size: 1,
		},
	}
}
//根据实参类型生产不同品牌服装
func MakeClothes(clothesType string) (IClothes, error) {
	if clothesType == "ANTA" {
		return newANTA(), nil
	}
	if clothesType == "PEAK" {
		return newPEAK(), nil
	}
	return nil, fmt.Errorf("Wrong clothes type passed")
}

func main() {
	ANTAs, _ := MakeClothes("ANTA")
	PEAKs, _ := MakeClothes("PEAK")

	PrintDetails(ANTAs)
	PrintDetails(PEAKs)
}

func PrintDetails(c IClothes) {
	fmt.Printf("Clothes: %s", c.GetName())
	fmt.Println()
	fmt.Printf("Size: %d", c.GetSize())
	fmt.Println()
}

抽象工厂模式 #

介绍 #

工厂方法模式的另一层抽象

  • 如果开发者不希望代码基于具体产品进行构建,则可以使用抽象工厂模式
  • 如果某个类中具有一组抽象方法,并且这个类的功能不够明确,则可以考虑使用抽象工厂模式
  • 如果一个类要与多种类型的产品交互,则可以考虑将工厂方法抽象到具备完整功能的抽象工厂接口中
// 抽象产品接口
type AbstractProduct interface {
	GetName()
}

// 具体产品类
type ConcreteProduct struct {
}

// 具体产品的方法
func (c *ConcreteProduct) GetName() {
	fmt.Println("具体产品 ConcreteProduct")
}

// 抽象工厂接口
type AbstractFactory interface {
	CreateProduct() ConcreteProduct
}

// 具体工厂
type ConcreteFactory struct {
}

// 初始化具体工厂对象
func NewConcreteFactory() ConcreteFactory {
	return ConcreteFactory{}
}

// 具体工厂创建具体产品
func (s *ConcreteFactory) CreateProduct() ConcreteProduct {
	return ConcreteProduct{}
}

func main() {

	var abstractFactory AbstractFactory
	abstractFactory = new(ConcreteFactory)
	a:=abstractFactory.CreateProduct()
	a.GetName()
	
	factory := NewConcreteFactory()
	product := factory.CreateProduct()
	product.GetName()
}

优点 #

  • 当客户端不知道要创建什么类型的对象时
  • 抽象工厂模式实现了具体类的隔离
  • 可以轻松改变产品系列
  • 保证产品一致性

缺点 #

  • 不利于后期添加新产品,抽象工厂模式难以扩展新型产品,如果要支持新型产品,则需要扩展工厂接口,这涉及更改抽象工厂对象及其所有子对象

示例 #

假设一个代工厂可以组装生产多种手机和计算机,分为生产小米产品的小米工厂和生产联想产品的联想工厂,小米工厂和联想工厂都可以生产各自品牌的手机和计算机,也可以生产其他产品。

定义抽象工厂接口

//电子产品工厂
type InterfaceElectronicFactory interface {
	MakePhone() AbstractPhone
	MakeComputer() AbstractComputer
}

//获取电子产品工厂对象
func GetElectronicFactory(brand string) (InterfaceElectronicFactory, error) {
	if brand == "Xiaomi" {
		return &XiaomiFactory{}, nil
	}

	if brand == "Lenovo" {
		return &LenovoFactory{}, nil
	}

	return nil, fmt.Errorf("%s", "error brand type")
}

定义具体工厂类

//联想品牌工厂
type LenovoFactory struct {
}

//生产手机
func (n *LenovoFactory) MakePhone() AbstractPhone {
	return &LenovoPhone{
		Phone: Phone{
			color: "Black",
			size:  5,
		},
	}
}

//生产电脑
func (n *LenovoFactory) MakeComputer() AbstractComputer {
	return &LenovoComputer{
		Computer: Computer{
			color: "White",
			size:  14,
		},
	}
}

//小米品牌工厂
type XiaomiFactory struct {
}

//生产手机
func (a *XiaomiFactory) MakePhone() AbstractPhone {
	return &XiaomiPhone{
		Phone: Phone{
			color: "White",
			size:  5,
		},
	}
}

//生产电脑
func (a *XiaomiFactory) MakeComputer() AbstractComputer {
	return &XiaomiComputer{
		Computer: Computer{
			color: "Black",
			size:  14,
		},
	}
}

定义抽象产品接口

//定义电脑接口
type AbstractComputer interface {
	SetColor(color string)
	SetSize(size int)
	GetColor() string
	GetSize() int
}

type Computer struct {
	color string
	size  int
}

func (s *Computer) SetColor(color string) {
	s.color = color
}

func (s *Computer) GetColor() string {
	return s.color
}

func (s *Computer) SetSize(size int) {
	s.size = size
}

func (s *Computer) GetSize() int {
	return s.size
}

定义手机接口

//定义手机接口
type AbstractPhone interface {
	SetColor(color string)
	SetSize(size int)
	GetColor() string
	GetSize() int
}

type Phone struct {
	color string
	size  int
}

func (s *Phone) SetColor(color string) {
	s.color = color
}

func (s *Phone) GetColor() string {
	return s.color
}

func (s *Phone) SetSize(size int) {
	s.size = size
}

func (s *Phone) GetSize() int {
	return s.size
}

定义具体产品类

//联想电脑
type LenovoComputer struct {
	Computer
}
//联想手机
type LenovoPhone struct {
	Phone
}
//小米电脑
type XiaomiComputer struct {
	Computer
}
//小米手机
type XiaomiPhone struct {
	Phone
}
func main() {
	//声明小米工厂
	xiaomiFactory, _ := actualCombat.GetElectronicFactory("Xiaomi")
	//声明联想工厂
	lenovoFactory, _ := actualCombat.GetElectronicFactory("Lenovo")

	//联想工厂生产联想手机
	lenovoPhone := lenovoFactory.MakePhone()
	//联想电脑生产联想电脑
	lenovoComputer := lenovoFactory.MakeComputer()

	//小米工厂生产小米手机
	xiaomiPhone := xiaomiFactory.MakePhone()
	//小米电脑生产小米电脑
	xiaomiComputer := xiaomiFactory.MakeComputer()

	printPhoneDetails(lenovoPhone)
	printComputerDetails(lenovoComputer)

	printPhoneDetails(xiaomiPhone)
	printComputerDetails(xiaomiComputer)
}

func printPhoneDetails(s actualCombat.AbstractPhone) {
	fmt.Printf("Color: %s", s.GetColor())
	fmt.Println()
	fmt.Printf("Size: %d inch", s.GetSize())
	fmt.Println()
}

func printComputerDetails(s actualCombat.AbstractComputer) {
	fmt.Printf("Color: %s", s.GetColor())
	fmt.Println()
	fmt.Printf("Size: %d inch", s.GetSize())
	fmt.Println()
}

生成器模式 #

介绍 #

目标是将复杂对象的构造与其实现分离,以相同的构造过程可以创建不同的实现

  • 当开发者希望创建不同形式的产品时
  • 当开发者需要创建各种形式的产品,这些产品的制造过程相似且产品之间的差别不大(如红色钢笔和黑色钢笔)
  • 如果需要使用构造函数,并且构造函数的参数很多,则可以使用生成器模式
  • 当需要构建同一个对象的不同表示时,可以使用生成器模式
// 主管
type Director struct {
	builder Builder //接口
}

// 初始化主管对象
func NewDirector(builder Builder) Director {//入参接口类型,相当于接口赋值
	return Director{builder}
}

// 通过一系列步骤生成产品
func (d *Director) Construct() {
	d.builder.Build()
}

// 生成器接口
type Builder interface {
	Build()
}

//具体生成器,用于构建产品的生成器
type ConcreteBuilder struct {
	result Product
}

// 初始化具体生成器对象
func NewConcreteBuilder() ConcreteBuilder {
	return ConcreteBuilder{result: Product{}}
}

// 生成产品
func (b *ConcreteBuilder) Build() {
	b.result = Product{}
}

// 返回在生成步骤中生成的产品
func (b *ConcreteBuilder) GetResult() Product {
	return Product{true}
}

// 产品
type Product struct {
	Built bool
}

func main() {
	builder:=NewConcreteBuilder()    //生成结构体,结构体中嵌套产品结构体
	director:=NewDirector(&builder)  //得到一个结构体里面带接口
	director.Construct()         //执行结构体方法 里面执行接口方法
	product:=builder.GetResult() //执行结构体方法
	fmt.Println(product)
}

优点 #

  • 在生成器模式中,产品内部组成的细节对客户端不可见,将产品的创建过程和产品解耦,使相同的创建过程可以创建不同的产品对象
  • 每个具体的生成器都相对独立,因此可以十分方便地替换具体生成器或增加新的具体生成器,无须修改原有类库的代码,系统扩展方便,符合开闭原则,设计灵活性和代码可读性较高。
  • 生成器模式可以将复杂产品的创建步骤分解在不同的方法中,使创建过程更加清晰,更易于使用程序控制创建过程

缺点 #

  • 使用范围有限,不适合产品之间差异很大的情况
  • 代码量大,需要为不同类型的产品创建单独的具体生成器

示例 #

介绍如何使用生成器模式生产MPV和SUV两种类型的汽车

// 生成器接口
type InterfaceBuilder interface {
	SetSeatsType()
	SetEngineType()
	SetNumber()
	GetCar() Car
}

// 获取生成器
func GetBuilder(BuilderType string) InterfaceBuilder {
	if BuilderType == "mpv" {
		return &MpvBuilder{}
	}

	if BuilderType == "suv" {
		return &SuvBuilder{}
	}
	return nil
}
// MPV生成器
type MpvBuilder struct {
	SeatsType  string
	EngineType string
	Number     int
}

func NewMpvBuilder() *MpvBuilder {
	return &MpvBuilder{}
}

func (b *MpvBuilder) SetSeatsType() {
	b.SeatsType = "MPV型座椅"
}

func (b *MpvBuilder) SetEngineType() {
	b.EngineType = "MPV型引擎"
}

func (b *MpvBuilder) SetNumber() {
	b.Number = 8
}

func (b *MpvBuilder) GetCar() Car {
	return Car{
		EngineType: b.EngineType,
		SeatsType:  b.SeatsType,
		Number:     b.Number,
	}
}
// SUV生成器
type SuvBuilder struct {
	SeatsType  string
	EngineType string
	Number     int
}

func newSuvBuilder() *SuvBuilder {
	return &SuvBuilder{}
}

func (b *SuvBuilder) SetSeatsType() {
	b.SeatsType = "SUV型座椅"
}

func (b *SuvBuilder) SetEngineType() {
	b.EngineType = "SUV型引擎"
}

func (b *SuvBuilder) SetNumber() {
	b.Number = 6
}

func (b *SuvBuilder) GetCar() Car {
	return Car{
		EngineType: b.EngineType,
		SeatsType:  b.SeatsType,
		Number:     b.Number,
	}
}
type Car struct {
	SeatsType  string
	EngineType string
	Number     int
}

// 主管类型
type Director struct {
	Builder InterfaceBuilder
}

func NewDirector(b InterfaceBuilder) *Director {
	return &Director{
		Builder: b,
	}
}

func (d *Director) SetBuilder(b InterfaceBuilder) {
	d.Builder = b
}

func (d *Director) BuildCar() Car {
	d.Builder.SetEngineType()
	d.Builder.SetSeatsType()
	d.Builder.SetNumber()
	return d.Builder.GetCar()
}
func main() {
	//声明MPV生成器对象
	MpvBuilder := GetBuilder("mpv") //得到接口
	//声明SUV生成器对象
	SuvBuilder := GetBuilder("suv")

	//声明主管对象
	Director := NewDirector(MpvBuilder) //把接口给结构体的子类
	//生产MPV类型汽车
	mpvCar := Director.BuildCar() //调接口体方法 里面执行接口方法

	fmt.Printf("MPV类型引擎: %s\n", mpvCar.EngineType)
	fmt.Printf("MPV类型座椅: %s\n", mpvCar.SeatsType)
	fmt.Printf("MPV类型数量: %d\n", mpvCar.Number)

	//设置生成器对象
	Director.SetBuilder(SuvBuilder)
	//生产SUV类型汽车
	suvCar := Director.BuildCar()

	fmt.Printf("\nSUV类型引擎: %s\n", suvCar.EngineType)
	fmt.Printf("SUV类型座椅: %s\n", suvCar.SeatsType)
	fmt.Printf("SUV类型数量: %d\n", suvCar.Number)
}

原型模式 #

原型模式能够复制对象,并且代码不依赖对象所属的类。原型模式可以为开发者节省资源和时间,尤其在对象创建过程较为复杂时。

  • 比较常见
// 原型接口
type Prototype interface {
	GetName() string
	Clone() Prototype
}

// 具体原型类
type ConcretePrototype struct {
	Name string
}

// 返回具体原型的名称
func (p *ConcretePrototype) GetName() string {
	return p.Name
}

// Clone 创建一个ConcretePrototype类的克隆新实例
func (p *ConcretePrototype) Clone() Prototype {  //返回的是一个接口
	return &ConcretePrototype{p.Name}
}

func main() {
	cp := &ConcretePrototype{Name: "Shirdon"}
	a := cp.Clone() //返回一个接口
	fmt.Println(a.GetName()) //可以直接调方法
	res := cp.GetName()
	fmt.Println(res)
}

优点 #

  • 比其他模式更灵活
  • 可以通过改变值指定新对象
  • 可以通过改变结构指定新对象
  • 可以减少子类

缺点 #

  • 向客户端隐藏了具体的产品类别
  • 当克隆的类已经存在时,原型接口的每个子类都必须实现Clone()方法

对象池模式 #

在对象池模式中,对象被预先初始化并存储于对象池中,当需要时,客户端可以从对象池中请求一个对象并使用,然后将其返回对象池中。对象池模式可以减少频繁创建对象造成的资源浪费。

  • 当系统资源受限时,如果需要提高内存管理效率时
  • 需要创建大量对象时,如数据库连接
  • 当对象时不可变对象时,如数据库连接
  • 当需要提升性能时
  • 需要在短时间内连续创建和销毁大量对象时
  • 当需要使用相似对象,不加选择和不受控制的初始化新对象时
// 对象池
type Pool struct {
	sync.Mutex
	Inuse     []interface{}
	Available []interface{}
	new       func() interface{}
}

// 创建一个新对象池
func NewPool(new func() interface{}) *Pool {
	return &Pool{new: new}
}

// 从池中获取要使用的新池对象。
// 如果没有可用,则获取创建1个池对象的新实例
func (p *Pool) Acquire() interface{} {
	p.Lock()
	var object interface{}
	if len(p.Available) != 0 {
		object = p.Available[0]
		p.Available = append(p.Available[:0], p.Available[1:]...)
		p.Inuse = append(p.Inuse, object)
	} else {
		object = p.new()                  //执行new函数 返回10
		p.Inuse = append(p.Inuse, object) //将10插入切片
	}
	p.Unlock()
	return object
}

// 将对象释放回对象池
func (p *Pool) Release(object interface{}) {
	p.Lock()
	p.Available = append(p.Available, object)
	for i, v := range p.Inuse {
		if v == object {
			p.Inuse = append(p.Inuse[:i], p.Inuse[i+1:]...) //移除
			break
		}
	}
	p.Unlock()
}

func main() {
	num := func() interface{} { //定义一个返回interface的函数
		return 10.0
	}
	pool := NewPool(num)     //返回结构体指针,将上个interface给结构体里面的一个new字段
	object := pool.Acquire() //执行方法

	fmt.Println(pool.Inuse)
	fmt.Println(pool.Available)

	pool.Release(object)
	fmt.Println(pool.Inuse)
	fmt.Println(pool.Available)
}
//$ go run main.go
//[10]
//[]
//[]
//[10]

优点 #

  • 有助于提高整体性能
  • 有助于在某些情况下提高对象初始化速度
  • 有助于更好的管理连接,并且提供重用和共享这些连接的方法
  • 有助于限制对象的最大创建数量

缺点 #

  • 会增加分配/释放对象的资源开销
  • 多个对象长期存在于对象池而不销毁他们,造成资源浪费
  • 对象池数量难以把控

示例 #

初始化一个指定大小的资源池,用于避免通过通道的资源竞争问题,并且在资源池为空的情况下设置获取超时处理,用于防止客户端等待太久。

var (
	ErrPoolNotExist  = errors.New("pool not exist")
	ErrGetResTimeout = errors.New("get resource time out")
)

// 资源类
type Resource struct {
	reusable int
}

// 初始化资源对象
// 模拟缓慢的资源访问,例如,TCP 连接等
func NewResource(id int) *Resource {
	time.Sleep(500 * time.Millisecond)
	return &Resource{reusable: id}
}

// 模拟资源耗时
func (r *Resource) Do(workId int) {
	time.Sleep(time.Duration(rand.Intn(5)) * 1000 * time.Millisecond)
	log.Printf("using resource #%d finished work %d finish\n", r.reusable, workId)
}

// 对象池
type Pool chan *Resource

// 并发创建资源对象,节省资源对象初始化时间
func New(size int) Pool {
	p := make(Pool, size)
	wg := new(sync.WaitGroup)
	wg.Add(size)
	for i := 0; i < size; i++ {
		go func(reusable int) {
			p <- NewResource(reusable)
			wg.Done()
		}(i)
	}
	wg.Wait()
	return p
}

// 从获取对象池获取对象
func (p Pool) GetResource() (r *Resource, err error) {
	deadline := time.Now().Add(3 * time.Second)
	for time.Now().Before(deadline) {
		select {
		case r := <-p:
			return r, nil
		default:
			// 如果通道为空,等待 100 毫秒后再尝试
			time.Sleep(100 * time.Millisecond)
		}
	}
	return nil, ErrGetResTimeout
}

//	func (p Pool) GetResource() (r *Resource, err error) {
//		timer := time.NewTimer(3 * time.Second)
//		defer timer.Stop()
//
//		for {
//			select {
//			case r := <-p:
//				return r, nil
//			case <-timer.C:
//				return nil, ErrGetResTimeout
//			default:
//				// 如果通道为空,等待 100 毫秒后再尝试
//				time.Sleep(100 * time.Millisecond)
//			}
//		}
//	}
//
// 将资源返回到资源池
func (p Pool) GiveBackResource(r *Resource) error {
	if p == nil {
		return ErrPoolNotExist
	}
	p <- r
	return nil
}

func main() {
	// 初始化一个包含五个资源的资源池
	// 可以调整为 1 或 10 以查看差异
	size := 5
	p := New(size)

	// 调用资源池
	doWork := func(workId int, wg *sync.WaitGroup) {
		defer wg.Done()
		// 从资源池中获取资源对象
		res, err := p.GetResource()
		if err != nil {
			log.Println(err)
			return
		}
		//返回的资源对象
		defer p.GiveBackResource(res)
		// 使用资源处理工作
		res.Do(workId)
	}

	// 模拟100个并发进程从资产池中获取资源对象
	num := 100
	wg := new(sync.WaitGroup)
	wg.Add(num)
	for i := 0; i < num; i++ {
		go doWork(i, wg)
	}
	wg.Wait()
}