写点什么

设计模式总结(golang 版)

用户头像
2流程序员
关注
发布于: 2020 年 06 月 24 日

几点感受

这是极客时间第 0 期架构师训练营第三周学习总结。

这周主要讲了几种常用设计模式。学完后,有以下几点感受:

  1. 设计模式用起来感觉不够直接(我做事喜欢单刀直入),但却是保证代码可扩展、可维护和可协作的基础。所以写代码前还是需要做一些必要的设计。

  2. 学习设计模式最好使用像Java或C++这样的面向对象的“原教旨主义”语言。太新的语言,如:Python、Golang,在其语言特性中就包含了一些设计模式的思想,如:Python的装饰器,Go的面相接口编程。

  3. 平时工作中用到的一些编程技巧和方法其实都有对应的模式。前人已经为我们总结好了,日用而不自知。

所以,还是要多学习,多思考,多实践。

设计模式总结

简单工厂



// awesomeProject/sorting/sortAlgos.py
package SorterFactory
import "fmt"
// 定义接口
type Sorter interface {
Sort([]interface{})
}
// 冒泡排序
type bubbleSorter struct { // 首字母小写,确保外部只能用GetSorter获得实例
}
//冒泡排序实现Sorter接口
func (bbs *bubbleSorter) Sort(sortable []interface{}) {
fmt.Print("冒泡排序算法实现。。。")
}
// 其他排序算法
// type insertSorter struct{}
// 获得算法实例
func GetSorter() Sorter {
// 根据配置获得对应的算法实例并返回
sorter := bubbleSorter{}
return &sorter
}



// main.go
package main
import SorterFactory "awesomeProject/sorting"
func main() {
sorter := SorterFactory.GetSorter()
sorter.Sort([]interface{}{1,5,3,7,2})
}

简单工厂特点:

优点:

  1. 满足开闭原则(OCP)

  • 抽象:调用者和被调用者依赖抽象接口

  • 动态编程:运行时确定被调用的实例类型

缺点:

  1. 缺少编译时类型安全

  2. 限制了Sorter的实现智能通过“默认构造函数”创建

  3. 如果不同实现类需要传递不同参数会变得麻烦

单例模式

懒汉模式

package singleton
import "sync"
type singleton1 struct { // 首字母小写,确保外部无法直接实例化
}
var once sync.Once
var instance *singleton1
func GetInstance() *singleton1 {
once.Do(func() { // 确保只执行一次,且执行时加锁
instance = &singleton1{}
})
return instance
}

饿汉模式

package singleton
import "sync"
type singleton2 struct { // 首字母小写,确保外部无法直接实例化
id string
}
var once sync.Once
var instance *singleton2
func init() { // 加载时执行
once.Do(func() { // 确保只执行一次,且执行时加锁
instance = &singleton2{}
})
}
func GetInstance() *singleton2 {
return instance
}

适配器模式

该模式适用于,被调用者的接口已经定型的情况下(如:已经在运行的服务),而调用者定义的接口又不兼容被调用者提供的接口,这时可以利用一个适配器类提供接口转换功能。



//定义button和处理button事件的接口
package main
import "fmt"
type ButtonServer interface {
ButtonPressed(int) // 键被按下后调用
}
// button 类
type Button struct {
token int // 按键值
buttonServer ButtonServer // 处理按键事件的对象
}
// 按键被按下
func (b *Button) Pressed() {
fmt.Println("key:", b.token, " pressed.")
b.buttonServer.ButtonPressed(b.token) // 调用按键事件处理程序
}



// 定义dialer
package main
import "fmt"
type Dailer struct {
numbers []int // 存放待拨电话号码
}
// 按下数字键
func (d *Dailer) EnterDigit(token int) {
d.numbers = append(d.numbers, token)
}
// 发送
func (d *Dailer) Dail() {
fmt.Println("call number:", d.numbers)
d.numbers = []int{}
}



// 定义适配器
package main
import "fmt"
// 数字键适配器
type DigitButtonDailerAdapter struct {
dailer *Dailer
}
func (b *DigitButtonDailerAdapter) ButtonPressed(token int) {
b.dailer.EnterDigit(token)
}
// 拨号键适配器
type SendButtonDailerAdapter struct {
dailer *Dailer
}
func (b *SendButtonDailerAdapter) ButtonPressed(token int) {
b.dailer.Dail()
}



package main
// 测试
func main() {
dailer := &Dailer{[]int{}}
digitButtonDailerAdapter := &DigitButtonDailerAdapter{dailer}
sendButtonDailerAdapter := &SendButtonDailerAdapter{dailer}
// 初始化数字键
digitButtons := [10]*Button{}
for i := 0; i <= 9; i++ {
digitButtons[i] = &Button{i, digitButtonDailerAdapter}
}
//初始化send键
sendButton := &Button{-99, sendButtonDailerAdapter}
// 输入电话号码
for _, n := range []int{1, 3, 8, 3, 4, 5, 6, 7, 8, 9, 0} {
digitButtons[n].Pressed()
}
sendButton.Pressed()
}

模版方法

通过“继承”来实现扩展:基类负责算法的轮廓和骨架;子类负责算法的具体实现。

注意:适当使用该模式,多用组合,慎用继承。

Golang不支持严格意义上的继承,对于该模式我认为网上的方法更像是策略模式。

策略模式

系统需要在多种算法中选择一种时,由调用者以参数的形式将算法实现传递给被调用者。

实现一个计算器:



package main
import "fmt"
// 策略抽象
type Strategy interface {
DoOperation(int, int) int
}
// 加法实现
type OperationAdd struct {
}
func (add *OperationAdd) DoOperation(num1, num2 int) int {
return num1 + num2
}
// 减法实现
type OperationSub struct {
}
func (sub *OperationSub) DoOperation(num1, num2 int) int {
return num1 - num2
}
// 乘法实现
type OperationMulti struct {
}
func (multi *OperationMulti) DoOperation(num1, num2 int) int {
return num1 * num2
}
// 控制器
type Context struct {
num1, num2 int
strategy Strategy
}
func (c *Context) ExecuteStrategy() int {
return c.strategy.DoOperation(c.num1, c.num2)
}
// 客户端
func main() {
context := Context{10,5,new(OperationAdd)}
fmt.Println(context.ExecuteStrategy())
context = Context{10,5,new(OperationSub)}
fmt.Println(context.ExecuteStrategy())
context = Context{10,5,new(OperationMulti)}
fmt.Println(context.ExecuteStrategy())
}



组合模式

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。



package main
import (
"fmt"
)
// interface
type Widget interface {
Draw()
}
type BaseWidget struct {
WidgetType string
text string
subWidgets []Widget
}
// 添加子组件
func (w *BaseWidget) Add(widget Widget) {
w.subWidgets = append(w.subWidgets, widget)
}
// 移除子组件
func (w *BaseWidget) Remove(idx int) {
// todo: 检查idx的有效性
// todo: 空间回收
w.subWidgets[idx] = nil
}
// draw
func (w *BaseWidget) Draw() {
fmt.Println("draw:", w.WidgetType, w.text)
for _, w := range w.subWidgets {
if w == nil {
continue
}
w.Draw()
}
}
func main() {
winform := BaseWidget{WidgetType: "WinForm", text: "Windown窗口"}
picture := BaseWidget{WidgetType: "Picture", text: "logo图片"}
buttonLogin := BaseWidget{WidgetType: "Button", text: "登录"}
buttonReg := BaseWidget{WidgetType: "Button", text: "注册"}
frame := BaseWidget{WidgetType: "Frame", text: "Frame1"}
label1 := BaseWidget{WidgetType: "Lable", text: "用户名"}
textbox := BaseWidget{WidgetType: "TextBox", text: "文本框"}
label2 := BaseWidget{WidgetType: "Lable", text: "用户名"}
passwdBox := BaseWidget{WidgetType: "PasswdBox", text: "密码框"}
checkBox := BaseWidget{WidgetType: "CheckBox", text: "复选框"}
text := BaseWidget{WidgetType: "Text", text: "记住用户名"}
linkLable := BaseWidget{WidgetType: "LinkLable", text: "忘记密码"}
winform.Add(&picture)
winform.Add(&buttonLogin)
winform.Add(&buttonReg)
winform.Add(&frame)
frame.Add(&label1)
frame.Add(&textbox)
frame.Add(&label2)
frame.Add(&passwdBox)
frame.Add(&checkBox)
frame.Add(&text)
frame.Add(&linkLable)
winform.Draw()
}

装饰模式

装饰模式使用对象组合的方式动态改变或增加对象行为。



package main
import "fmt"
type Shape interface {
Draw()
}
type Circle struct {
}
func (c *Circle)Draw() {
fmt.Println("draw a normal circle")
}
type RedShapeDecorator struct {
shape Shape
}
func (rd *RedShapeDecorator) Draw() {
rd.shape.Draw()
fmt.Println("set border color with red")
}
func main() {
circle := Circle{}
rc := RedShapeDecorator{&circle}
rc.Draw()
}



用户头像

2流程序员

关注

还未添加个人签名 2020.03.18 加入

还未添加个人简介

评论

发布
暂无评论
设计模式总结(golang版)