创建型设计模式 #
单例模式 #
单例模式提供了一种访问其唯一对象的方法,该对象可以直接被访问,无需实例化
双重检查 #
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()
}