1.说明

前面5篇文章讲解了设计模式的语法、面向对象分析、原则、代码编写、类图表示法,从本文开始讲述23种设计模式。
后面会按照创建型、结构型、行为型的顺序来写

  1. 创建型5个:单例模式、简单工厂、工厂模式、建造者模式、原型模式 ,主要解决“对象的创建”问题
  2. 结构型7:代理模式、桥接模式、装饰器模式、适配器模式、门面模式 、组合模式、享元模式,主要解决“类或对象的组合或组装”问题
  3. 行为型11:观察者模式、模板模式、策略模式、职责链模式、状态模式、迭代器模式、访问者模式、备忘录模式、命令模式、解释器模式、中介模式,主要解决“类或对象之间的交互”问题

有7个模式并不太常用,他们分别是:组合模式、享元模式、状态模式、访问者模式、命令模式、解释器模式、中介模式,所以常用的设计模式16个。
每篇文章尽量都会有类图、定义、分析、使用场景、实现、代码、扩展等信息。之所以包含这些信息,因为对于很多人来说,你问他个具体的设计原则、思想、模式的原理和实现,他都能回答得头头是道,但是,在实际的项目开发中,写出来的代码质量还是很差。这种情况出现的原因还是,相关的知识点都过于抽象,通俗点讲就是有点“假大空”,不够具体、不太能落地,所以导致理论和实践容易脱节。

2.定义

2.1单例模式

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
UML类图:链接为https://www.processon.com/view/link/6080def6079129456d4beecf
image.png

2.2分析

通过分析能够看出以下几点:

  1. 单例模式能够保证类只有一个实例,就是类图里的instance
  2. 有方法能够让外部访问到该实例,就是类图里的GetInstance
  3. 外部无法创建实例,是因为构造函数为私有函数,外部无法访问,自然也就无法生成实例

    3.使用场景

    单例模式理解相对简单,一般在哪些场景下我们会用到单例模式呢?

  4. 多线程情况下会导致资源访问冲突

    • 如项目需要写Log,而且Log一般会写入同一个文件。如果存在多个Log对象,即使Log有对象级别的锁,但在多线程下完全无用,日志仍然会乱序。解决这个问题我们可以使用类级别锁、分布式锁,但是都相对麻烦一些。如果使用单例模式,则Log只需保持对象级别锁就可以解决资源访问冲突
  5. 需要保证全局唯一的类

    • 比如配置类,这种只应该存在一份

      4.代码

      单例模式的实现的时候需要考虑如下问题:
  6. 对象创建时线程安全问题

  7. 是否支持延迟加载
  8. getInstance()性能是否高

根据语言不同,实现方式也不一样,一般有饿汉式、懒汉式、双重检测、静态内部类、枚举等。无论使用哪种方式,核心目的都是为了只会生成一个实例
这里多少解释一下饿汉式和懒汉式。饿汉式可以简单的理解为实例提前创建好了,getInstance只是获取实例返回。懒汉式是调用getInstance的时候,getInstance负责生成唯一实例。两者各有优缺点,饿汉式不必考虑线程安全问题,实例生成的成本放在项目启动时,但不支持延迟加载;懒汉式需要考虑线程安全问题,支持延迟加载。
具体实现以前在文章Go单例实现方案中写过,实现方式比较简单

  1. /**
  2. @date: 2021/4/22
  3. 单例模式
  4. **/
  5. package design
  6. import (
  7. "fmt"
  8. "sync"
  9. )
  10. type singleTon struct {
  11. }
  12. func (s *singleTon) Show() {
  13. fmt.Println("hello world")
  14. }
  15. var (
  16. once sync.Once
  17. single *singleTon
  18. )
  19. func GetSingleInstance() *singleTon {
  20. once.Do(func() {
  21. single = &singleTon{}
  22. })
  23. return single
  24. }
  25. func main() {
  26. single := design.GetSingleInstance()
  27. single.Show()
  28. }

关于代码,此处说明几点:

  1. 使用sync.Once.Do,轻松解决线程安全问题,确保只会有一个实例
  2. singleTon类首字母需要小写,这样能够保证非design的包无法创建单例类。如下图所示,main包无法获取到singleTon类

image.png 3. 此处的实现方式是懒汉式,如果想使用饿汉式,可以使用init或者项目启动初始化时直接调用,具体实现大家可自己完成 4. 具体代码可以查看:https://github.com/shidawuhen/asap/blob/master/controller/design/6single.go

5.总结

单例模式作为简单、常用的模式是一定需要掌握的。但单例模式也有一些缺点,如在继承、多态方面能力会弱一些,是否使用单例模式需要依据具体情况而定。另外单例模式也可以扩展到集群环境下、可以扩展为多例模式,这些内容大家有兴趣可以研究一下。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/