设计模式

创建

简单工厂模式

简单工厂模式主要实现了 通过工厂类来进行对象的创建,通过传入参数的不同创建不同具体产品类的实例。
使创建和使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦
更符合面向对象的原则和面向接口编程。但违背了开闭原则,添加新产品必须要修改工厂类的逻辑。

(go中使用工厂方法创建产品类)

优点:

  • 将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦;
  • 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。

缺点:

  • 工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响;
  • 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。
  • 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

场景:

  • 客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时;
  • 当工厂类负责创建的对象(具体产品)比较少时。

go 语言没有构造函数一说,所以一般会定义NewXXX函数来初始化相关类。
NewXXX 函数返回接口时就是简单工厂模式,也就是说Golang的一般推荐做法就是简单工厂。

  1. package main
  2. import "fmt"
  3. //简单工厂模式主要实现了 通过工厂类来进行对象的创建,通过传入参数的不同创建不同具体产品类的实例。
  4. //使创建和使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦。
  5. //更符合面向对象的原则和面向接口编程
  6. type car interface {
  7. say(name string) string
  8. }
  9. type car1 struct {
  10. }
  11. func (*car1) say(name string) string {
  12. return fmt.Sprint("car1 say : ", name)
  13. }
  14. type car2 struct {
  15. }
  16. func (*car2) say(name string) string {
  17. return fmt.Sprint("car2 say : ", name)
  18. }
  19. func simpleFactory(name int) car {
  20. if name == 1 {
  21. return &car1{}
  22. } else if name == 2 {
  23. return &car2{}
  24. }
  25. return nil
  26. }
  27. func main() {
  28. myCar := simpleFactory(1)
  29. fmt.Println(myCar.say("car1"))
  30. myCar = simpleFactory(2)
  31. fmt.Println(myCar.say("car2"))
  32. }

工厂方法模式*

又称工厂模式、多态工厂模式和虚拟构造器模式。

通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。

工厂方法模式使得工厂类不再负责所有产品的生产,而是定义所有子类工厂类必须实现的接口,这样添加新产品时就不需要修改工厂逻辑而是添加新的工厂子类,符合开放封闭原则。

  1. package main
  2. import "fmt"
  3. //工厂方法模式使得工厂父类只用定义工厂子类需要实现的接口,
  4. //添加新产品时只需要添加新的的工厂子类,不需要修改工厂父类的逻辑。
  5. //解决了简单工厂模式不符合开闭原则的问题。
  6. type factoryInterface interface {
  7. creatFactory(name string)
  8. }
  9. type productInterface interface {
  10. say()
  11. }
  12. //产品A
  13. type productA struct {
  14. }
  15. //实现了方法say
  16. func (*productA) say() {
  17. fmt.Println("hello, i am productA")
  18. }
  19. //工厂A
  20. type factoryA struct {
  21. }
  22. //工厂实现了creat方法
  23. func (*factoryA) creatFactory() productInterface {
  24. return &productA{}
  25. }
  26. //产品B
  27. type productB struct {
  28. }
  29. //产品B实现了say方法
  30. func (*productB) say() {
  31. fmt.Println("hello, i am productB")
  32. }
  33. //B工厂
  34. type factoryB struct {
  35. }
  36. //产品B工厂实现了creat方法
  37. func (*factoryB) creatFactory() productInterface {
  38. return &productB{}
  39. }
  40. func main() {
  41. //常见子类工厂实例
  42. fA := &factoryA{}
  43. //通过子类工厂创建产品
  44. proA := fA.creatFactory()
  45. //调用B的say方法
  46. proA.say()
  47. fB := &factoryB{}
  48. //通过子类工厂创建产品
  49. proB := fB.creatFactory()
  50. //调用B的say方法
  51. proB.say()
  52. }
  53. //////////////////////
  54. hello, i am productA
  55. hello, i am productB

image-20221109233627976

抽象工厂模式*

建造者模式

通过主管类来管理各个步骤,将具体的建造者作为参数传递给构造函数,构造函数再将参数传递给对应的主管类,最后由主管类完成后续建造任务。

建造者模式隐藏了复合对象的创建过程,不同的创建者builder有着不同的创建方法。

建造者模式解决的问题是构建和组装的解耦,用户无需关注复杂对象的创建过程,只需要指定复杂对象的类型就可以得到该对象。

image-20221110135429259

  1. package main
  2. import "fmt"
  3. //建造者模式主要有主管来进行各个部分的管理,首先将建造者子类作为参数传递给构造函数,构造函数内将参数传递给主管类。
  4. //获得主管类后,主管类通过建造者子类的不同来完后后续的创建工作
  5. //建造者模式 builder 解决的问题是 构造和装配的解耦,用户无须担心复杂对象的创建过程,只需要指定复杂对象的类型即可得到该对象。
  6. //建造者接口 规定建造者子类需要实现的方法
  7. type Builder interface {
  8. part1()
  9. part2()
  10. part3()
  11. }
  12. //主管类
  13. type Director struct {
  14. builder Builder
  15. }
  16. func (d *Director) Construct() {
  17. d.builder.part1()
  18. d.builder.part2()
  19. d.builder.part3()
  20. }
  21. //构造函数
  22. //传入子类建造者类型 返回一个包含其的主管类
  23. func newDirector(builder Builder) *Director {
  24. return &Director{
  25. builder: builder,
  26. }
  27. }
  28. //建造者子类
  29. type builder1 struct {
  30. }
  31. //建造者需要实现的方法
  32. func (*builder1) part1() {
  33. fmt.Println("hello, this is part1")
  34. }
  35. func (*builder1) part2() {
  36. fmt.Println("hello, this is part2")
  37. }
  38. func (*builder1) part3() {
  39. fmt.Println("hello, this is part3")
  40. }
  41. func main() {
  42. //获取建造者子类
  43. builder := &builder1{}
  44. dir := newDirector(builder)
  45. dir.Construct()
  46. }

单例模式singleton*

解决的问题:整个运行时域,一个类只有一个实例对象,并且该对象的功能依旧能被其他模块使用。

内部生成的单例对外部私有,只有通过对外部暴露的get方法才能获取该实例。

由于有的类比较庞大,频繁的销毁和创建将会造成不必要的性能浪费。(比如数据库链接对象)因此需要单例模式,在系统中只存在一个可控对象,从而节约系统资源。

优点:单例模式提供了对唯一实例的受控访问。在系统内存中只存在一个对象,节约了系统资源。

缺点:单例类的职责过重,拓展略难

饿汉式:

初始化单例唯一指针时,就已经提前提前开辟好对象申请了内存。

好处:不会出现多线程并发创建,导致多个单例的出现。

缺点:无论该单例对象是否被使用,都会创建该单例对象。

  1. package main
  2. //饿汉式单例 无论使用或者不使用,单例都会创建出来
  3. import "fmt"
  4. type singleton struct {
  5. }
  6. func (s *singleton) say() {
  7. fmt.Println("这里是单例的方法")
  8. }
  9. //对外部私有,只有对外暴露的get方法能够获取该对象
  10. var instance *singleton = new(singleton)
  11. //对外提供一个方法获取这个对象
  12. //去掉了写权限,只暴露读方法
  13. func GetInstance() *singleton {
  14. return instance
  15. }
  16. func main() {
  17. i := GetInstance()
  18. i.say()
  19. }

懒汉式:

通过get方法获取单例对象,对get方法加锁从而避免并发下多次创建单例对象,第一次使用get方法会开辟对象并申请内存,之后使用将直接返回单例对象。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. //定义一个锁
  7. //var lock sync.Mutex
  8. var once sync.Once
  9. var instance *singleton
  10. type singleton struct {
  11. }
  12. //once 是线程安全的
  13. func GetInstance() *singleton {
  14. //once.Do() 内的函数只能执行一次
  15. //只有第一次才会执行创建单例
  16. once.Do(func() {
  17. instance = new(singleton)
  18. })
  19. //之后的都会直接返回单例
  20. return instance
  21. }
  22. func (s *singleton) Say() {
  23. fmt.Println("hello")
  24. }
  25. func main() {
  26. in := GetInstance()
  27. in.Say()
  28. }

Once.Do()方法的源代码:

  1. func (o *Once) Do(f func()) {   //判断是否执行过该方法,如果执行过则不执行
  2. if atomic.LoadUint32(&o.done) == 1 {
  3. return
  4. }
  5. // Slow-path.
  6. o.m.Lock()
  7. defer o.m.Unlock()  
  8. if o.done == 0 {
  9. defer atomic.StoreUint32(&o.done, 1)
  10. f()
  11. }
  12. }

结构型

代理模式

代理模式为某个目标对象提供一个代理对象,并且由代理对象控制对目标对象的访问,代理模式用于延迟处理操作或者在进行实际操作前后进行其它处理

作用:

  1. 代理模式在客户端和目标对象之间起到中介的作用和保护目标对象的作用
  2. 代理对象可以拓展目标对象的作用,只需要修改代理类不需要修改目标对象,符合开闭原则。并且如果需要修改目标对象,因为实现了接口,不需要修改代理类,同样符合开闭原则。
  3. 代理模式可以将客户端与目标对象分离,降低了系统耦合

抽象主题类:真实主题和代理主题的共同接口

真实主题类:代理对象所代表的真实对象

proxy 代理类:含有真实主题的引用,代理角色通常在客户端调用传递给真实主题对象之前或者之后执行某些操作,可以访问、控制、拓展真实主题的功能。

image-20221112141138797

  1. package main
  2. import "fmt"
  3. //代理模式
  4. type Goods struct {
  5. Kind string //商品种类
  6. Fact bool //商品的真伪
  7. }
  8. //抽象层
  9. type Shopping interface {
  10. Buy(good *Goods) //某任务
  11. }
  12. //海外代理、韩国购物、美国购物都实现了shopping
  13. //他们都是Shopping
  14. //实现层
  15. type KoreaShopping struct{}
  16. func (ks *KoreaShopping) Buy(goods *Goods) {
  17. fmt.Println("去韩国购物,买了:", goods.Kind)
  18. }
  19. type AmericaShopping struct{}
  20. func (As *AmericaShopping) Buy(goods *Goods) {
  21. fmt.Println("去美国购物,买了:", goods.Kind)
  22. }
  23. //海外代理
  24. type OverSeasProxy struct {
  25. Shopping Shopping //代理某个主题 ,这里是抽象的数据类型
  26. }
  27. //new
  28. func NewProxy(shopping Shopping) Shopping {
  29. return &OverSeasProxy{Shopping: shopping}
  30. }
  31. func (op *OverSeasProxy) Buy(goods *Goods) {
  32. //1 辨别真伪
  33. if op.distinguish(goods) == true {
  34. //2 调用具体要被代理的购物buy方法
  35. op.Shopping.Buy(goods)
  36. //3 海关安检
  37. op.check(goods)
  38. }
  39. }
  40. func (op *OverSeasProxy) distinguish(goods *Goods) bool {
  41. fmt.Println("对:", goods.Kind, "进行了辨别真伪。")
  42. if goods.Fact == false {
  43. fmt.Println("发现假货:", goods.Kind, " ,不应该购买")
  44. }
  45. return goods.Fact
  46. }
  47. func (op *OverSeasProxy) check(goods *Goods) {
  48. fmt.Println("对", goods.Kind, " 进行了海关检查")
  49. }
  50. func main() {
  51. //根据对象类型的不同 代理方法中的某个子方法也会不同
  52. g1 := Goods{
  53. Kind: "韩国面膜",
  54. Fact: true,
  55. }
  56. g2 := Goods{
  57. Kind: "四级证书",
  58. Fact: false,
  59. }
  60. //创建一个shopping对象
  61. var KShopping Shopping
  62. KShopping = new(KoreaShopping)
  63. //传入具体的shopping对象,得到代理
  64. //将具体的类转换为代理类
  65. var k_proxy Shopping
  66. k_proxy = NewProxy(KShopping)
  67. //通过代理模式进行动作
  68. k_proxy.Buy(&g1)
  69. k_proxy.Buy(&g2)
  70. var AShopping Shopping
  71. AShopping = new(AmericaShopping)
  72. var A_proxy Shopping
  73. A_proxy = NewProxy(AShopping)
  74. A_proxy.Buy(&g1)
  75. }
  76. ////////////////////
  77. 对: 韩国面膜 进行了辨别真伪。
  78. 去韩国购物,买了: 韩国面膜
  79. 对 韩国面膜 进行了海关检查
  80. 对: 四级证书 进行了辨别真伪。
  81. 发现假货: 四级证书 ,不应该购买
  82. 对: 韩国面膜 进行了辨别真伪。
  83. 去美国购物,买了: 韩国面膜
  84. 对 韩国面膜 进行了海关检查

装饰器

装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。

当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

优点:

(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

(2) 可以通过一种动态的方式来扩展一个对象的功能,从而实现不同的行为。

(3) 可以对一个对象进行多次装饰。

(4) 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

缺点:

(1) 使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,影响程序的性能。

(2) 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐。

  1. package main
  2. import "fmt"
  3. type Phone interface {
  4. Show() //构件的功能
  5. }
  6. //装饰器
  7. //包含具体被装饰的类
  8. type Decorator struct {
  9. phone Phone
  10. }
  11. func (d *Decorator) Show() {}
  12. //实现层
  13. type Man struct{}
  14. func (*Man) Show() {
  15. fmt.Println("this is man")
  16. }
  17. //具体的装饰器
  18. //继承装饰器基础类
  19. type GunDecorator struct {
  20. Decorator
  21. }
  22. func (gun *GunDecorator) Show() {
  23. gun.phone.Show()
  24. fmt.Println("A man with a gun ")
  25. }
  26. func newGunDecorator(ph Phone) *GunDecorator {
  27. return &GunDecorator{Decorator{ph}}
  28. }
  29. func main() {
  30. //
  31. var xiaoming Phone
  32. xiaoming = new(Man)
  33. xiaoming.Show()
  34. fmt.Println("--------")
  35. //加入装饰器
  36. var gunMan Phone
  37. gunMan = newGunDecorator(xiaoming)
  38. gunMan.Show()
  39. }

适配器模式

外观模式*