设计原则--开闭原则

73 阅读3分钟

1.没有使用开闭原则的代码

package main

import "fmt"

type phone struct {
   host      string
}

func (p *phone) photo() {
   fmt.Printf("%s", "这是"+p.host+"手机的拍照功能\n")
}
func (p *phone) sendInfo() {
   fmt.Printf("%s", "这是"+p.host+"手机的发短信功能\n")
}
func main() {
   p1 := phone{host: "xhz"}
   p1.photo()
}
  • 假设现在根据需求,手机加入听音乐的功能,手机要存下音乐名字
  • 这时候就一定会涉及到,在原来结构体里面加上新字段 musicName string,类似于下面这样
type phone struct {
   host      string
   musicName string
}
func (p *phone) listenMusic() {
   fmt.Printf("%s %s", "这是"+p.host+"手机的听音乐功能,听曲子--", p.musicName+"\n")
}
package main

import "fmt"

type phone struct {
   host      string
   musicName string
}

func (p *phone) photo() {
   fmt.Printf("%s", "这是"+p.host+"手机的拍照功能\n")
}
func (p *phone) sendInfo() {
   fmt.Printf("%s", "这是"+p.host+"手机的发短信功能\n")
}
func (p *phone) listenMusic() {
   fmt.Printf("%s %s", "这是"+p.host+"手机的听音乐功能,听曲子--", p.musicName+"\n")
}
func main() {
   p1 := phone{host: "xhz"}
   p1.photo()
   p2 := phone{host: "xhz", musicName: "我爱你中国"}
   p2.listenMusic()

}

缺点:涉及到变量的增加的时候,势必引起原结构体设计的改动
这种就很不符合"开闭原则"

2.使用开闭原则的代码

  • 既然不能动原来结构体的源代码,那么就只能把新字段 musicName 放进一个新的结构体
  • 用新的结构体绑定新的【听音乐功能】显然已经不符合要求
  • 因为这样就相当于新的结构体,有了自己的第一个功能

为了还是让【听音乐功能】和以前的功能形成同级关系,那就引入接口,把发短信,拍照抽象成一个work( )功能,原本函数名就能看出功能的事情就交给结构体名来体现,类似于下面这样:

type tool interface {
   work()
}

type photoPhone struct {
   name string
}

func (p *photoPhone) work() {
   fmt.Printf("%s", "这是"+p.name+"手机的拍照功能\n")
}

type sendInfoPhone struct {
   name string
}

func (s *sendInfoPhone) work() {
   fmt.Printf("%s", "这是"+s.name+"手机的发短信功能\n")
}

再有什么新的功能,那就再写一个新的结构体,实现work( ),这就是开闭原则(OCP)

开闭原则: 增加功能(涉及到新字段)不是通过改原来结构体解决的,而是写新结构体解决的

type listenMusicPhone struct {
   name      string
   musicName string
}

// 当然还可以进一步提炼出一个基于接口层封装的函数,因为现在大家都实现了work()函数嘛
func (l *listenMusicPhone) work() {
   fmt.Printf("%s %s", "这是"+l.name+"手机的听音乐功能,听曲子--", l.musicName+"\n")
}
// 就像这样
func allTools(t tool) {
   t.work()
}

所以完整的 开闭原则设计代码

package main

import "fmt"

type tool interface {
	work()
}

type photoPhone struct {
	name string
}

func (p *photoPhone) work() {
	fmt.Printf("%s", "这是"+p.name+"手机的拍照功能\n")
}

type sendInfoPhone struct {
	name string
}

func (s *sendInfoPhone) work() {
	fmt.Printf("%s", "这是"+s.name+"手机的发短信功能\n")
}

type listenMusicPhone struct {
	name      string
	musicName string
}

func (l *listenMusicPhone) work() {
	fmt.Printf("%s %s", "这是"+l.name+"手机的听音乐功能,听曲子--", l.musicName+"\n")
}

func allTools(t tool) {
	t.work()
}
func main() {
	p := photoPhone{name: "xhz"}
	p.work()
	allTools(&p)

	s := sendInfoPhone{name: "xhz"}
	s.work()
	allTools(&s)

	l := listenMusicPhone{name: "xhz", musicName: "我爱你中国"}
	l.work()
	allTools(&l)

}

总结:

要想做到开闭原则,那么就必须让函数放进接口里
因为只有这样才能让新的结构体实现函数的时候达到自定义的效果