diff --git a/README.md b/README.md index 7703832..86e4ff9 100644 --- a/README.md +++ b/README.md @@ -4,47 +4,54 @@ [![Build Status](https://travis-ci.org/senghoo/golang-design-pattern.svg?branch=master)](https://travis-ci.org/senghoo/golang-design-pattern) - ## 姿势 -以实际代码示例展示设计模式 - ++ 所谓模式就是套路,如功夫,招有定式 ++ 这里就是以实际代码示例展示设计模式,通俗易懂 ++ 除了常见的23种普适的设计模式,Go也有一些属于自己的模式 ## 创建型模式 -* [简单工厂模式(Simple Factory)](./creation/00_simple_factory) -* [工厂方法模式(Factory Method)](./creation/04_factory_method) -* [抽象工厂模式(Abstract Factory)](./creation/05_abstract_factory) -* [创建者模式(Builder)](./creation/06_builder) -* [原型模式(Prototype)](./creation/07_prototype) -* [单例模式(Singleton)](./creation/03_singleton) ++ [简单工厂模式(Simple Factory)](./creation/00_simple_factory) ++ [工厂方法模式(Factory Method)](./creation/04_factory_method) ++ [抽象工厂模式(Abstract Factory)](./creation/05_abstract_factory) ++ [创建者模式(Builder)](./creation/06_builder) ++ [原型模式(Prototype)](./creation/07_prototype) ++ [单例模式(Singleton)](./creation/03_singleton) ## 结构型模式 -* [外观模式(Facade)](./01_facade) -* [适配器模式(Adapter)](./02_adapter) -* [代理模式(Proxy)](./09_proxy) -* [组合模式(Composite)](./13_composite) -* [享元模式(Flyweight)](./18_flyweight) -* [装饰模式(Decorator)](./20_decorator) -* [桥模式(Bridge)](./22_bridge) ++ [外观模式(Facade)](./01_facade) ++ [适配器模式(Adapter)](./02_adapter) ++ [代理模式(Proxy)](./09_proxy) ++ [组合模式(Composite)](./13_composite) ++ [享元模式(Flyweight)](./18_flyweight) ++ [装饰模式(Decorator)](./20_decorator) ++ [桥模式(Bridge)](./22_bridge) ## 行为型模式 -* [中介者模式(Mediator)](./08_mediator) -* [观察者模式(Observer)](./10_observer) -* [命令模式(Command)](./11_command) -* [迭代器模式(Iterator)](./12_iterator) -* [模板方法模式(Template Method)](./14_template_method) -* [策略模式(Strategy)](./15_strategy) -* [状态模式(State)](./16_state) -* [备忘录模式(Memento)](./17_memento) -* [解释器模式(Interpreter)](./19_interpreter) -* [职责链模式(Chain of Responsibility)](./21_chain_of_responsibility) -* [访问者模式(Visitor)](./23_visitor) ++ [中介者模式(Mediator)](./08_mediator) ++ [观察者模式(Observer)](./10_observer) ++ [命令模式(Command)](./11_command) ++ [迭代器模式(Iterator)](./12_iterator) ++ [模板方法模式(Template Method)](./14_template_method) ++ [策略模式(Strategy)](./15_strategy) ++ [状态模式(State)](./16_state) ++ [备忘录模式(Memento)](./17_memento) ++ [解释器模式(Interpreter)](./19_interpreter) ++ [职责链模式(Chain of Responsibility)](./21_chain_of_responsibility) ++ [访问者模式(Visitor)](./23_visitor) ## Design patters Articles [GO模式](https://github.com/tmrts/go-patterns) -[本设计模式的原始代码](https://github.com/senghoo/golang-design-pattern) +[参考代码1](https://github.com/tmrts/go-patterns) +[参考代码2](https://github.com/senghoo/golang-design-pattern) + +## 更多 + +需要重新温习下Go基础?看这里 + +[go-exercise](https://github.com/crazybber/go-exercise) diff --git a/creation/03_singleton/README.md b/creation/03_singleton/README.md index 8bf5720..6b6ac50 100644 --- a/creation/03_singleton/README.md +++ b/creation/03_singleton/README.md @@ -1,3 +1,9 @@ # 单例模式 -使用懒惰模式的单例模式,使用双重检查加锁保证线程安全 +大名鼎鼎的单例模式,永远返回相同内存位置的绝对的、同一个实例对象。 + +Go有两种常见的单例模式: + ++ 使用懒惰模式的单例模式,使用`once.Do()`的双重同步检查保证线程安全生成单实例 + ++ 使用初始化的`init(){}`能力保证只生成一个实例 diff --git a/creation/03_singleton/singleton.go b/creation/03_singleton/singleton.go index c99a380..bd644f1 100644 --- a/creation/03_singleton/singleton.go +++ b/creation/03_singleton/singleton.go @@ -2,17 +2,50 @@ package singleton import "sync" -//Singleton 是单例模式类 -type Singleton struct{} +//////////////////////////////// +//way 1 +//使用 sync的 once.Do(){}确保执行一次 +//////////////////////////////// -var singleton *Singleton +//Worker Singleton 是单例模式类 +type Worker struct{} + +//better to be pointer +var onlyTheWorker *Worker + +// init a control var once sync.Once -//GetInstance 用于获取单例模式对象 -func GetInstance() *Singleton { +//GetWorkerInstance 总是获取到同一个Worker对象(内存位置相同) +func GetWorkerInstance() *Worker { + + //be sure ,to do this,only once! once.Do(func() { - singleton = &Singleton{} + onlyTheWorker = &Worker{} }) - return singleton + return onlyTheWorker +} + +//Manager Singleton 是单例模式类 +type Manager struct{} + +//better to be pointer +var instance *Manager + +//better to be pointer +var onlyTheManager *Manager + +//////////////////////////////// +//way2 +//使用func init(){}函数来初始化保证,只初始化一次,更简单. +//////////////////////////////// + +func init() { + onlyTheManager = &Manager{} +} + +//GetManagerInstance 总是获取到同一个Manager对象(内存位置相同) +func GetManagerInstance() *Manager { + return onlyTheManager } diff --git a/creation/03_singleton/singleton_test.go b/creation/03_singleton/singleton_test.go index ad7928e..76dc2b8 100644 --- a/creation/03_singleton/singleton_test.go +++ b/creation/03_singleton/singleton_test.go @@ -5,30 +5,58 @@ import ( "testing" ) -const parCount = 100 +const workerCount = 500 -func TestSingleton(t *testing.T) { - ins1 := GetInstance() - ins2 := GetInstance() +func TestWorkerSingleton(t *testing.T) { + ins1 := GetWorkerInstance() + ins2 := GetWorkerInstance() if ins1 != ins2 { - t.Fatal("instance is not equal") + t.Fatal("worker(instance) is not exactly the same") } } -func TestParallelSingleton(t *testing.T) { +// 获取500次,Worker 是否总是同一个worker +func TestParallelWorkerSingleton(t *testing.T) { wg := sync.WaitGroup{} - wg.Add(parCount) - instances := [parCount]*Singleton{} - for i := 0; i < parCount; i++ { + wg.Add(workerCount) + instances := [workerCount]*Worker{} + for i := 0; i < workerCount; i++ { go func(index int) { - instances[index] = GetInstance() + instances[index] = GetWorkerInstance() wg.Done() }(i) } wg.Wait() - for i := 1; i < parCount; i++ { + for i := 1; i < workerCount; i++ { if instances[i] != instances[i-1] { - t.Fatal("instance is not equal") + t.Fatal("Worker instance is not equal") + } + } +} + +func TestManagerSingleton(t *testing.T) { + ins1 := GetManagerInstance() + ins2 := GetManagerInstance() + if ins1 != ins2 { + t.Fatal("Manager(instance) is not exactly the same") + } +} + +// 获取500次,Manager 是否总是同一个Manager +func TestParallelManagerSingleton(t *testing.T) { + wg := sync.WaitGroup{} + wg.Add(workerCount) + instances := [workerCount]*Manager{} + for i := 0; i < workerCount; i++ { + go func(index int) { + instances[index] = GetManagerInstance() + wg.Done() + }(i) + } + wg.Wait() + for i := 1; i < workerCount; i++ { + if instances[i] != instances[i-1] { + t.Fatal("Manager instance is not exactly equal") } } } diff --git a/creation/04_factory_method/README.md b/creation/04_factory_method/README.md index 9c4c834..e15e4cb 100644 --- a/creation/04_factory_method/README.md +++ b/creation/04_factory_method/README.md @@ -2,4 +2,6 @@ 工厂方法模式使用子类的方式延迟生成对象到子类中实现。 -Go中不存在继承 所以使用匿名组合来实现 +不同的工厂对象,返回一个实现了某接口的类型对象,不同的工厂返回不同实现了相同接口的不同对象,在只能使用特定的类简单工厂的基础上,使用的代码和类生成的灵活性大大增加。 + +Go中的继承关系是使用匿名组合来实现的。 diff --git a/creation/04_factory_method/factorymethod.go b/creation/04_factory_method/factorymethod.go index c8f57d6..0c11014 100644 --- a/creation/04_factory_method/factorymethod.go +++ b/creation/04_factory_method/factorymethod.go @@ -1,66 +1,84 @@ package factorymethod -//Operator 是被封装的实际类接口 -type Operator interface { - SetA(int) - SetB(int) - Result() int +import ( + "fmt" + "strconv" +) + +//Assistant 是robot能做的事情 +type Assistant interface { + Clean(int) + Speak(string) + Work() string } -//OperatorFactory 是工厂接口 -type OperatorFactory interface { - Create() Operator +//IRobotFactory must be implemented by Factory +//different Factory create different robot +type IRobotFactory interface { + Build() Assistant } -//OperatorBase 是Operator 接口实现的基类,封装公用方法 -type OperatorBase struct { - a, b int +//BasicRobotModel 是基本的机器人模型 +type BasicRobotModel struct { + words string + workTime int } -//SetA 设置 A -func (o *OperatorBase) SetA(a int) { - o.a = a +//Clean 打扫 +func (b *BasicRobotModel) Clean(a int) { + b.workTime = a + fmt.Printf("i can clean :%d hours\n", a) } -//SetB 设置 B -func (o *OperatorBase) SetB(b int) { - o.b = b +//Speak 说话 +func (b *BasicRobotModel) Speak(w string) { + b.words = w + fmt.Printf("my name is: %s\n", w) } -//PlusOperatorFactory 是 PlusOperator 的工厂类 -type PlusOperatorFactory struct{} +//Work main work +func (b *BasicRobotModel) Work() string { + return fmt.Sprint("my main work is do somthing") +} -func (PlusOperatorFactory) Create() Operator { - return &PlusOperator{ - OperatorBase: &OperatorBase{}, +//FightingRobotFactory 生产各类军工机器人 +type FightingRobotFactory struct{} + +//Build a robot from FightingRobotFactory +func (FightingRobotFactory) Build() Assistant { + return &FightingRobot{ + BasicRobotModel: &BasicRobotModel{}, } } -//PlusOperator Operator 的实际加法实现 -type PlusOperator struct { - *OperatorBase +//FightingRobot 实际的战斗机器人 +type FightingRobot struct { + *BasicRobotModel } -//Result 获取结果 -func (o PlusOperator) Result() int { - return o.a + o.b +//Work for FightingRobot to do some fighting +func (f FightingRobot) Work() string { + fmt.Printf("%s\n", "i can fighting") + return "i can fighting" + strconv.Itoa(f.workTime) } -//MinusOperatorFactory 是 MinusOperator 的工厂类 -type MinusOperatorFactory struct{} +//HomeRobotFactory 生产各类家用机器人 +type HomeRobotFactory struct{} -func (MinusOperatorFactory) Create() Operator { - return &MinusOperator{ - OperatorBase: &OperatorBase{}, +//Build a robot from HomeRobotFactory +func (HomeRobotFactory) Build() Assistant { + return &HomeRobot{ + BasicRobotModel: &BasicRobotModel{}, } } -//MinusOperator Operator 的实际减法实现 -type MinusOperator struct { - *OperatorBase +//HomeRobot 实际的家用机器人 +type HomeRobot struct { + *BasicRobotModel } -//Result 获取结果 -func (o MinusOperator) Result() int { - return o.a - o.b +//Work robot do some work +func (h HomeRobot) Work() string { + fmt.Printf("%s\n", "i can do homework") + return "i can do homework" + strconv.Itoa(h.workTime) } diff --git a/creation/04_factory_method/factorymethod_test.go b/creation/04_factory_method/factorymethod_test.go index 70f7aa2..d5e3afc 100644 --- a/creation/04_factory_method/factorymethod_test.go +++ b/creation/04_factory_method/factorymethod_test.go @@ -2,25 +2,26 @@ package factorymethod import "testing" -func compute(factory OperatorFactory, a, b int) int { - op := factory.Create() - op.SetA(a) - op.SetB(b) - return op.Result() +func doWork(factory IRobotFactory, cleanhour int) string { + robot := factory.Build() + robot.Clean(cleanhour) + + robot.Speak("robot name") + + return robot.Work() + } -func TestOperator(t *testing.T) { - var ( - factory OperatorFactory - ) +func TestRobotFactory(t *testing.T) { + var factory IRobotFactory - factory = PlusOperatorFactory{} - if compute(factory, 1, 2) != 3 { + factory = FightingRobotFactory{} + if doWork(factory, 2) != "i can fighting2" { t.Fatal("error with factory method pattern") } - factory = MinusOperatorFactory{} - if compute(factory, 4, 2) != 2 { + factory = HomeRobotFactory{} + if doWork(factory, 1) != "i can do homework1" { t.Fatal("error with factory method pattern") } } diff --git a/creation/05_abstract_factory/README.md b/creation/05_abstract_factory/README.md index 8783b79..9b39e34 100644 --- a/creation/05_abstract_factory/README.md +++ b/creation/05_abstract_factory/README.md @@ -1,8 +1,12 @@ # 抽象工厂模式 -抽象工厂模式用于生成产品族的工厂,所生成的对象是有关联的。 +抽象工厂模式用于生成具有多产品种类生产能力的的工厂,所生成的对象往往是有关联的。 -如果抽象工厂退化成生成的对象无关联则成为工厂函数模式。 +如果抽象工厂退化成生成的对象无关联的或者单一的产品种类则成为工厂函数模式。 + +参考:[对比](https://blog.csdn.net/wyxhd2008/article/details/5597975) + +![对比图片](../../images/abstract-factorys-method.png) 比如本例子中使用RDB和XML存储订单信息,抽象工厂分别能生成相关的主订单信息和订单详情信息。 如果业务逻辑中需要替换使用的时候只需要改动工厂函数相关的类就能替换使用不同的存储方式了。 diff --git a/creation/07_prototype/README.md b/creation/07_prototype/README.md index 62e3ef0..c2bfbe2 100644 --- a/creation/07_prototype/README.md +++ b/creation/07_prototype/README.md @@ -1,5 +1,5 @@ # 原型模式 -原型模式使对象能复制自身,并且暴露到接口中,使客户端面向接口编程时,不知道接口实际对象的情况下生成新的对象。 +原型模式用于快速复制具有相同属性的自身对象. -原型模式配合原型管理器使用,使得客户端在不知道具体类的情况下,通过接口管理器得到新的实例,并且包含部分预设定配置。 +一般是通过实现实现: `Clone()`接口来实现,注意**必须返回新的内存实例**. diff --git a/creation/07_prototype/prototype.go b/creation/07_prototype/prototype.go index 2da0ec6..3ec7ce5 100644 --- a/creation/07_prototype/prototype.go +++ b/creation/07_prototype/prototype.go @@ -1,24 +1,26 @@ package prototype -//Cloneable 是原型对象需要实现的接口 +//Cloneable is the key interface type Cloneable interface { Clone() Cloneable } -type PrototypeManager struct { - prototypes map[string]Cloneable +//cloneLab 克隆实验室,可以克隆很多动物 +type cloneLab struct { + animals map[string]Cloneable } -func NewPrototypeManager() *PrototypeManager { - return &PrototypeManager{ - prototypes: make(map[string]Cloneable), +//new 返回一个 +func newCloneLab() *cloneLab { + return &cloneLab{ + animals: make(map[string]Cloneable), } } -func (p *PrototypeManager) Get(name string) Cloneable { - return p.prototypes[name] +func (c *cloneLab) Get(name string) Cloneable { + return c.animals[name] } -func (p *PrototypeManager) Set(name string, prototype Cloneable) { - p.prototypes[name] = prototype +func (c *cloneLab) Set(name string, newObject Cloneable) { + c.animals[name] = newObject } diff --git a/creation/07_prototype/prototype_test.go b/creation/07_prototype/prototype_test.go index cdde8f5..4b83a5d 100644 --- a/creation/07_prototype/prototype_test.go +++ b/creation/07_prototype/prototype_test.go @@ -2,51 +2,57 @@ package prototype import "testing" -var manager *PrototypeManager +//cloneLab 克隆实验室 +var lab *cloneLab -type Type1 struct { - name string +type sheep struct { + name string + weight int } -func (t *Type1) Clone() Cloneable { - tc := *t +func (s *sheep) Clone() Cloneable { + tc := *s return &tc } -type Type2 struct { - name string +type cow struct { + name string + gender bool } -func (t *Type2) Clone() Cloneable { - tc := *t - return &tc +func (c *cow) Clone() Cloneable { + newCow := &cow{ + gender: c.gender, + name: c.name, + } + return newCow } func TestClone(t *testing.T) { - t1 := manager.Get("t1") - t2 := t1.Clone() + sheep1 := &sheep{ + name: "sheep", + weight: 10, + } - if t1 == t2 { + sheep2 := sheep1.Clone() + + if sheep1 == sheep2 { t.Fatal("error! get clone not working") } } -func TestCloneFromManager(t *testing.T) { - c := manager.Get("t1").Clone() +func TestCloneFromLab(t *testing.T) { - t1 := c.(*Type1) - if t1.name != "type1" { + lab := newCloneLab() + + lab.Set("cow", &cow{name: "i am cow", gender: true}) + + c := lab.Get("cow").Clone() + + cw := c.(*cow) + if cw.name != "i am cow" { t.Fatal("error") } } - -func init() { - manager = NewPrototypeManager() - - t1 := &Type1{ - name: "type1", - } - manager.Set("t1", t1) -} diff --git a/creation/24_object_pool/README.md b/creation/24_object_pool/README.md new file mode 100644 index 0000000..ecffe10 --- /dev/null +++ b/creation/24_object_pool/README.md @@ -0,0 +1,3 @@ +# Object Pool + +对象池创建模式,根据业务和执行的需要,用于一次性准备大量的预置对象 diff --git a/creation/24_object_pool/obejct_poo.go b/creation/24_object_pool/obejct_poo.go new file mode 100644 index 0000000..15c9dc8 --- /dev/null +++ b/creation/24_object_pool/obejct_poo.go @@ -0,0 +1,23 @@ +package objectpool + +type doctor struct { + name string + kind int //科室 +} + +type pool chan *doctor + +func newPool(total int) pool { + p := make(pool, total) + + for i := 0; i < total; i++ { + p <- new(doctor) + } + + return p +} + +//surgery +func (d doctor) surgery() { + +} diff --git a/creation/24_object_pool/object_pool_test.go b/creation/24_object_pool/object_pool_test.go new file mode 100644 index 0000000..2e7baa6 --- /dev/null +++ b/creation/24_object_pool/object_pool_test.go @@ -0,0 +1,20 @@ +package objectpool + +import ( + "testing" +) + +func TestObjectPool(t *testing.T) { + + p := newPool(2) + + select { + case obj := <-p: + obj.surgery( /*...*/ ) + + p <- obj + default: + // No more objects left — retry later or fail + return + } +} diff --git a/idiom/functional_options_test.go b/idiom/functional_options_test.go new file mode 100644 index 0000000..5141b55 --- /dev/null +++ b/idiom/functional_options_test.go @@ -0,0 +1,83 @@ +package idiom + +import ( + "os" + "testing" +) + +func TestFileFunctionOptions(t *testing.T) { + err := New("empty.txt") + if err != nil { + panic(err) + } + os.Remove("empty.txt") + + err = New("file.txt", UID(1000), Contents("input some data")) + if err != nil { + panic(err) + } + os.Remove("file.txt") +} + +///Options is key struct +type Options struct { + UID int + GID int + Flags int + Contents string + Permissions os.FileMode +} + +//Option func is key func +type Option func(*Options) + +func UID(userID int) Option { + return func(args *Options) { + args.UID = userID + } +} + +func GID(groupID int) Option { + return func(args *Options) { + args.GID = groupID + } +} + +func Contents(c string) Option { + return func(args *Options) { + args.Contents = c + } +} + +func Permissions(perms os.FileMode) Option { + return func(args *Options) { + args.Permissions = perms + } +} + +func New(filepath string, setters ...Option) error { + // Default Options + args := &Options{ + UID: os.Getuid(), + GID: os.Getgid(), + Contents: "", + Permissions: 0666, + Flags: os.O_CREATE | os.O_EXCL | os.O_WRONLY, + } + + for _, setter := range setters { + setter(args) + } + + f, err := os.OpenFile(filepath, args.Flags, args.Permissions) + if err != nil { + return err + } + defer f.Close() + + if _, err := f.WriteString(args.Contents); err != nil { + return err + } + + return err +} diff --git a/images/abstract-factorys-method.png b/images/abstract-factorys-method.png new file mode 100644 index 0000000..21c03ed Binary files /dev/null and b/images/abstract-factorys-method.png differ