1、适配器模式
(1)什么是适配器模式?
适配器模式作为两个不兼容的接口之间的桥梁,将一个接口转换为我们希望的另一个接口。
适配器模式其实是一种结构型模式,我们读源码过程中接触到的Adapter、Wrapper基本都是适配器模式的实现。
适配器分为两类:类适配器(采用多继承方式)和对象适配器(采用组合方式)。
适配器模式的角色:Target(目标接口)、Adaptee(被适配的对象)、Adaptor(适配器)。
(2)生活场景实例
小明的书包一般是有夹层的,书籍放到书包里,考试试卷放到书包夹层里。小明的妈妈给小明在淘宝上买了一个书包,小明发现这个书包没有夹层。
package main
import "fmt"
type Backpack interface {
putTheBook([]string)
}
type backpack struct {
}
func (bp *backpack) putTheBook(books []string) {
for _, book := range books {
fmt.Println("把%s放入书包里面\n", book)
}
}
type TargetBackpack interface {
putTheBook([]string, []string)
}
type targetBackpack struct {
}
func (tb *targetBackpack) putTheBook(books []string, papers []string){
for _, book := range books {
fmt.Println("把%s放入书包里面\n", book)
}
for _, paper := range papers {
fmt.Println("把%s放入书包夹层里面\n", paper)
}
}
func main() {
books := []string{"语文书", "数学书", "英语书"}
papers := []string{"语文试卷", "数学试卷"}
bp := &backpack{}
tb := &targetBackpack{}
bp.putTheBook(books)
fmt.Println("=======================")
tb.putTheBook(books, papers)
小明的妈妈于是想了个办法,找了一块和书包同材质的布料,然后手工做了一个夹层,使这个书包变成了夹层书包。这里的角色分为:Target(有夹层的书包)、Adaptee(普通书包)、Adapter(夹层)。
package main
import "fmt"
type Backpack interface {
putTheBook([]string)
}
type backpack struct {}
func (bp *backpack) putTheBook(books []string) {
for _, book := range books {
fmt.Println("把%s放入书包里面\n", book)
}
}
type Adapter interface {
putTheBook([]string, []string)
}
type adapter struct {
bp backpack
}
func (ad *adapter) putTheBook(books []string, papers []string) {
ad.bp.putTheBook(books)
for _, paper := range papers {
fmt.Println("把%s放入书包夹层里面\n", paper)
}
}
func main() {
books := []string{"语文书", "数学书", "英语书"}
papers := []string{"语文试卷", "数学试卷"}
bp := &backpack{}
ad := &adapter{}
bp.putTheBook(books)
fmt.Println("=======================")
ad.putTheBook(books, papers)
}
(3)适配器模式和装饰者模式的区别
1)适配器模式Adaptee和Target的接口形式不同,而装饰者模式的装饰者和被装饰者的接口形式相同,即接口下的方法的名字和参数列表相同。
2)Adapter保存了Adaptee的引用,接收调用端的请求,并且这些请求最终都会调用Adaptee的接口。
(4)grpc中的应用
func (d *dnsResolver) lookupSRV() []resolver.Address {
var newAddrs []resolver.Address
_, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host)
if err != nil {
grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
return nil
}
...
return newAddrs
}
2、单例模式
(1)什么是单例模式?
单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是最简单的设计模式之一,属于创建型模式。单例模式被广泛运用在资源共享的场景,避免对象的频繁创建和回收。比如日志打印、数据库连接池等。
单例模式的实现方式:私有化构造方法。
(2)生活场景实例
小明的班级为了方便管理,老师指定了小明为班长。同学们有任何需要帮助的地方先告诉班长,班长解决不了再汇报给老师。
package main
import "fmt"
type Monitor interface {
Help(string)
}
type Member interface {
AskForHelp(string)
}
type monitor struct{
name string
}
func (mo *monitor) Help(name string) {
fmt.Println("%s帮助了%s", mo.name, name)
}
type member struct {
name string
}
func (mem *member) AskForHelp(name string) {
mo := &monitor{
name : name,
}
fmt.Println("%s寻求了%s的帮助", mem.name, name)
mo.Help(mem.name)
}
这一天,张三在学业上碰到了困难,于是找班长小明寻求帮助。
func main() {
zhangsan := &member{
name : "张三",
}
zhangsan.AskForHelp("小明")
}
同时,李四和王五因为闹矛盾,也来找班长小明评理。
func main() {
zhangsan := &member{
name : "张三",
}
zhangsan.AskForHelp("小明")
lisi := &member{
name : "李四",
}
lisi.AskForHelp("小明")
wangwu := &member {
name : "王五",
}
wangwu.AskForHelp("小明")
}
在以上代码中,班长小明被实例化了多次,如果班级有许多同学,每个同学来寻求小明的帮助,就会在内存中产生很多的小对象。解决方式:
package main
import "fmt"
type Monitor interface {
Help(string)
}
type Member interface {
AskForHelp(string)
}
type monitor struct{
name string
}
func (mo *monitor) Help(name string) {
fmt.Println("%s帮助了%s", mo.name, name)
}
type member struct {
name string
}
func (mem *member) AskForHelp(name string) {
mo = GetMonitor(name)
fmt.Println("%s寻求了%s的帮助", mem.name, name)
mo.Help(mem.name)
}
var mo *monitor
func GetMonitor(name string) *monitor{
if mo != nil {
return mo
}
mo = &monitor {
name : name,
}
return mo
}
func main() {
zhangsan := &member{
name : "张三",
}
zhangsan.AskForHelp("小明")
lisi := &member{
name : "李四",
}
lisi.AskForHelp("小明")
wangwu := &member {
name : "王五",
}
wangwu.AskForHelp("小明")
}
(3)线程安全
方式一:静态加载
var mo = &monitor {
name : "小明"
}
var mu sync.Mutex
func GetMonitor(name string) *monitor{
if mo != nil {
return mo
}
return mo
}
方式二:加锁
var mo *monitor
var mu sync.Mutex
func GetMonitor(name string) *monitor{
mu.Lock() // 加锁
defer mu.Unlock() // 在return语句之前一定会执行该方法
if mo != nil {
return mo
}
mo = &monitor {
name : name,
}
return mo
}
方式三:双重校验
var mo *monitor
var mu sync.Mutex
func GetMonitor(name string) *monitor{
if mo != nil {
return mo
}
mu.Lock()
defer mu.Unlock()
if mo != nil { // 双重校验
return mo
}
mo = &monitor {
name : name,
}
return mo
}
方式四:once.Do
var mo *monitor
var once sync.Once
func GetMonitor(name string) *monitor{
once.Do(func() {
mo = &monitor{
name : name,
}
})
return mo
}
main函数:
import (
"fmt"
"log"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(3)
go func() {
zhangsan := &member {
name : "张三",
}
zhangsan.AskForHelp("小红")
defer wg.Done()
}()
go func() {
lisi := &member {
name : "李四",
}
lisi.AskForHelp("小明")
defer wg.Done()
}()
go func() {
wangwu := &member {
name : "王五",
}
wangwu.AskForHelp("小王")
defer wg.Done()
}()
wg.Wait()
log.Println("singleton")
}
(4)go log中的应用
var std = New(os.Stderr, "", LstdFlags)
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...))
}
3、工厂模式
(1)什么是工厂模式?
工厂模式定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂模式属于创建型模式,工厂模式的初衷就是为了在创建对象时不会对调用方暴露创建逻辑。
工厂模式分为简单工厂模式、工厂方法模式和抽象工厂模式。
工厂模式是一种从简单到抽象的过程。
(2)生活场景实例
在小明的学校,每一年开学都会发教材,主要包括语文书、数学书、英语书,还有各种练习试卷。这一天,小明去领了三本教材,分别是语文书、数学书和英语书,老师忙不过来,指定某个同学去发书,同学们都去这个同学这里去领书。这个同学就是工厂。
package main
import "fmt"
type Book interface {
Name() string
}
type chineseBook struct {
name string
}
func (c *chineseBook) Name() string {
return c.name
}
type mathBook struct {
name string
}
func (m *mathBook) Name() string {
return c.name
}
type englishBook struct {
name string
}
func (e *englishBook) Name() string {
return c.name
}
type Person interface {
GetBook(string) Book
}
type person struct {}
func (p *person) GetBook(name string) Book {
if name == "语文书" {
return &chineseBook{
name : name,
}
}
if name == "数学书" {
return &mathBook{
name : name,
}
}
if name == "英语书" {
return &englishBook{
name : name,
}
}
return nil
}
func main() {
// 不用工厂模式的写法
c := &chineseBook {
name : "语文书",
}
fmt.Println("小明领取了一本%s", c.Name())
m := &mathBook {
name : "数学书"
}
fmt.Println("小明领取了一本%s", m.Name())
e := &englishBook {
name : "英语书"
}
fmt.Println("小明领取了一本%s", e.Name())
// 简单工厂模式的写法
p := &person{}
fmt.Println("小明领取了一本%s", p.GetBook("语文书").Name())
fmt.Println("小明领取了一本%s", p.GetBook("数学书").Name())
fmt.Println("小明领取了一本%s", p.GetBook("英语书").Name())
}
老师分别指定语文、数学、英语课代表,由课代表去发书。
package main
import "fmt"
type Book interface {
Name() string
}
type chineseBook struct {
name string
}
func (c *chineseBook) Name() string {
return c.name
}
type mathBook struct {
name string
}
func (m *mathBook) Name() string {
return c.name
}
type englishBook struct {
name string
}
func (e *englishBook) Name() string {
return c.name
}
type Person interface {
GetBook(string) Book
}
type chineseAssistant struct {}
func (c *chineseAssistant) GetBook(name string) Book {
if name == "语文书" {
return &chineseBook{
name : name,
}
}
return nil
}
type mathAssistant struct {}
func (m *mathAssistant) GetBook(name string) Book {
if name == "数学书" {
return &mathBook{
name : name,
}
}
return nil
}
type englishAssistant struct {}
func (e *englishAssistant) GetBook(name string) Book {
if name == "英语书" {
return &englishBook{
name : name,
}
}
return nil
}
func main() {
// 工厂方法模式的写法:将大工厂实例化为子工厂
c := &chineseAssistant{}
fmt.Println("小明领取了一本%s", c.GetBook("语文书").Name())
m := &mathAssistant{}
fmt.Println("小明领取了一本%s", m.GetBook("数学书").Name())
e := &englishAssistant{}
fmt.Println("小明领取了一本%s", e.GetBook("英语书").Name())
}
每个学生除了领教材,还要领试卷。
package main
import "fmt"
type Book interface {
Name() string
}
type Paper interface {
Name() string
}
type chineseBook struct {
name string
}
func (c *chineseBook) Name() string {
return c.name
}
type chinesePaper struct {
name string
}
func (c *chinesePaper) Name() string {
return c.name
}
type mathBook struct {
name string
}
func (m *mathBook) Name() string {
return c.name
}
type mathPaper struct {
name string
}
func (m *mathPaper) Name() string {
return m.name
}
type englishBook struct {
name string
}
func (e *englishBook) Name() string {
return c.name
}
type englishPaper struct {
name string
}
func (e *englishPaper) Name() string {
return e.name
}
type Person interface {
GetBook(string) Book
GetPaper(string) Paper
}
type chineseAssistant struct {}
func (c *chineseAssistant) GetBook(name string) Book {
if name == "语文书" {
return &chineseBook{
name : name,
}
}
return nil
}
func (c *chineseAssistant) GetPaper(name string) Paper {
if name == "语文试卷" {
return &chinesePaper{
name : name,
}
}
}
type mathAssistant struct {}
func (m *mathAssistant) GetBook(name string) Book {
if name == "数学书" {
return &mathBook{
name : name,
}
}
return nil
}
func (m *mathAssistant) GetPaper(name string) Paper {
if name == "数学试卷" {
return &mathPaper{
name : name,
}
}
}
type englishAssistant struct {}
func (e *englishAssistant) GetBook(name string) Book {
if name == "英语书" {
return &englishBook{
name : name,
}
}
return nil
}
func (e *englishAssistant) GetPaper(name string) Paper {
if name == "英语试卷" {
return &englishPaper{
name : name,
}
}
}
func main() {
// 抽象工厂模式的写法:将大工厂实例化为子工厂,有更多的抽象方法
c := &chineseAssistant{}
fmt.Println("小明领取了一本%s", c.GetBook("语文书").Name())
fmt.Println("小明领取了一张%s", c.GetBook("语文试卷").Name())
m := &mathAssistant{}
fmt.Println("小明领取了一本%s", m.GetBook("数学书").Name())
fmt.Println("小明领取了一张%s", m.GetBook("数学试卷").Name())
e := &englishAssistant{}
fmt.Println("小明领取了一本%s", e.GetBook("英语书").Name())
fmt.Println("小明领取了一张%s", e.GetBook("英语试卷").Name())
}
(3)简单工厂模式和抽象工厂模式的区别
简单工厂:直接提供工厂对象,创建产品实例。
抽象工厂:提供一系列创建产品实例的接口定义,具体怎么创建由子工厂实现。
(4)go CollatorFactory中的应用
func getCollator(name, locale string) Collator {
for _, f := range collators{
if f.name == name {
col, err := f.makeFn(locale)
if err != nil {
log.Fatal(err)
}
return col
}
}
log.Fatalf("collator of type %q not found", name)
return nil
}