面向对象三大特性:封装、继承、多态。
设计模式需遵循面向对象的设计原则,由于本文是通过go语言实现的,所以需要先了解go中的面向对象是怎么样的。
Golang中的面向对象是通过struct结构体实现的,类似于C++和Java中的Class类。其中struct类似C++的普通类类型,interface则对应抽象类类型。对象的成员变量可见性则是通过大小写字母开头来区分。
Golang中的继承是通过组合来实现的,下例基类是Base,子类是Foo;子类可以直接调用基类的公有方法,子类也可以定义自己的属性以及实现自己的方法。
type Base struct {
Name string
}
func (base *Base) Bar() {
fmt.Println(base.Name)
}
type Foo struct {
Base
}
func (foo *Foo) Bar() {
foo.Base.Bar()
}
func main() {
f := Foo{Base{Name: "hello"}}
f.Bar()
}
// hello
Golang中的多态是通过interface实现的,下例通过interface抽象出了不同品牌手机的价格,具体则是通过不同手机品牌的类来实现该手机品牌的价格。
type Phone interface {
Price() float64
}
type Huawei struct {
price float64
}
func (h *Huawei) Price() float64 {
return h.price
}
type Xiaomi struct {
price float64
}
func (x *Xiaomi) Price() float64 {
return x.price
}
func NewXiaomiPrice(xiaomi *Xiaomi) Phone {
return xiaomi
}
func main() {
h := Huawei{price: 3000}
fmt.Println(h.Price())
x := Xiaomi{price: 2000}
fmt.Println(x.Price())
nx := NewXiaomiPrice(&Xiaomi{price: 2500})
fmt.Println(nx.Price())
}
// 3000
// 2000
// 2500
即一个类只对应一个职责,其职责是引起该类变化的原因,例如类T1只完成F1的功能,类T2只完成F2的功能,T1改变的时候不会影响F2,同理T2改变的时候不会影响F1,做到职责单一
type T1 struct {}
func (t *T1) F1() {}
type T2 struct {}
func (t *T2) F2() {}
func main() {
t1 := new(T1)
t1.F1()
t2 := new(T2)
t2.F2()
}
开闭原则即对程序的扩展是持开放态度的,对程序的修改是封闭态度的。目的是为了程序的扩展性好,易于维护,减少因为代码的多次修改而造成各种隐性bug,遵循高内聚,低耦合的原则。
在GO语言中需要用到interface字段对方法进行抽象,做到T1业务和Function和T2业务的Function互不影响,而且还可以扩展新业务而不用从之前的Function的修改
type Abstract interface {
Function()
}
type T1 struct {}
func (t *T1) Function() {
fmt.Println("t1业务")
}
type T2 struct {}
func (t *T2) Function() {
fmt.Println("t2业务")
}
func Business(abstract Abstract) {
abstract.Function()
}
func main() {
Business(new(T1)) // t1业务
Business(new(T2)) // t2业务
}
里氏代换原则规定子类不得重写父类的普通方法,只能重写父类的抽象方法;即子类可以扩展父类的功能,但是不能改变父类原有的功能
依赖倒置原则的定义为:高层模块不应该依赖低层模块,而是高层和低层都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。其核心思想是:要面向接口编程,不要面向实现编程。
例如下例就是耦合度极高的一个例子,固定死了peopleA play xiaomi games,peopleB play huawei games,如果我想peopleA play huawei games就还得写结构体类去实现,这样不利于扩展
type Xiaomi struct {}
func (x *Xiaomi) games() {
fmt.Println("xiaomi games")
}
type Huawei struct {}
func (x *Huawei) games() {
fmt.Println("huawei games")
}
type PeopleA struct {}
func (p *PeopleA) play(x *Xiaomi) {
fmt.Printf("peopleA play ")
x.games()
}
type PeopleB struct {}
func (p *PeopleB) play(h *Huawei) {
fmt.Printf("peopleB play ")
h.games()
}
func main() {
x := &Xiaomi{}
h := &Huawei{}
a := &PeopleA{}
a.play(x) // peopleA play xiaomi games
b := &PeopleB{}
b.play(h) // peopleB play huawei games
}
根据依赖倒转原则改进,人和手机可以分别抽象成两个接口(模块),模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生。下例中如果我想 peopleA play huawei games 可以直接在main函数中调用,而不用像上例还要手动实现类
type People interface {
play(phone Phone)
}
type Phone interface {
games()
}
type Xiaomi struct {}
func (x *Xiaomi) games() {
fmt.Println("xiaomi games")
}
type Huawei struct {}
func (x *Huawei) games() {
fmt.Println("huawei games")
}
type PeopleA struct {}
func (p *PeopleA) play(x Phone) {
fmt.Printf("peopleA play ")
x.games()
}
type PeopleB struct {}
func (p *PeopleB) play(h Phone) {
fmt.Printf("peopleB play ")
h.games()
}
func main() {
x := &Xiaomi{}
h := &Huawei{}
pa := &PeopleA{}
pb := &PeopleB{}
pa.play(x) // peopleA play xiaomi games
pb.play(h) // peopleB play huawei games
}
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。简单地说,就是使用多个专门的接口比使用单个接口要好很多。
如果使用继承,会导致父类的任何变换都可能影响到子类的行为,所以优先使用组合的方式代替继承的方式。
又叫最少知道原则,一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立;降低类之间的耦合度,提高模块的相对独立性
抽象工厂接口
+具体工厂
+抽象产品接口
+具体产品
优点:一个调用者想创建一个对象,只要知道其名称就可以了,易扩展
缺点:每次增加具体产品时需要新增实现,增加了代码量和复杂度
type Phone interface {
Show()
}
type AbstractFactory interface {
ProducePhone() Phone
}
// 手机模块
type Xiaomi struct {}
func (x *Xiaomi) Show() {
fmt.Println("xiaomi")
}
type Huawei struct {}
func (h *Huawei) Show() {
fmt.Println("huawei")
}
// 手机工厂模块
type XiaomiFactory struct {}
func (x *XiaomiFactory) ProducePhone() Phone {
return new(Xiaomi)
}
type HuaweiFactory struct {}
func (h *HuaweiFactory) ProducePhone() Phone {
return new(Huawei)
}
func main() {
f1 := new(XiaomiFactory)
f1.ProducePhone().Show() // 生产小米手机
f2 := new(HuaweiFactory)
f2.ProducePhone().Show() // 生产华为手机
}
在工厂方法模式中,每一个工厂只生产一类产品,导致大量工厂类的存在。
因此抽象工厂模式在工厂的维度又抽象了一层,使得增加新的产品族时(如增加一个厂商)方便,但是当新增产品等级结构(如Intel厂商下新增其他配件)时会修改原来的抽象层代码,违背了开闭原则;因此抽象工厂模式适用于产品族较多,且每一个产品族内的产品等级结构稳定的情况。
用抽象工厂模式实现下面例子:
设计一个电脑主板架构,电脑包括(显卡,内存,CPU)3个固定的插口,显卡具有显示功能(display,功能实现只要打印出意义即可),内存具有存储功能(storage),cpu具有计算功能(calculate)。现有Intel厂商,nvidia厂商,Kingston厂商,均会生产以上三种硬件。要求组装两台电脑:
// 抽象层
type AbstractGPU interface {
Display()
}
type AbstractCPU interface {
Calculate()
}
type AbstractMemory interface {
Storage()
}
// 抽象工厂
type AbstractFactory interface {
CreateGPU() AbstractGPU
CreateCPU() AbstractCPU
CreateMemory() AbstractMemory
}
// Intel 厂商
type IntelGPU struct{}
type IntelCPU struct{}
type IntelMemory struct{}
type IntelFactory struct{}
func (i *IntelGPU) Display() {
fmt.Printf("Intel GPU display,")
}
func (i *IntelCPU) Calculate() {
fmt.Printf("Intel CPU calculate,")
}
func (i *IntelMemory) Storage() {
fmt.Printf("Intel memory storage,")
}
func (i *IntelFactory) CreateIntelGPU() AbstractGPU {
return new(IntelGPU)
}
func (i *IntelFactory) CreateIntelCPU() AbstractCPU {
return new(IntelCPU)
}
func (i *IntelFactory) CreateIntelMemory() AbstractMemory {
return new(IntelMemory)
}
// Nvidia 厂商
type NvidiaGPU struct{}
type NvidiaCPU struct{}
type NvidiaMemory struct{}
type NvidiaFactory struct{}
func (n *NvidiaGPU) Display() {
fmt.Printf("Nvidia GPU display,")
}
func (n *NvidiaCPU) Calculate() {
fmt.Printf("Nvidia CPU calculate,")
}
func (n *NvidiaMemory) Storage() {
fmt.Printf("Nvidia memory storage,")
}
func (n *NvidiaFactory) CreateNvidiaGPU() AbstractGPU {
return new(NvidiaGPU)
}
func (n *NvidiaFactory) CreateNvidiaCPU() AbstractCPU {
return new(NvidiaCPU)
}
func (n *NvidiaFactory) CreateNvidiaMemory() AbstractMemory {
return new(NvidiaMemory)
}
// Kingston 厂商
type KingstonGPU struct{}
type KingstonCPU struct{}
type KingstonMemory struct{}
type KingstonFactory struct{}
func (k *KingstonGPU) Display() {
fmt.Printf("Kingston GPU display,")
}
func (k *KingstonCPU) Calculate() {
fmt.Printf("Kingston CPU calculate,")
}
func (k *KingstonMemory) Storage() {
fmt.Printf("Kingston memory storage,")
}
func (k *KingstonFactory) CreateKingstonGPU() AbstractGPU {
return new(KingstonGPU)
}
func (k *KingstonFactory) CreateKingstonCPU() AbstractCPU {
return new(KingstonCPU)
}
func (k *KingstonFactory) CreateKingstonMemory() AbstractMemory {
return new(KingstonMemory)
}
func main() {
// Intel的CPU,Intel的显卡,Intel的内存
intelFac := new(IntelFactory)
intelFac.CreateIntelCPU().Calculate()
intelFac.CreateIntelGPU().Display()
intelFac.CreateIntelMemory().Storage()
fmt.Println()
// Intel的CPU, nvidia的显卡,Kingston的内存
nvidiaFac := new(NvidiaFactory)
kingstonFac := new(KingstonFactory)
intelFac.CreateIntelCPU().Calculate()
nvidiaFac.CreateNvidiaGPU().Display()
kingstonFac.CreateKingstonMemory().Storage()
}
定义:保证一个类、只有一个实例存在,同时提供能对该实例访问的全局访问方法
var once sync.Once
var singleInstance *single
type single struct{} // 首字母小写,不对外暴露
func GetSingleInstance() *single {
if singleInstance == nil {
once.Do(func() { // 只会执行一次,即使在并发的情况下
fmt.Println("Create single instance.")
singleInstance = &single{}
})
} else {
// 之后使用已创建的实例
fmt.Println("Get single instance.")
}
return singleInstance
}
func main() {
for i := 0; i < 10; i++ {
go GetSingleInstance()
}
time.Sleep(time.Second * 60)
}
// Create single instance.
// Get single instance.
// Get single instance.
[1]. Title https://www.bilibili.com/video/BV1Eg411m7rV/?spm_id_from=333.999.0.0
[2]. Title https://refactoringguru.cn/