第7节 工厂模式(创建型模式)


❤️💕💕Java和Golang的设计模式,设计模式介绍、创建者模式、结构型模式、行为型模式。Myblog:http://nsddd.topopen in new window


[TOC]

创建形模式

模式名称模式名称作用
创建型模式 Creational Pattern单例模式★★★★☆是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
简单工厂模式★★★☆☆通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
工厂方法模式★★★★★定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中。
抽象工厂模式★★★★★提供一个创建一系列相关或者相互依赖的接口,而无需指定它们具体的类。
原型模式★★★☆☆用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
建造者模式★★☆☆☆将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

提示

目前标准的创建型设计模式共有6种(注:设计模式种类并非仅仅局限于此,设计模式实则是一种编程思想,开发者可以根据自身经验来总结出很多种设计模式思想,这6中创建型设计模式为早期官方认可的标准模式)

简单工厂模式

一般逻辑💡简单的一个案例如下:

// Factory mode
package main

import "fmt"

//Define a fruit structure
type Fruit struct {
	//................................................................

}

//Define a method of obtaining fruit
func (f *Fruit) GetFruit() string {
	return "fruit"
}

func (f *Fruit) String(name string) string {
	switch name {
	case "apple":
		fmt.Println("我是苹果")
		return "apple"
	case "orange":
		fmt.Println("我是橘子")
		return "orange"
	default:
		fmt.Println("我是水果")
		return "fruit"
	}
}

//Define a fruit method (create a fruit)
func NewFruit(name string) *Fruit {
	fruit := &Fruit{}
	switch name {
	case "apple":
		//Create an apple
	case "orange":
		//Create an orange
	case "banana":
		//Create a banana
	case "pear":
		//Create a pear
	default:
		fmt.Println("没有这个水果")
	}
	return fruit
}

//Business logic
func main() {
	//Create a fruit
	fruit := NewFruit("apple")
	//Get fruit
	fruit.GetFruit()
	//Get the name of the fruit
	fruit.String("apple")

	fruit = NewFruit("orange")
	fruit.GetFruit()
	fruit.String("orange")

	fruit = NewFruit("banana")
	fruit.GetFruit()
	fruit.String("banana")
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\84-main.go"
我是苹果
我是橘子
我是水果

问题

不难看出,Fruit类是一个“巨大的”类,在该类的设计中存在如下几个问题:

​ (1) 在Fruit类中包含很多“if…else…” (或者 switch) 代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断。

​ (2) Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水果对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原则”,不利于类的重用和维护;

(3) 当需要增加新类型的水果时,必须修改Fruit类的构造函数NewFruit()和其他相关方法源代码,违反了“开闭原则”。

关键是来观察 main() 函数,main() 函数与 Fruit 类是两个模块。当业务层希望创建一个对象的时候,将直接依赖Fruit类型的构造方法 NewFruit(),这样随着Fruit的越来越复杂,那么业务层的开发逻辑也需要依赖Fruit模块的更新,且随之改变,这样将导致业务层开发需要观察Fruit模块做改动,影响业务层的开发效率和稳定性。整体的依赖关系为。

业务逻辑层 ---> 基础类模块

那么如何将业务层创建对象与基础类模块做解耦呢,这里即可以在中间加一层工厂模块层,来降低业务逻辑层对基础模块层的直接依赖和耦合关联。

业务逻辑层 ---> 工厂模块 ---> 基础类模块

​ 这样就引出了需要对工厂模块的一些设计和加工生成基础模块对象的模式。

简单工厂模式设计

简单工厂模式并不属于GoF的23种设计模式。他是开发者自发认为的一种非常简易的设计模式,其角色和职责如下:

工厂(Factory)角色:简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。

抽象产品(AbstractProduct)角色:简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。

具体产品(Concrete Product)角色:简单工厂模式所创建的具体实例对象。

image-20221128142632119

改进类图:

image-20221128143357792

工厂作为交际,我们不需要针对具体的水果,工厂返回就好了

package main

import "fmt"

//------抽象层------
type Fruit interface {
	String(name string) string
}

//------实现层------
type Apple struct {
}

func (apple *Apple) String(name string) string {
	fmt.Println("apple")
	return "apple"
}

type Orange struct {
}

func (orange *Orange) String(name string) string {
	fmt.Println("orange")
	return "orange"
}

type Banana struct {
}

func (banana *Banana) String(name string) string {
	fmt.Println("banana")
	return "banana"
}

type Pear struct {
}

func (pear *Pear) String(name string) string {
	fmt.Println("pear")
	return "pear"
}

//------工厂层------
type Factory struct {
}

func (factory *Factory) GetFruit(name string) Fruit {
	var fruit Fruit
	switch name {
	case "apple":
		fruit = &Apple{}
	case "orange":
		fruit = &Orange{}
	case "banana":
		fruit = &Banana{}
	case "pear":
		fruit = &Pear{}
	}
	return fruit
}

//------业务逻辑层------
func main() {
	Factory := &Factory{}
	apple := Factory.GetFruit("apple")
	apple.String("apple")

	orange := Factory.GetFruit("orange")
	orange.String("orange")

	banana := Factory.GetFruit("banana")
	banana.String("banana")
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\85-main.go"
apple
orange
banana

简单工厂方法模式的优缺点

优点:

  1. 实现了对象创建和使用的分离。

  2. 不需要记住具体类名,记住参数即可,减少使用者记忆量。

缺点:

  1. 对工厂类职责过重,一旦不能工作,系统受到影响。

  2. 增加系统中类的个数,复杂度和理解度增加。

  3. 违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂。

适用场景:

  1. 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

  2. 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

抽象工厂

Go语言的抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一个接口,用于创建一系列相关或相互依赖的对象,而不需要指定它们的具体类。换句话说,抽象工厂模式为创建一组相关的对象提供了一个统一的接口,而客户端代码只需使用该接口就可以与这些对象交互,从而降低了代码的耦合度。

简单来说

抽象工厂模式就是将多个工厂类的接口进行抽象,然后再用一个 工厂类 来封装这些工厂的接口。在创建具体对象的时候,我们通常需要使用其他对象和数据结构,因此,我们还需要定义一些相关 产品接口产品结构体

// 抽象工厂接口
type AbstractFactory interface {
    CreateProductA() ProductA
    CreateProductB() ProductB
}

// 具体工厂1
type ConcreteFactory1 struct{}

func (f *ConcreteFactory1) CreateProductA() ProductA {
    return &ConcreteProductA1{}
}

func (f *ConcreteFactory1) CreateProductB() ProductB {
    return &ConcreteProductB1{}
}

// 具体工厂2
type ConcreteFactory2 struct{}

func (f *ConcreteFactory2) CreateProductA() ProductA {
    return &ConcreteProductA2{}
}

func (f *ConcreteFactory2) CreateProductB() ProductB {
    return &ConcreteProductB2{}
}

// 抽象产品A接口
type ProductA interface {
    GetName() string
}

// 具体产品A1
type ConcreteProductA1 struct{}

func (p *ConcreteProductA1) GetName() string {
    return "ConcreteProductA1"
}

// 具体产品A2
type ConcreteProductA2 struct{}

func (p *ConcreteProductA2) GetName() string {
    return "ConcreteProductA2"
}

// 抽象产品B接口
type ProductB interface {
    GetPrice() float64
}

// 具体产品B1
type ConcreteProductB1 struct{}

func (p *ConcreteProductB1) GetPrice() float64 {
    return 10.0
}

// 具体产品B2
type ConcreteProductB2 struct{}

func (p *ConcreteProductB2) GetPrice() float64 {
    return 20.0
}

在上面的代码中,我们定义了一个抽象工厂接口 AbstractFactory,以及两个具体工厂 ConcreteFactory1ConcreteFactory2。每个具体工厂都实现了 CreateProductACreateProductB 方法,用于创建不同类型的产品。

同时,我们还定义了抽象产品A和产品B接口 ProductAProductB,以及它们的具体实现类 ConcreteProductA1ConcreteProductA2ConcreteProductB1ConcreteProductB2。这些具体产品类包含了各自的实现细节,实现了抽象产品接口定义的方法。

通过以上实现,客户端可以使用工厂接口 AbstractFactory 创建一组相关的对象,而无需关心具体的实现细节。例如:

// 根据需要选择合适的工厂~
factory1 := &ConcreteFactory1{}

productA := factory1.CreateProductA()
productB := factory1.CreateProductB()

fmt.Println(productA.GetName()) // 输出: ConcreteProductA1
fmt.Println(productB.GetPrice()) // 输出: 10.0

客户端代码只需要使用抽象工厂接口 AbstractFactory,以及该接口定义的创建产品方法 CreateProductACreateProductB,即可创建一组相关的对象。具体的产品实现由具体工厂类负责。

END 链接