├── .github └── workflows │ └── go.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── behavior ├── 01_mediator │ ├── README.md │ ├── mediator.go │ └── mediator_test.go ├── 02_option │ ├── README.md │ ├── functional_options_test.go │ └── options.go ├── 03_observer │ ├── README.md │ ├── obserser.go │ └── obserser_test.go ├── 04_iterator │ ├── README.md │ ├── book_iterator │ │ ├── book_iterator.go │ │ └── book_iterator_test.go │ ├── iterator.go │ ├── iterator_test.go │ └── person_iterator │ │ ├── iterator.go │ │ └── iterator_test.go ├── 05_template_method │ ├── README.md │ ├── template_method.go │ └── template_method_test.go ├── 06_chain_of_responsibility │ ├── README.md │ ├── responsibility_chain.go │ ├── responsibility_chain_game_test.go │ └── responsibility_chain_test.go ├── 07_visitor │ ├── README.md │ ├── visitor.go │ ├── visitor_game.go │ └── visitor_test.go ├── 08_interpreter │ ├── README.md │ ├── interpreter.go │ ├── interpreter_caculator.go │ └── interpreter_test.go ├── 09_memento │ ├── README.md │ ├── memento.go │ └── memento_test.go ├── 10_state │ ├── README.md │ ├── state.go │ ├── state_clock.go │ └── state_test.go ├── 11_command │ ├── README.md │ ├── command.go │ ├── command_draw.go │ └── command_test.go ├── 12_strategy │ ├── README.md │ ├── strategy.go │ └── strategy_test.go └── README.md ├── creation ├── 01_new │ ├── REDME.md │ └── new_test.go ├── 02_simple_factory │ ├── README.md │ ├── simple.go │ └── simple_test.go ├── 03_builder │ ├── README.md │ ├── builder.go │ └── builder_test.go ├── 04_object_pool │ ├── README.md │ ├── obejct_poo.go │ └── object_pool_test.go ├── 05_factory_method │ ├── README.md │ ├── factorymethod.go │ └── factorymethod_test.go ├── 06_singleton │ ├── README.md │ ├── singleton.go │ └── singleton_test.go ├── 07_prototype │ ├── README.md │ ├── prototype.go │ └── prototype_test.go ├── 08_abstract_factory │ ├── README.md │ ├── abstractfactory_db.go │ ├── abstractfactory_robot.go │ └── abstractfactory_test.go └── README.md ├── cspell.json ├── go.mod ├── go.sum ├── gomore ├── 01_messages │ ├── README.md │ ├── message.go │ ├── message_queue.go │ ├── message_test.go │ ├── message_weather.go │ └── message_weather_test.go ├── 02_profiles │ ├── README.md │ └── time_profile_test.go ├── 03_context │ ├── README.md │ └── context_test.go ├── 04_fan_in │ ├── README.md │ ├── fan_in.go │ ├── fan_in_out_test.go │ └── fan_in_test.go ├── 05_fan_out │ ├── README.md │ ├── fan_out.go │ ├── fan_out_complex.go │ ├── fan_out_complex_test.go │ └── fan_out_test.go ├── 08_semaphore │ ├── README.md │ ├── semaphore.go │ └── semaphore │ │ ├── semaphore.go │ │ └── semaphore_test.go ├── 09_parallelism │ ├── README.md │ ├── parallelism.go │ └── parallelism_test.go ├── 10_generators │ └── README.md ├── 11_n_barrier │ └── README.md ├── 12_bounded_parallelism │ └── README.md ├── 13_batcher │ ├── README.md │ ├── batcher.go │ └── batcher_test.go └── README.md ├── images ├── abstract-factorys-method.png ├── breaker-state-machine-flow.png ├── breaker-state-machine.png ├── composite.png ├── pub-sub-pattern-0.png └── pub-sub-pattern-1.png ├── index.go ├── main.go ├── resiliency ├── 01_circuit_breaker │ ├── README.md │ ├── breaker │ │ ├── breaker.go │ │ └── breaker_test.go │ ├── breaker_options.go │ ├── circuit_breaker_adv.go │ ├── circuit_breaker_test.go │ ├── circuit_counter.go │ ├── circuit_func_closure_basic.go │ └── gobreaker │ │ ├── README.md │ │ ├── gobreaker.go │ │ ├── gobreaker_example_test.go │ │ └── gobreaker_test.go ├── 02_rate_limiting │ ├── README.md │ ├── rate_limiting.go │ └── rate_limiting_test.go ├── 03_deadline │ ├── README.md │ ├── deadline.go │ └── deadline_test.go └── 04_retrier │ ├── backoffs.go │ ├── backoffs_test.go │ ├── classifier.go │ ├── classifier_test.go │ ├── retrier.go │ └── retrier_test.go └── structure ├── 01_facade ├── README.md ├── facade.go └── facade_test.go ├── 02_adapter ├── README.md ├── adapter.go └── adapter_test.go ├── 03_bridge ├── README.md ├── bridge.go └── bridge_test.go ├── 04_flyweight ├── README.md ├── flyweight.go └── flyweight_test.go ├── 05_composite ├── README.md ├── composite.go └── composite_test.go ├── 06_decorator ├── README.md ├── decorator.go ├── decorator_log_test.go └── decorator_test.go ├── 07_proxy ├── README.md ├── proxy.go └── proxy_test.go └── README.md /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: '1.20' 23 | 24 | - name: Build 25 | run: go build -v ./... 26 | 27 | - name: Test 28 | run: go test -v ./... 29 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "window.autoDetectColorScheme": false, 3 | "editor.tabSize": 2, 4 | "editor.semanticTokenColorCustomizations": { 5 | "enabled": true, // enable semantic highlighting for all themes 6 | "rules": { 7 | "typeParameter": "#352cea", 8 | "type": { 9 | "foreground": "#00aa00" 10 | }, 11 | "parameter": "#4c70e7", 12 | } 13 | }, 14 | "extensions.showRecommendationsOnlyOnDemand": true, 15 | "files.trimTrailingWhitespace": true, 16 | "files.insertFinalNewline": true, 17 | "extensions.ignoreRecommendations": true, 18 | "workbench.colorCustomizations": { 19 | "activityBar.background": "#759570", 20 | "activityBar.activeBorder": "#cfd3ef", 21 | "activityBar.foreground": "#15202b", 22 | "activityBar.hoverBackground": "#352cea", 23 | "activityBar.inactiveForeground": "#15202b99", 24 | "activityBarBadge.background": "#cfd3ef", 25 | "activityBarBadge.foreground": "#15202b", 26 | "statusBarItem.hoverBackground": "#759570", 27 | "statusBar.foreground": "#e7e7e7", 28 | "activityBar.activeBackground": "#759570", 29 | "statusBar.background": "#5e7959", 30 | "statusBar.border": "#5e7959", 31 | "titleBar.activeBackground": "#5e7959", 32 | "titleBar.inactiveBackground": "#5e795999", 33 | "titleBar.activeForeground": "#e7e7e7", 34 | "titleBar.inactiveForeground": "#e7e7e799", 35 | "titleBar.border": "#5e7959" 36 | }, 37 | "go.useLanguageServer": true, 38 | "go.lintOnSave": "file", 39 | "go.vetOnSave": "package", 40 | "go.useCodeSnippetsOnFunctionSuggest": true, 41 | "go.testFlags": [ 42 | "-v" 43 | ], 44 | "go.testTimeout": "10s", 45 | "go.formatTool": "goimports", 46 | "cSpell.allowCompoundWords": true, 47 | "editor.codeActionsOnSave": { 48 | "source.organizeImports": true 49 | }, 50 | "fileheader.Author": "Edward", 51 | "fileheader.tpl": "/*\r\n * @Description: https://github.com/crazybber\r\n * @Author: {author}\r\n * @Date: {createTime}\r\n * @Last Modified by: {lastModifiedBy}\r\n * @Last Modified time: {updateTime}\r\n */\r\n\r\n", 52 | "fileheader.LastModifiedBy": "Edward", 53 | "peacock.color": "#5e7959" 54 | } 55 | //https://vscode.readthedocs.io/en/latest/getstarted/settings/ 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Edward https://github.com/crazybber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang工程模式示例集合(Go Patterns Examples) 2 | 3 | **包括了[go-patterns](https://github.com/tmrts/go-patterns) 和[golang-design-pattern](https://github.com/senghoo/golang-design-pattern)中的全部模式** 4 | 5 | 目前包括**42种Go中常用的、面向工程化和最佳实践的模式/套路**,自然也包含常见的23种设计模式,重点是这里全部是例子、通俗易懂,甚至每个模式下的例子,改一下名字,稍微再增加几行代码就可以直接用在项目和工程中了。 6 | 7 | 每一种设计模式都有其特定的应用场景和要解决的问题,了解模式的关键点就在于弄清这些目标场景和问题,千万不要纠结于:为什么这个模式叫这个名字,这个模式为啥要这样用? **要知道,最初,这些模式不是你总结的,也不是我的总结的,命名不由你我,如果是你我的写的,当然可以按照自己的喜欢的感觉给这些套路取名字,让其他人去费劲想** 8 | 9 | ## 了解姿势 Ways 10 | 11 | + 所谓模式就是套路,如功夫,招有定式 12 | + 学习模式,就是学习一些经典套路,更弄清楚套路要解决的目标场景,这很重要,才能举一反三更好创新 13 | + 这里就是以实际代码示例出发,展示设计模式,通俗易懂 14 | + 除了常见的23种普适的设计模式,Go也有一些属于自己的模式 15 | 16 | ## 走起 Go 17 | 18 | 下载: 19 | 20 | ```bash 21 | go https://github.com/crazybber/go-pattern-examples 22 | ``` 23 | 24 | 跑一遍测试: 25 | 26 | ```bash 27 | cd go-pattern-examples 28 | go test ./... 29 | ``` 30 | 31 | ## 行为型模式 Behavior Patterns 32 | 33 | + [x] [备忘录模式(Memento)](./behavior/09_memento) 34 | + [x] [中介者模式(Mediator)](./behavior/01_mediator) 35 | + [x] [闭包选项模式(Function Option)](./behavior/02_option) 36 | + [x] [观察者模式(Observer)](./behavior/03_observer) 37 | + [x] [命令模式(Command)](./behavior/11_command) 38 | + [x] [迭代器模式(Iterator)](./behavior/04_iterator) 39 | + [x] [模板方法模式(Template Method)](./behavior/05_template_method) 40 | + [x] [策略模式(Strategy)](./behavior/12_strategy) 41 | + [x] [状态模式(State)](./behavior/behavior16_state) 42 | + [x] [访问者模式(Visitor)](./behavior/07_visitor) 43 | + [x] [解释器模式(Interpreter)](./behavior/08_interpreter) 44 | + [x] [职责链模式(Chain of Responsibility)](./behavior/06_chain_of_responsibility) 45 | 46 | ## 创建型模式 Creation Patterns 47 | 48 | + [x] [New模式(New)](./creation/01_new) 49 | + [x] [简单工厂模式(Simple Factory)](./creation/02_simple_factory) 50 | + [x] [创建者模式(Builder)](./creation/03_builder) 51 | + [x] [单例模式(Singleton)](./creation/06_singleton) 52 | + [x] [对象池模式(Object Pool)](./creation/04_object_pool) 53 | + [x] [工厂方法模式(Factory Method)](./creation/05_factory_method) 54 | + [x] [抽象工厂模式(Abstract Factory)](./creation/08_abstract_factory) 55 | + [x] [原型模式(Prototype)](./creation/07_prototype) 56 | 57 | ## 结构型模式 Structure Patterns 58 | 59 | + [x] [外观模式(Facade)](./structure/01_facade) 60 | + [x] [适配器模式(Adapter)](./structure/02_adapter) 61 | + [x] [桥模式(Bridge)](./structure/03_bridge) 62 | + [x] [复合模式(Composite)](./structure/05_composite) 63 | + [x] [享元模式(Flyweight)](./structure/04_flyweight) 64 | + [x] [装饰器模式(Decorator)](./structure/06_decorator) 65 | + [x] [代理模式(Proxy)](./structure/07_proxy) 66 | 67 | ## 弹性模式 Resiliency Patterns 68 | 69 | + [x] [熔断模式(circuit breaker)](./resiliency/01_circuit_breaker) 70 | + [x] [限流模式(rate limiting)](./resiliency/02_rate_limiting) 71 | + [ ] [WIP][重试模式(retrier)](./resiliency/04_retrier) 72 | + [x] [最后期限模式(deadline)](./resiliency/03_deadline) 73 | 74 | ## 更多模式(同步/并发/并行) Go More Patterns(Concurrency/Parallelism/Sync) 75 | 76 | + [x] [发布订阅模式(Pub-Sub)](./gomore/01_messages) 77 | + [x] [时差模式(Time Profile)](./gomore/02_profiles) 78 | + [x] [上下文模式(Context)](./gomore/03_context) 79 | + [x] [扇入模式(Fan-In)](./gomore/04_fan_in) 80 | + [x] [扇出模式(Fan-Out)](./gomore/05_fan_out) 81 | + [ ] [WIP][信号量模式(Semaphore)](./gomore/08_semaphore) 82 | + [ ] [WIP][并行模式(Parallelism)](./gomore/09_parallelism) 83 | + [ ] [WIP][生成器模式(Generators)](./gomore/10_generators) 84 | + [ ] [WIP][屏障模式(N-Barrier)](./gomore/11_n_barrier) 85 | + [ ] [WIP][有限并行模式(Bounded Parallelism)](./gomore/12_bounded_parallelism) 86 | + [ ] [WIP][批处理模式(batcher)](./gomore/13_batcher) 87 | 88 | 89 | 90 | ## 参考资料(Design patters Articles) 91 | 92 | | Patterns | Instructions | Status | 93 | |:-------:|:----------- |:------:| 94 | | [go-patterns](https://github.com/crazybber/go-patterns)|搜集整理到各种模式文章和代码|p| 95 | | [菜鸟设计模式](https://www.runoob.com/design-pattern/design-pattern-tutorial.html)|以Java为主的设计模式介绍|p| 96 | | [design_pattern](http://c.biancheng.net/design_pattern)|编程之家的设计模式|p| 97 | | [golang-design-pattern](https://github.com/senghoo/golang-design-pattern)|go语言的设计模式理论|p| 98 | | [go-resiliency](https://github.com/eapache/go-resiliency)|go的一些弹性模式 |m| 99 | | [Behavioral](https://github.com/AlexanderGrom/go-patterns/tree/master/Behavioral)|设计模式中的行为模式(俄语版)|m| 100 | | [go-patterns](https://github.com/sevenelevenlee/go-patterns)|一些设计模式 |p| 101 | | [go_design_pattern](https://github.com/monochromegane/go_design_pattern)|日语版设计模式|p| 102 | | [microsoft-patterns](https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn600223(v=pandp.10))|微软设计模式系列文章(24)|p/w| 103 | 104 | ## 更多 More 105 | 106 | 需要重新温习下Go基础?看这里: 107 | 108 | [go-fucking-exercises](https://github.com/crazybber/go-fucking-exercise) 109 | -------------------------------------------------------------------------------- /behavior/01_mediator/README.md: -------------------------------------------------------------------------------- 1 | # 中介者模式 2 | 3 | 也叫中间人模式,设计模式的核心理念就是封装变化点,中介模式顾名思义,就是在有关系的两方或者多方之间引入中间人,使得中介两方(多方)都跟中间人交谈,可以独立自由变化,达到解耦的目的. 4 | 5 | 现实生活的中的租房中介/职业介绍都是中介者模式的实际典型代表. 6 | 7 | 庞大的中介平台Online就是一个中介者模式的系统的实现. 8 | 9 | //中介模式的缺点是:中介本身会实现复杂的逻辑以维护多边关系. 10 | -------------------------------------------------------------------------------- /behavior/01_mediator/mediator.go: -------------------------------------------------------------------------------- 1 | package mediator 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | //在这个模式里面,我们演示一个场景用例 8 | //房东通过中介向房客收房租 9 | //房客通过中介向房东要求换新家具 10 | //中介每次服务都收100块服务费 11 | 12 | //Person 定义一个本身的人 13 | type Person struct { 14 | Name string 15 | WalletAssets int //每个人都有钱包 16 | } 17 | 18 | //Tenant 租客 继承Person 19 | type Tenant struct { 20 | Person 21 | furniture string 22 | } 23 | 24 | //ITenant 租户能做的事情 25 | type ITenant interface { 26 | AskRepair(mediator IMediator) 27 | } 28 | 29 | //Landlord 房东,也继承Person,要收房租 30 | type Landlord struct { 31 | Person 32 | RentAccout int //房东的租金账户 33 | } 34 | 35 | //ILandlord 房东能做的事情 36 | type ILandlord interface { 37 | CollectRent(mediator IMediator) 38 | } 39 | 40 | //Mediator 中介也继承Person,比如某居客,某家,某壳,即代表租客跟房东谈条件,又代表房东对付租客 41 | //Mediator 中介一定会持有两方的必要信息 42 | //Mediator 这里简化一下,假设中介只为一个房东和一个租客服务 43 | type Mediator struct { 44 | Person 45 | tenant ITenant //中介持有房客的信息 46 | landlord ILandlord //中介持有房东的信息 47 | feelandlord int 48 | feetenant int 49 | } 50 | 51 | //IMediator 中介能做的事情,中介能代表任何一方, 52 | //所以理论上他需要实现所代表对象的所有能力 53 | //实际设计中,中介对象本身也会成为问题的所在,可能会比较臃肿 54 | type IMediator interface { 55 | RegisterRoom(landlord ILandlord) 56 | Serve(client interface{}) //服务日常活动 57 | RentOutRoom(tenant ITenant) 58 | } 59 | 60 | //AskRepair 要求房东修家具,只需要向中介提要求,中介会提代替房客提要求 61 | func (t *Tenant) AskRepair(mediator IMediator) { 62 | fmt.Println("Tenant: i need landlord fix furniture:") 63 | mediator.Serve(t) 64 | } 65 | 66 | //CollectRent 房东收租金,只需要向中介收,中介会提代替房东收租金 67 | func (l *Landlord) CollectRent(mediator IMediator) { 68 | fmt.Println("Landlord: collect money") 69 | fmt.Printf("Landlord: RentAccout %d, WalletAssets %d\n", l.RentAccout, l.WalletAssets) 70 | mediator.Serve(l) 71 | 72 | } 73 | 74 | //RegisterRoom 可以在中介这里发布房源 75 | func (m *Mediator) RegisterRoom(landlord ILandlord) { 76 | m.landlord = landlord 77 | } 78 | 79 | //RentOutRoom 可以从中介租房子 80 | func (m *Mediator) RentOutRoom(tenant ITenant) { 81 | m.tenant = tenant 82 | } 83 | 84 | //Serve 中介要替两边或者多边办事,所以它很累,所有事情都要做 85 | //这是关键过程 86 | //简单起见,1代表租客,2代表房东 87 | func (m *Mediator) Serve(client interface{}) { 88 | 89 | switch client.(type) { 90 | case ITenant: 91 | fmt.Println("i am serving tenant") 92 | case ILandlord: 93 | fmt.Println("i am serving landlord") 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /behavior/01_mediator/mediator_test.go: -------------------------------------------------------------------------------- 1 | package mediator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestMediator(t *testing.T) { 9 | 10 | med := &Mediator{} 11 | landlord := &Landlord{} 12 | //登记房源信息 13 | med.RegisterRoom(landlord) 14 | tenant := &Tenant{} 15 | //向中介租房 16 | med.RentOutRoom(tenant) 17 | 18 | //房东收租 19 | landlord.CollectRent(med) 20 | //租客要求修理 21 | tenant.AskRepair(med) 22 | 23 | } 24 | 25 | func TestClassCompose(t *testing.T) { 26 | 27 | med := &Mediator{Person: Person{Name: "mediator", WalletAssets: 1001}} 28 | 29 | landlord := &Landlord{Person: Person{Name: "landlord", WalletAssets: 2000}, RentAccout: 500} 30 | 31 | tenant := &Tenant{Person: Person{Name: "tenant", WalletAssets: 500}, furniture: "desk"} 32 | 33 | fmt.Println("mediator", med) 34 | fmt.Println("landlord", landlord.Name) 35 | fmt.Println("tenant", tenant.furniture) 36 | } 37 | -------------------------------------------------------------------------------- /behavior/02_option/README.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 3 | Option模式又称傻瓜模式,常常用于参数传递,一般常常与New模式配合使用用于初始化的一个对象的外部参数. 4 | -------------------------------------------------------------------------------- /behavior/02_option/functional_options_test.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestFileFunctionOptions(t *testing.T) { 8 | 9 | Introduce("tom", Gender(true), Company("land company")) 10 | 11 | Introduce("lily", Company("sky commnay"), UID(123)) 12 | 13 | Introduce("admin", Company("risky commnay"), UID(883)) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /behavior/02_option/options.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | //Options is key struct,关键数据结构,聚合所有外部可传入的参数 8 | type Options struct { 9 | UID int 10 | GID int 11 | Flags int 12 | Company string 13 | Gender bool //is male 14 | } 15 | 16 | //Option func is key func 17 | type Option func(*Options) 18 | 19 | //UID set User ID 20 | func UID(userID int) Option { 21 | return func(args *Options) { 22 | args.UID = userID 23 | } 24 | } 25 | 26 | //GID set User Group ID 27 | func GID(groupID int) Option { 28 | return func(args *Options) { 29 | args.GID = groupID 30 | } 31 | } 32 | 33 | //Company set Company Name 34 | func Company(cname string) Option { 35 | return func(args *Options) { 36 | args.Company = cname 37 | } 38 | } 39 | 40 | //Gender for male or female 41 | func Gender(gender bool) Option { 42 | return func(args *Options) { 43 | args.Gender = gender 44 | } 45 | } 46 | 47 | //Introduce someone 48 | func Introduce(name string, setters ...Option /*传入闭包设置函数*/) { 49 | // Default Options 50 | args := &Options{ 51 | UID: 0, 52 | GID: 0, 53 | Company: "", 54 | Gender: true, 55 | } 56 | //模式的重点体现在这里,通过外部传入的闭包函数设置内在变量 57 | for _, setter := range setters { 58 | setter(args) 59 | } 60 | gender := "famale" 61 | if args.Gender { 62 | gender = "male" 63 | } 64 | fmt.Println("----------------------") 65 | fmt.Println("im am: ", name, "\nfrom: ", args.Company, "\ngender: ", gender, "\nUID: ", args.UID) 66 | } 67 | -------------------------------------------------------------------------------- /behavior/03_observer/README.md: -------------------------------------------------------------------------------- 1 | # 观察者模式 2 | 3 | 观察者模,又称事件订阅模式,是一种单向的消息告知模式,有事件发生了,被观察的一方告知观察的以方,一般并不要求任何回复和响应,这种模式在C#中大量存在,并且广泛应用,在响应式模式中得到进一步扩展。 4 | 5 | 6 | 一个对象的改变会触发其它观察者的相关动作,当前模式实现的关键点在于被观察对象要持有观察者对象的订阅关系,否则无法通知到观察者,也就是两者一定要存在一定的关联关系,可以接口的引用,可以类型的引用. 7 | 8 | 现实生活中,网站的RSS订阅,各种优惠打折的通知,云服务降价促销场景都是这个模式。 9 | 10 | 通知方通过邮箱/手机等方式跟用户产生关联,当事件发生时候直接通知用户,并不要求用户响应。 11 | -------------------------------------------------------------------------------- /behavior/03_observer/obserser.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | //使用RSS邮件订阅的例子 9 | //某科技论坛更新时候,通过邮件通知订阅该版块的用户关注更新. 10 | 11 | type updates struct { 12 | topic string 13 | order int 14 | } 15 | 16 | //TechBBS 科技论坛,是发送通知的主体 17 | type TechBBS struct { 18 | mailObservers []IMailReceiver ///邮件订阅该板块动态的用户 19 | context context.Context ///更新的上下消息,其实就是一堆参数了 20 | } 21 | 22 | //IMailReceiver 邮件接收者就是订阅更新的用户 23 | type IMailReceiver interface { 24 | Notice(context.Context) 25 | } 26 | 27 | //Registry 提供给用户通过邮件注册获取/订阅更新动态的能力 28 | func (t *TechBBS) Registry(receiver IMailReceiver) { 29 | t.mailObservers = append(t.mailObservers, receiver) 30 | } 31 | 32 | //SetConext 设置更新内容的上下文 33 | func (t *TechBBS) SetConext(ctx context.Context) { 34 | t.context = ctx 35 | } 36 | 37 | //notifyUpdate 通知订阅用户,我更新了,你们可以来看了 38 | func (t *TechBBS) noticeAllUpdate() { 39 | for _, m := range t.mailObservers { 40 | //逐个通知 41 | m.Notice(t.context) 42 | } 43 | } 44 | 45 | //User 用户 46 | type User struct { 47 | name string 48 | } 49 | 50 | //Notice 用户收到订阅通知 51 | func (u *User) Notice(ctx context.Context) { 52 | 53 | content := ctx.Value(updates{}).(updates) 54 | 55 | fmt.Printf("%s receive updates notice\n", u.name) 56 | 57 | fmt.Printf("updates order: %d content:%s\n", content.order, content.topic) 58 | } 59 | -------------------------------------------------------------------------------- /behavior/03_observer/obserser_test.go: -------------------------------------------------------------------------------- 1 | package observer 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | ) 7 | 8 | func TestObserver(t *testing.T) { 9 | 10 | //内容提供商,科技BBS 11 | techInfoProvider := TechBBS{} 12 | 13 | lily := User{"Lily"} 14 | 15 | jacky := User{"Jacky"} 16 | 17 | techInfoProvider.Registry(&lily) 18 | techInfoProvider.Registry(&jacky) 19 | 20 | updateKey := updates{} 21 | 22 | updateValue := updates{topic: "cosmos", order: 1001} 23 | 24 | updateContent := context.WithValue(context.Background(), updateKey, updateValue) 25 | techInfoProvider.SetConext(updateContent) 26 | 27 | techInfoProvider.noticeAllUpdate() 28 | 29 | } 30 | -------------------------------------------------------------------------------- /behavior/04_iterator/README.md: -------------------------------------------------------------------------------- 1 | # 迭代器模式 2 | 3 | 迭代器模式主要设计在于将一类相关的对象集中管理起来,通过一组通用的行为的迭代接口,对外提供一致性的迭代访问机制,使外部不必关心内部的数据类型和结构组织形式,目的是对集合类或者类集合类数据提供一致性的访问方式. 4 | 5 | 迭代器模式在各种语言中都有很深入,很广泛的应用,迭代器模式就是,对这种存在于语言中的基本模式的进一步应用。 6 | 7 | 迭代器模式中的两个重要的接口: 8 | 9 | ```go 10 | type Iterator interface { 11 | //定义迭代器可以进行的动作 12 | Next() 13 | //... 14 | } 15 | 16 | type IEnumerator interface { 17 | //返回一个迭代器接口 18 | Iterator() Iterator 19 | } 20 | ``` 21 | 22 | 迭代器是一种管理数据的方式,往往需要一个管理数据的内在容器/集合/链表/栈等结构,需要注意的是,一般在访问上不实现为链表的访问方式,而使用滑动游标的方式。 23 | 现实生活中的景区的地图指南就是一个很好的迭代器的例子,地图就是一个迭代器,不论是哪个景区,都能通过一张地图(或者说这个迭代器),让你明白当前景区的景点数,最佳参观顺序等,游客只需要按照迭代器的方式去访问景点即可。 24 | 25 | -------------------------------------------------------------------------------- /behavior/04_iterator/book_iterator/book_iterator.go: -------------------------------------------------------------------------------- 1 | // Package iterator is an example of the Iterator Pattern. 2 | package iterator 3 | 4 | // Iterator provides a iterator interface. 5 | type Iterator interface { 6 | Index() int 7 | Value() interface{} 8 | Has() bool 9 | Next() 10 | Prev() 11 | Reset() 12 | End() 13 | } 14 | 15 | // Aggregate provides a collection interface. 16 | type Aggregate interface { 17 | Iterator() Iterator 18 | } 19 | 20 | // BookIterator implements the Iterator interface. 21 | type BookIterator struct { 22 | shelf *BookShelf 23 | index int 24 | internal int 25 | } 26 | 27 | // Index returns current index 28 | func (i *BookIterator) Index() int { 29 | return i.index 30 | } 31 | 32 | // Value returns current value 33 | func (i *BookIterator) Value() interface{} { 34 | return i.shelf.Books[i.index] 35 | } 36 | 37 | // Has implementation. 38 | func (i *BookIterator) Has() bool { 39 | if i.internal < 0 || i.internal >= len(i.shelf.Books) { 40 | return false 41 | } 42 | return true 43 | } 44 | 45 | // Next goes to the next item. 46 | func (i *BookIterator) Next() { 47 | i.internal++ 48 | if i.Has() { 49 | i.index++ 50 | } 51 | } 52 | 53 | // Prev goes to the previous item. 54 | func (i *BookIterator) Prev() { 55 | i.internal-- 56 | if i.Has() { 57 | i.index-- 58 | } 59 | } 60 | 61 | // Reset resets iterator. 62 | func (i *BookIterator) Reset() { 63 | i.index = 0 64 | i.internal = 0 65 | } 66 | 67 | // End goes to the last item. 68 | func (i *BookIterator) End() { 69 | i.index = len(i.shelf.Books) - 1 70 | i.internal = i.index 71 | } 72 | 73 | // BookShelf implements the Aggregate interface. 74 | type BookShelf struct { 75 | Books []*Book 76 | } 77 | 78 | // Iterator creates and returns the iterator over the collection. 79 | func (b *BookShelf) Iterator() Iterator { 80 | return &BookIterator{shelf: b} 81 | } 82 | 83 | // Add adds an item to the collection. 84 | func (b *BookShelf) Add(book *Book) { 85 | b.Books = append(b.Books, book) 86 | } 87 | 88 | // Book implements a item of the collection. 89 | type Book struct { 90 | Name string 91 | } 92 | -------------------------------------------------------------------------------- /behavior/04_iterator/book_iterator/book_iterator_test.go: -------------------------------------------------------------------------------- 1 | // Package iterator is an example of the Iterator Pattern. 2 | package iterator 3 | 4 | import "testing" 5 | 6 | func TestIterator(t *testing.T) { 7 | 8 | shelf := new(BookShelf) 9 | 10 | books := []string{"A", "B", "C", "D", "E", "F"} 11 | 12 | for _, book := range books { 13 | shelf.Add(&Book{Name: book}) 14 | } 15 | 16 | for iterator := shelf.Iterator(); iterator.Has(); iterator.Next() { 17 | index, value := iterator.Index(), iterator.Value().(*Book) 18 | if value.Name != books[index] { 19 | t.Errorf("Expect Book.Name to %s, but %s", books[index], value.Name) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /behavior/04_iterator/iterator.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | //////////////////////////////// 4 | //使用景点例子 5 | 6 | //Iterator 游客地图是一个迭代器Iterator,为用户提供当前景区中不同景点的统一访问能力 7 | type Iterator interface { 8 | Reset() //重置 9 | FirstPot() IPot //首个景点 10 | IsLastPot() bool //当前景点是否是最后一个 11 | Next() IPot //下一个景点 12 | } 13 | 14 | //IPot 景点对象的接口 15 | type IPot interface { 16 | Visit() //景点可以参观 17 | } 18 | 19 | //ScenicArea 景区包含所有的景点 20 | type ScenicArea struct { 21 | count int //景点的数量 22 | pots []IPot //景点列表,景区可能一直在开发新的景点,所以景区的数量可能一直在增长 23 | } 24 | 25 | //PotsIterator 实现景区景点迭代器的对象 26 | //PotsIterator 该对象的目的就是为了隐藏景区本身 27 | //PotsIterator 实现为一个游标迭代器 28 | type PotsIterator struct { 29 | cursor, count int 30 | potsSlice []IPot 31 | } 32 | 33 | //Iterator 返回一个接待 34 | func (s *ScenicArea) Iterator() Iterator { 35 | return &PotsIterator{ 36 | cursor: 0, 37 | count: s.count, 38 | potsSlice: s.pots, 39 | } 40 | } 41 | 42 | //AddPot 添加景点 43 | func (s *ScenicArea) AddPot(pots ...IPot) { 44 | for i := range pots { 45 | s.count++ 46 | s.pots = append(s.pots, pots[i]) 47 | } 48 | } 49 | 50 | //PotsCount 添加景点 51 | func (s *ScenicArea) PotsCount() int { 52 | return s.count 53 | } 54 | 55 | //Reset 重置 56 | func (s *PotsIterator) Reset() { 57 | s.cursor = 0 58 | } 59 | 60 | //FirstPot 第一个景点 61 | func (s *PotsIterator) FirstPot() IPot { 62 | return s.potsSlice[0] 63 | } 64 | 65 | //IsLastPot 判断游标的位置 66 | func (s *PotsIterator) IsLastPot() bool { 67 | return s.cursor == s.count 68 | } 69 | 70 | //Next 去路线上的下一个景点 71 | func (s *PotsIterator) Next() IPot { 72 | s.cursor++ 73 | if s.IsLastPot() { 74 | return nil 75 | } 76 | return s.potsSlice[s.cursor] 77 | 78 | } 79 | -------------------------------------------------------------------------------- /behavior/04_iterator/iterator_test.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | //ChildPot 儿童景点 9 | type ChildPot struct { 10 | Name string 11 | } 12 | 13 | func (c *ChildPot) Visit() { 14 | fmt.Println("i am: ", c.Name) 15 | } 16 | func TestIterator(t *testing.T) { 17 | 18 | scenicArea := ScenicArea{} 19 | 20 | scenicArea.AddPot(&ChildPot{Name: "monkey garden"}, &ChildPot{Name: "fairy country"}, &ChildPot{Name: "future space"}) 21 | 22 | t.Log("pots count:", scenicArea.PotsCount()) 23 | 24 | potInterator := scenicArea.Iterator() 25 | 26 | pot := potInterator.FirstPot() 27 | 28 | t.Logf("first pot: %#v\n", pot) 29 | 30 | VisitAllPots(potInterator) 31 | 32 | t.Log("add a pot", "pot: count", scenicArea.PotsCount()) 33 | 34 | scenicArea.AddPot(&ChildPot{Name: "virtual world"}) 35 | 36 | t.Log("pots count:", scenicArea.PotsCount()) 37 | 38 | //切片变了,所以要重新获取快照. 39 | //如果想进一步了解看以这个例子: 40 | //https://github.com/crazybber/go-fucking-exercise/blob/master/routine/slice_read_write_test.go 41 | potInterator = scenicArea.Iterator() 42 | 43 | potInterator.Reset() 44 | 45 | VisitAllPots(potInterator) 46 | 47 | } 48 | 49 | func VisitAllPots(i Iterator) { 50 | for c := i.FirstPot(); !i.IsLastPot(); c = i.Next() { 51 | c.Visit() 52 | fmt.Printf("finish visit pot : %#v\n", c) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /behavior/04_iterator/person_iterator/iterator.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | import "fmt" 4 | 5 | /* 6 | 设计思想: 7 | 1. Iterator结构体 8 | 实现Next() HasNext()方法 9 | 2. Container容器 10 | 容器实现添加 移除Visitor 和 11 | */ 12 | //创建迭代器 13 | type Iterator struct { 14 | index int 15 | Container 16 | } 17 | 18 | func (i *Iterator) Next() Visitor { 19 | fmt.Println(i.index) 20 | visitor := i.list[i.index] 21 | i.index += 1 22 | return visitor 23 | } 24 | 25 | func (i *Iterator) HasNext() bool { 26 | if i.index >= len(i.list) { 27 | return false 28 | } 29 | return true 30 | } 31 | 32 | //创建容器 33 | type Container struct { 34 | list []Visitor 35 | } 36 | 37 | func (c *Container) Add(visitor Visitor) { 38 | c.list = append(c.list, visitor) 39 | } 40 | 41 | func (c *Container) Remove(index int) { 42 | if index < 0 || index > len(c.list) { 43 | return 44 | } 45 | c.list = append(c.list[:index], c.list[index+1:]...) 46 | } 47 | 48 | //创建Visitor接口 49 | type Visitor interface { 50 | Visit() 51 | } 52 | 53 | //创建具体的visitor对象 54 | type Teacher struct{} 55 | 56 | type Analysis struct{} 57 | 58 | func (t *Teacher) Visit() { 59 | fmt.Println("this is teacher visitor") 60 | } 61 | 62 | func (a *Analysis) Visit() { 63 | fmt.Println("this is analysis visitor") 64 | } 65 | 66 | //工厂方法创建迭代器 67 | func NewIterator() *Iterator { 68 | return &Iterator{ 69 | index: 0, 70 | Container: Container{}, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /behavior/04_iterator/person_iterator/iterator_test.go: -------------------------------------------------------------------------------- 1 | package iterator 2 | 3 | import "testing" 4 | 5 | func TestIterator_Next(t *testing.T) { 6 | teacher := new(Teacher) 7 | analysis := new(Analysis) 8 | //迭代器 9 | iterator := NewIterator() 10 | iterator.Add(teacher) 11 | iterator.Add(analysis) 12 | if len(iterator.list) != 2 { 13 | t.Error("期望的count is 2") 14 | } 15 | for iterator.HasNext() { 16 | iterator.Next().Visit() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /behavior/05_template_method/README.md: -------------------------------------------------------------------------------- 1 | # 模版方法模式 2 | 3 | 模板方法主要是为了解决一些方法通用性问题,将通用方法放在父类,需要单独实现的方法放在其他类中,将通用方法和变化的方法分开,各自实现分开到不同的地方,并且可以单独实现,往往是将将变化部分方法/处理器的实现延迟到子类或者其他可注入的类型中。 4 | 5 | 6 | go 的结构对象和接口的分离做的更彻底,不需要像其他语言一样显式声明继承或者实现关系,所以在实现模板方法时候更灵活。 7 | 8 | 模板方法的特点在于,定义一好一套接口,定义基本类型结构作为父类,同时在父类中组合好调用方式,并实现好通用的方法,变化部分的接口的具体实现可以在子类或者其他地方实现。 9 | 10 | 现实生活中工作中,文件打印就是一个很好的模板方法的例子,我们打印操作一般式: 11 | 12 | 文件--->设置--->打印. 13 | 14 | 但是对于不同的打印方式对于同一个文件的打印实现可能略有不同。 15 | 16 | 比如虚拟打印到 pdf或者xps与打印打纸质文件需要的设置和打印肯定式不同的,虚拟打印你肯定需要指明输出路径,纸质打印需要可能需要设置打印质量,质量越高,越费墨。 17 | 18 | -------------------------------------------------------------------------------- /behavior/05_template_method/template_method.go: -------------------------------------------------------------------------------- 1 | package templatemethod 2 | 3 | import "fmt" 4 | 5 | //////////////////////////////// 6 | //使用打印的例子 7 | 8 | //IPrinter 定义打印的流程 9 | type IPrinter interface { 10 | Set(mark string) 11 | Print() 12 | } 13 | 14 | //Printer 定义基本结构类型 15 | type Printer struct { 16 | workerMark string 17 | printer IPrinter //指项实际工作的类型 18 | } 19 | 20 | //LoadDrive 载入驱动 21 | func (p *Printer) LoadDrive() { 22 | fmt.Print("init print drive\n") 23 | } 24 | 25 | //UnLoadDrive 卸载驱动 26 | func (p *Printer) UnLoadDrive() { 27 | fmt.Print("unload drive\n") 28 | } 29 | 30 | //Set 设置参数,这是变化的部分 31 | func (p *Printer) Set(mark string) { 32 | p.workerMark = mark 33 | //调用实现 34 | if p.printer != nil { 35 | p.printer.Set(mark) 36 | } 37 | } 38 | 39 | //Print 执行打印,这是变化的部分 40 | func (p *Printer) Print() { 41 | //调用实现 42 | fmt.Print("print with task mark: ", p.workerMark, "\n") 43 | if p.printer != nil { 44 | p.printer.Print() 45 | } 46 | 47 | } 48 | 49 | //DoPrintWork 打印 50 | //DoPrintWork 定义了打印的流程 51 | func (p *Printer) DoPrintWork() { 52 | p.LoadDrive() 53 | p.Set(p.workerMark) 54 | p.Print() 55 | p.UnLoadDrive() 56 | } 57 | 58 | //PDF 虚拟打印 59 | type PDF struct { 60 | Printer 61 | output string 62 | } 63 | 64 | //Print to a PDF 65 | func (p *PDF) Print() { 66 | fmt.Print("print to PDF ,save to ", p.output, "\n") 67 | 68 | } 69 | 70 | //DevicePrinter 设备打印机 71 | type DevicePrinter struct { 72 | Printer 73 | quality int //1,2,3表示打印高中低 74 | } 75 | 76 | //Print to a Paper 77 | func (d *DevicePrinter) Print() { 78 | fmt.Print("print to Paper ,with quality: ", d.quality, "\n") 79 | } 80 | -------------------------------------------------------------------------------- /behavior/05_template_method/template_method_test.go: -------------------------------------------------------------------------------- 1 | package templatemethod 2 | 3 | import "testing" 4 | 5 | func TestTemplateMethod(t *testing.T) { 6 | 7 | //打印机 8 | aprinter := Printer{} 9 | 10 | //这个是被复合的work流程 11 | aprinter.DoPrintWork() 12 | 13 | //连接PDF打印机 14 | aprinter.printer = &PDF{output: "./home"} 15 | 16 | aprinter.Set("---PDF--") 17 | //打印 18 | aprinter.DoPrintWork() 19 | 20 | //连接纸质打印机 21 | aprinter.printer = &DevicePrinter{quality: 5} 22 | 23 | aprinter.Set("---Paper--") 24 | //打印 25 | aprinter.DoPrintWork() 26 | 27 | } 28 | -------------------------------------------------------------------------------- /behavior/06_chain_of_responsibility/README.md: -------------------------------------------------------------------------------- 1 | # 职责链模式 2 | 3 | 职责链模式用于动态组合一些行为,在go实现相对更为简单,行为模式中提到的option模式跟此模式很像,但是两者场景用例不同,op模式的行为常常用于对象初始化或者某一过程中设置参数,职责链主要作用在于动态组合行为链,以便处理特定的对象或者问题。 4 | 5 | 6 | 各大框架和语言中间件模式就是一个典型的职责链模式,一个对象,在中间件中层层传递,每一层都可以进行自己的处理。 7 | 8 | Golang中,由于函数对象存在使得职责链模式实现来更加灵活。 9 | 10 | 现实生活中的一个申请的行政审批流程就是一个很好的职责链的例子,一个申请提交之后,要经过若干人对其进行处理。 11 | -------------------------------------------------------------------------------- /behavior/06_chain_of_responsibility/responsibility_chain.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import "fmt" 4 | 5 | //////////////////////////////// 6 | //使用费用申请审批的例子 7 | 8 | //FeeRequest 这就要要处理的对象 9 | type FeeRequest struct { 10 | Mount int //申请的金额 11 | RequiredLevel int //审批人等级要求 12 | Name string //申请人 13 | } 14 | 15 | //IApprove 审批动作 16 | type IApprove interface { 17 | SetNext(next IApprove) //设置下一个审批流转 18 | HaveRight(level int) bool // 19 | HandleApproval(request FeeRequest) bool 20 | } 21 | 22 | //////////////////////////////// 23 | //实现方式1 24 | //////////////////////////////// 25 | 26 | //FeeRequestChainFlow 定义一个流程管理器 27 | type FeeRequestChainFlow struct { 28 | approvers []IApprove //下一个流程节点 29 | } 30 | 31 | //AddApprover 添加一个审批对象 32 | func (f *FeeRequestChainFlow) AddApprover(approver IApprove) { 33 | f.approvers = append(f.approvers, approver) 34 | } 35 | 36 | //RunApprovalFlow to deal request by chains 37 | func (f *FeeRequestChainFlow) RunApprovalFlow(request FeeRequest) { 38 | 39 | for i := 0; i < len(f.approvers); i++ { 40 | result := f.approvers[i].HandleApproval(request) 41 | if !result { 42 | //中间有一个环节出问题,流程就终止 43 | break 44 | } 45 | } 46 | 47 | } 48 | 49 | //////////////////////////////// 50 | //实现方式2 51 | //////////////////////////////// 52 | 53 | //GM 总经理要审批 54 | type GM struct { 55 | nextHandler IApprove //下一个流程节点 56 | level int 57 | } 58 | 59 | //NewGM 总经理审批 60 | func NewGM() IApprove { 61 | return &GM{level: 8} 62 | } 63 | 64 | //SetNext 设置下一个审批节点 65 | func (g *GM) SetNext(next IApprove) { 66 | g.nextHandler = next 67 | } 68 | 69 | //HaveRight 处理审批所需要的权限级别 70 | func (g *GM) HaveRight(RequiredLevel int) bool { 71 | return g.level > RequiredLevel 72 | } 73 | 74 | //HandleApproval 进行审批 75 | func (g *GM) HandleApproval(request FeeRequest) bool { 76 | if g.HaveRight(request.RequiredLevel) { 77 | fmt.Printf("GM permit %s %d fee request\n", request.Name, request.Mount) 78 | return true 79 | } 80 | fmt.Printf("GM NO right to approve %s %d fee request\n", request.Name, request.Mount) 81 | //direct forward to Next One 82 | if g.nextHandler != nil { 83 | return g.nextHandler.HandleApproval(request) 84 | } 85 | return true 86 | } 87 | 88 | //CFO 需要审批 89 | type CFO struct { 90 | nextHandler IApprove //下一个流程节点 91 | level int 92 | } 93 | 94 | //NewCFO 对象 95 | func NewCFO() IApprove { 96 | return &CFO{} 97 | 98 | } 99 | 100 | //HaveRight CFO总是有权限的 101 | func (*CFO) HaveRight(RequiredLevel int) bool { 102 | return true 103 | } 104 | 105 | //SetNext 设置下一个审批节点 106 | func (c *CFO) SetNext(next IApprove) { 107 | c.nextHandler = next 108 | } 109 | 110 | //HandleApproval 进行审批 111 | func (c *CFO) HandleApproval(request FeeRequest) bool { 112 | if request.Mount < 1e+10 { 113 | fmt.Printf("CFO permit %s %d fee request\n", request.Name, request.Mount) 114 | return true 115 | } 116 | fmt.Printf("CFO No right to approve %s %d fee request \n", request.Name, request.Mount) 117 | if c.nextHandler != nil { 118 | return c.nextHandler.HandleApproval(request) 119 | } 120 | return true 121 | } 122 | 123 | //CEO 需要审批 124 | type CEO struct { 125 | } 126 | 127 | //NewCEO 对象 128 | func NewCEO() IApprove { 129 | return &CEO{} 130 | } 131 | 132 | //HaveRight CEO总是有权限的 133 | func (*CEO) HaveRight(RequiredLevel int) bool { 134 | return true 135 | } 136 | 137 | //SetNext 设置下一个审批节点 138 | func (c *CEO) SetNext(next IApprove) { 139 | //no thing to do 140 | } 141 | 142 | //HandleApproval 进行审批 143 | func (*CEO) HandleApproval(request FeeRequest) bool { 144 | if request.Mount < 1e+15 { 145 | fmt.Printf("CEO permit %s %d fee request\n", request.Name, request.Mount) 146 | return true 147 | } 148 | fmt.Printf("CEO deny %s %d fee request \n", request.Name, request.Mount) 149 | return false 150 | } 151 | -------------------------------------------------------------------------------- /behavior/06_chain_of_responsibility/responsibility_chain_game_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import "testing" 4 | 5 | type GameType int 6 | 7 | const ( 8 | TypeFPS GameType = 1 9 | TypeRPG = TypeFPS << 1 10 | ) 11 | 12 | type Game interface { 13 | Type() GameType 14 | Start(player string) 15 | } 16 | 17 | // chain of responsibility 18 | type GameSelector struct { 19 | GameList []Game 20 | } 21 | 22 | func (g *GameSelector) AddGame(games ...Game) { 23 | g.GameList = append(g.GameList, games...) 24 | } 25 | 26 | func (g GameSelector) Start(t GameType, player string) { 27 | for _, v := range g.GameList { 28 | if v.Type() == t { 29 | v.Start(player) 30 | return 31 | } 32 | } 33 | } 34 | 35 | type FPSGame struct { 36 | t GameType 37 | } 38 | 39 | func (f FPSGame) Start(player string) { 40 | println(player, "join in fps game") 41 | } 42 | 43 | func (f FPSGame) Type() GameType { 44 | return f.t 45 | } 46 | 47 | type RPGGame struct { 48 | t GameType 49 | } 50 | 51 | func (RPGGame) Start(player string) { 52 | println(player, "join in rpg game") 53 | } 54 | 55 | func (r RPGGame) Type() GameType { 56 | return r.t 57 | } 58 | 59 | func TestChainsOfResponsibilityInGame(t *testing.T) { 60 | fps := FPSGame{TypeFPS} 61 | rpg := RPGGame{TypeRPG} 62 | 63 | sl := GameSelector{} 64 | sl.AddGame(fps, rpg) 65 | 66 | player := "icg" 67 | sl.Start(TypeRPG, player) 68 | println() 69 | sl.Start(TypeFPS, player) 70 | // output: 71 | /* 72 | icg join in rpg game 73 | icg join in fps game 74 | */ 75 | } 76 | -------------------------------------------------------------------------------- /behavior/06_chain_of_responsibility/responsibility_chain_test.go: -------------------------------------------------------------------------------- 1 | package chain 2 | 3 | import "testing" 4 | 5 | func TestChainsOfResponsibilityInApprovementList(t *testing.T) { 6 | 7 | request := FeeRequest{ 8 | Name: "sophia", 9 | Mount: 1e+9, 10 | RequiredLevel: 8, 11 | } 12 | 13 | flow := &FeeRequestChainFlow{} 14 | 15 | gm1 := &GM{level: 7} 16 | 17 | cfo1 := &CFO{level: 9} 18 | 19 | flow.AddApprover(gm1) 20 | flow.AddApprover(cfo1) 21 | 22 | flow.RunApprovalFlow(request) 23 | 24 | t.Log("------second flow--------") 25 | request = FeeRequest{ 26 | Name: "peter", 27 | Mount: 1e+13, 28 | RequiredLevel: 8, 29 | } 30 | 31 | ceo := &CEO{} 32 | flow.AddApprover(ceo) 33 | 34 | flow.RunApprovalFlow(request) 35 | 36 | } 37 | 38 | func TestChainsOfResponsibilityInApprovementLink(t *testing.T) { 39 | 40 | request := FeeRequest{ 41 | Name: "sophia", 42 | Mount: 1e+11, 43 | RequiredLevel: 8, 44 | } 45 | 46 | gm := &GM{level: 7} 47 | 48 | cfo := &CFO{level: 9} 49 | 50 | ceo := &CEO{} 51 | 52 | gm.SetNext(cfo) 53 | 54 | cfo.SetNext(ceo) 55 | 56 | gm.HandleApproval(request) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /behavior/07_visitor/README.md: -------------------------------------------------------------------------------- 1 | # 访问者模式 2 | 3 | 访问者模式,常常1对N的情况处理,用于将数据结构和操作进行分离,访问者模式侧重Visitor一侧而不是被访问的一侧,可以方便的增加很多访问者,而是不是增加更多的访问对象. 4 | 5 | 访问者模式的目的是为了解耦对象和对象的使用和处理逻辑,一边提供资源,一边使用,也可以说是解耦生产出来的产品和产品消费者,同样用于分离操作的还有策略模式(strategy pattern),但两者存在侧重点不同. 6 | 7 | 一个石油的例子,油田提供石油,不同的客户将石油用到不同的地方,可能用于生产武器,可能用于提纯材料,生产服装,也可能只是用于只是用于提供动力,这些客户就是石油的不同visitor. 8 | 9 | 客户买石油的时候,都要遵循油田的购买方式,否则,就不卖给你,这个特定的购买方式就是指Visitor模式中常说的Accept方法。 10 | 11 | 打游戏是个不错的例子。 12 | 13 | 玩家角色需要跟NPC,其他玩家,以及游戏环境,交换这些都是Visit的过程. 14 | 15 | -------------------------------------------------------------------------------- /behavior/07_visitor/visitor.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import "fmt" 4 | 5 | //////////////////////////////// 6 | //使用石油的例子 7 | 8 | //IGasResource 作为资源提供接口 9 | type IGasResource interface { 10 | Accept(IGasVisitor) 11 | } 12 | 13 | //gas 汽油 14 | type gas struct { 15 | density int 16 | } 17 | 18 | //IGasVisitor 访问者接口 19 | type IGasVisitor interface { 20 | Visit(gas) 21 | } 22 | 23 | //Accept 接待汽油客户 24 | func (g gas) Accept(visitor IGasVisitor) { 25 | visitor.Visit(g) 26 | } 27 | 28 | //diesel 柴油 29 | type diesel struct { 30 | energy int 31 | } 32 | 33 | //IDieselVisitor 访问者接口 34 | type IDieselVisitor interface { 35 | Visit(diesel) 36 | } 37 | 38 | //Accept 提供柴油 39 | func (d diesel) Accept(visitor IDieselVisitor) { 40 | visitor.Visit(d) 41 | } 42 | 43 | //militaryFactory 军工厂,消费石油,制造务器 44 | type militaryFactory struct { 45 | name string 46 | } 47 | 48 | //Visit 军工厂只够买柴油,制造武器 49 | func (m *militaryFactory) Visit(d diesel) { 50 | fmt.Println("militaryFactory: use diesel with inner energy", d.energy) 51 | } 52 | 53 | // clothFactory 服务装类工厂,购买汽油,制造化纤物品 54 | type clothFactory struct{} 55 | 56 | //Visit 购买汽油 57 | func (c *clothFactory) Visit(g gas) { 58 | fmt.Println("clothFactory: use gas with density", g.density) 59 | } 60 | -------------------------------------------------------------------------------- /behavior/07_visitor/visitor_game.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import "fmt" 4 | 5 | //IGameObject 游戏上下文对象 6 | type IGameObject interface { 7 | Accept(IGameVisitor) 8 | } 9 | 10 | //Player 代表其他玩家,因为用户角色需要和其他玩家交互 11 | type Player struct { 12 | Name string 13 | Level int 14 | } 15 | 16 | //Accept 提供交互接口 17 | func (p Player) Accept(v IGameVisitor) { 18 | v.VisitPlayer(p) 19 | } 20 | 21 | //NPC 被方法对象 22 | type NPC struct { 23 | Name string 24 | IsImmortal bool //是否可以被打死 25 | } 26 | 27 | //Accept 接受聚能NPC访问能力的对象的访问 28 | func (n NPC) Accept(v IGameVisitor) { 29 | v.VisitNPC(n) 30 | } 31 | 32 | //SystemEnv 环境对象 33 | type SystemEnv struct { 34 | Mark string //环境标识 35 | Version string //环境版本 36 | } 37 | 38 | //Accept 提供对环境的访问 39 | func (s SystemEnv) Accept(v IGameVisitor) { 40 | v.VisitSystemEnv(s) 41 | } 42 | 43 | //IGameVisitor 游戏提供的环境访问能力 44 | type IGameVisitor interface { 45 | VisitPlayer(Player) 46 | VisitNPC(NPC) 47 | VisitSystemEnv(SystemEnv) 48 | } 49 | 50 | // SettingVisitor 只提供Setting的能力 51 | type SettingVisitor struct{} 52 | 53 | //VisitPlayer 提供交互的第三方对象的信息 54 | func (SettingVisitor) VisitPlayer(p Player) { 55 | fmt.Printf("Game Player: Name:%s ,Level:%d\n", p.Name, p.Level) 56 | } 57 | 58 | //VisitNPC 提供NPC的信息 59 | func (SettingVisitor) VisitNPC(n NPC) { 60 | fmt.Printf("Game NPC: Name:%s ,Immortal:%v\n", n.Name, n.IsImmortal) 61 | } 62 | 63 | //VisitSystemEnv 提供游戏环境信息 64 | func (SettingVisitor) VisitSystemEnv(s SystemEnv) { 65 | fmt.Printf("Game Env: Mark:%s ,Version:%s\n", s.Mark, s.Version) 66 | } 67 | 68 | // Attacker 攻击者 69 | type Attacker struct{ name string } 70 | 71 | //VisitPlayer 攻击其他玩家 72 | func (a Attacker) VisitPlayer(p Player) { 73 | fmt.Printf("%s Attack Player : %s\n", a.name, p.Name) 74 | } 75 | 76 | //VisitNPC 攻击NPC 77 | func (a Attacker) VisitNPC(n NPC) { 78 | fmt.Printf("%s Attack NPC: %s\n", a.name, n.Name) 79 | } 80 | 81 | //VisitSystemEnv 攻击环境,如石头,大门,墙壁 82 | func (a Attacker) VisitSystemEnv(s SystemEnv) { 83 | fmt.Printf("Unsupported target %s\n", "game env") 84 | } 85 | -------------------------------------------------------------------------------- /behavior/07_visitor/visitor_test.go: -------------------------------------------------------------------------------- 1 | package visitor 2 | 3 | import "testing" 4 | 5 | func TestSingleVisitor(t *testing.T) { 6 | 7 | //汽油提供给,制衣工厂 8 | g := gas{density: 100} 9 | 10 | //柴油,提供给军工厂 11 | d := diesel{energy: 897} 12 | 13 | //购买石油的客户 14 | m := &militaryFactory{} 15 | 16 | c := &clothFactory{} 17 | 18 | g.Accept(c) 19 | 20 | d.Accept(m) 21 | 22 | } 23 | 24 | func TestGameVisitorsList(t *testing.T) { 25 | 26 | retriveSetting := SettingVisitor{} 27 | attacker := Attacker{} 28 | 29 | pA := Player{"snow dance", 100} //角色名名:snow dance 100级 30 | pB := Player{"fire dragon", 120} 31 | npc := NPC{"groceries", true} //卖杂货的NPC,是能被打死的 32 | env := SystemEnv{"made by china", "v1.2.11"} 33 | 34 | //游戏对象 35 | gameObjects := []IGameObject{pA, npc, env, pB} 36 | 37 | for _, v := range gameObjects { 38 | v.Accept(retriveSetting) 39 | } 40 | 41 | t.Log("\n---- !!!attack!!!- --") 42 | 43 | for _, v := range gameObjects { 44 | v.Accept(attacker) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /behavior/08_interpreter/README.md: -------------------------------------------------------------------------------- 1 | # 解释器模式 2 | 3 | 解释器模式的本质是就是自己定一套规则、语言、表达方式,也就是所谓的DSL(Domain Specific Language),然后按照定义解析执行DSL,常见的自定义协议,私有协议,就是一个中解释器模式的概念,使用者按照协议的规则做事。 4 | 5 | 解释器模式的意义在于,它分离多种复杂功能的实现,每个功能只需关注自身的解释。 6 | 7 | 对于调用者不用关心内部的解释器的工作,只需要用简单的方式组合命令。 8 | 9 | 常见的redis协议就是一个很好解释器模式实现,通过redis-cli可以发送各种指令给到redis-server,服务端解释执行后返回结果。 10 | 11 | 我们常说的计算器,是一个很典型的解释器模式,它用来解释执行加减乘除的运行规则,现实生活中,哑语翻译、音乐乐谱、摩尔斯电码,道路交通指示系统,等都是解释器模式的好例子。 12 | 13 | 我们来设计一个简单交流标识系统,来表达会议中两个人交流时候的谈话方式,以代替冗长的语言表述. 14 | 15 | A "->" B 表示 A说,B听,此时B不能发言。 16 | A "<-" B 表示 B说,A听,此时B不能发言。 17 | A "<->" B 表示 A 和 B 可以自由发言。 18 | 19 | 解释器模式中的关键角色: 20 | 21 | 1. 表达式 一般就是一串带解析的内容流,可能是字符串,也可能是字节流等 22 | 2. 表达式解释类,一般就是解析器 23 | 3. 表达式子类型解析器,对于表达式中解析到的不同情况,交给不同的解析子类去处理。 24 | 25 | -------------------------------------------------------------------------------- /behavior/08_interpreter/interpreter.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | //用会议交流的例子,规则如下: 9 | // A表示左边发言者,B表示右边发言者 10 | // A "->" B 表示 A说,B听,此时B不能发言。 11 | // A "<-" B 表示 B说,A听,此时B不能发言。 12 | // A "<->" B 表示 A 和 B 可以自由发言。 13 | 14 | //IActionInterpret 解释器 15 | type IActionInterpret interface { 16 | Interpret() 17 | } 18 | 19 | //SpeakerUnit 每个参与动作的单元都是,会议发言者 20 | type SpeakerUnit struct { 21 | Name string 22 | CanSpeak bool //每个参会者有两种基本的行为,发言或者安静 23 | } 24 | 25 | //Interpret 解释自己的基本行为 26 | func (s *SpeakerUnit) Interpret() { 27 | if s.CanSpeak { 28 | fmt.Println("i'm", s.Name, "i’m speaking") 29 | return 30 | } 31 | fmt.Println("i'm", s.Name, "already silent") 32 | } 33 | 34 | //LeftSpeakAction :A "->" B 表示 A说,B听,此时B不能发言。 35 | type LeftSpeakAction struct { 36 | leftSpeaker, rightSpeaker IActionInterpret 37 | } 38 | 39 | //Interpret 解释执行 40 | func (l *LeftSpeakAction) Interpret() { 41 | 42 | l.leftSpeaker.Interpret() 43 | l.rightSpeaker.Interpret() 44 | 45 | } 46 | 47 | //RightSpeakAction :A "<-" B 表示 B说,A听,此时B不能发言。 48 | type RightSpeakAction struct { 49 | leftSpeaker, rightSpeaker IActionInterpret 50 | } 51 | 52 | //Interpret 解释执行 53 | func (r *RightSpeakAction) Interpret() { 54 | r.leftSpeaker.Interpret() 55 | r.rightSpeaker.Interpret() 56 | } 57 | 58 | //BothSpeakAction : A "<->" B 表示 A 和 B 可以自由发言。 59 | type BothSpeakAction struct { 60 | leftSpeaker, rightSpeaker IActionInterpret 61 | } 62 | 63 | //Interpret 解释执行 64 | func (b *BothSpeakAction) Interpret() { 65 | b.leftSpeaker.Interpret() 66 | b.rightSpeaker.Interpret() 67 | } 68 | 69 | //SignParser 我们自己的DSL解析器 70 | type SignParser struct { 71 | actionUnits []string //要解析的内容 72 | result IActionInterpret //上一个也是解释器单元 73 | } 74 | 75 | //解析 -> 76 | func (s *SignParser) newLeftSpeakAction() IActionInterpret { 77 | 78 | left := &SpeakerUnit{ 79 | Name: s.actionUnits[0], 80 | CanSpeak: true, 81 | } 82 | right := &SpeakerUnit{ 83 | Name: s.actionUnits[2], 84 | } 85 | return &LeftSpeakAction{ 86 | leftSpeaker: left, 87 | rightSpeaker: right, 88 | } 89 | } 90 | 91 | //解析 <- 92 | func (s *SignParser) newRightSpeakAction() IActionInterpret { 93 | 94 | left := &SpeakerUnit{ 95 | Name: s.actionUnits[0], 96 | } 97 | right := &SpeakerUnit{ 98 | Name: s.actionUnits[2], 99 | CanSpeak: true, 100 | } 101 | return &LeftSpeakAction{ 102 | leftSpeaker: left, 103 | rightSpeaker: right, 104 | } 105 | } 106 | 107 | //解析 <-> 108 | func (s *SignParser) newBothSpeakAction() IActionInterpret { 109 | 110 | left := &SpeakerUnit{ 111 | Name: s.actionUnits[0], 112 | CanSpeak: true, 113 | } 114 | right := &SpeakerUnit{ 115 | Name: s.actionUnits[2], 116 | CanSpeak: true, 117 | } 118 | return &LeftSpeakAction{ 119 | leftSpeaker: left, 120 | rightSpeaker: right, 121 | } 122 | } 123 | 124 | //Parse 标识解析器进行解析,exp就是要解释的内容 125 | func (s *SignParser) Parse(exp string) { 126 | 127 | s.actionUnits = strings.Split(exp, " ") //单元分割符 128 | 129 | switch s.actionUnits[1] { 130 | case "->": 131 | s.result = s.newLeftSpeakAction() 132 | case "<-": 133 | s.result = s.newRightSpeakAction() 134 | case "<->": 135 | s.result = s.newBothSpeakAction() 136 | default: 137 | fmt.Println("some error raised") 138 | } 139 | 140 | } 141 | 142 | //Result 就是两边正确执行了动作 143 | func (s *SignParser) Result() { 144 | 145 | s.result.Interpret() 146 | 147 | } 148 | -------------------------------------------------------------------------------- /behavior/08_interpreter/interpreter_caculator.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | ) 7 | 8 | //解释自定义的加减法运算 9 | //输入:字符串 10 | //输出:整数值 11 | //将一个包含加减运算的字符串,正常解析出结果 12 | 13 | //Element 每个元素的解释接口 14 | type Element interface { 15 | Interpret() int 16 | } 17 | 18 | //ValElement 值节点 19 | type ValElement struct { 20 | val int 21 | } 22 | 23 | //Interpret 值解析单元的返回值 24 | func (n *ValElement) Interpret() int { 25 | return n.val 26 | } 27 | 28 | //AddOperate Operation(+) 29 | type AddOperate struct { 30 | left, right Element 31 | } 32 | 33 | //Interpret AddOperate 34 | func (n *AddOperate) Interpret() int { 35 | return n.left.Interpret() + n.right.Interpret() 36 | } 37 | 38 | //MinOperate Operation(-) 39 | type MinOperate struct { 40 | left, right Element 41 | } 42 | 43 | //Interpret MinOperate 44 | func (n *MinOperate) Interpret() int { 45 | return n.left.Interpret() - n.right.Interpret() 46 | } 47 | 48 | //Parser machine 49 | type Parser struct { 50 | exp []string 51 | index int 52 | prev Element 53 | } 54 | 55 | //Parse content 56 | func (p *Parser) Parse(exp string) { 57 | p.exp = strings.Split(exp, " ") 58 | 59 | for { 60 | if p.index >= len(p.exp) { 61 | return 62 | } 63 | switch p.exp[p.index] { 64 | case "+": 65 | p.prev = p.newAddOperte() 66 | case "-": 67 | p.prev = p.newMinOperte() 68 | default: 69 | p.prev = p.newValElement() 70 | } 71 | } 72 | } 73 | 74 | func (p *Parser) newAddOperte() Element { 75 | p.index++ 76 | return &AddOperate{ 77 | left: p.prev, 78 | right: p.newValElement(), 79 | } 80 | } 81 | 82 | func (p *Parser) newMinOperte() Element { 83 | p.index++ 84 | return &MinOperate{ 85 | left: p.prev, 86 | right: p.newValElement(), 87 | } 88 | } 89 | 90 | func (p *Parser) newValElement() Element { 91 | v, _ := strconv.Atoi(p.exp[p.index]) 92 | p.index++ 93 | return &ValElement{ 94 | val: v, 95 | } 96 | } 97 | 98 | //Result of parsing result 99 | func (p *Parser) Result() Element { 100 | return p.prev 101 | } 102 | -------------------------------------------------------------------------------- /behavior/08_interpreter/interpreter_test.go: -------------------------------------------------------------------------------- 1 | package interpreter 2 | 3 | import "testing" 4 | 5 | func TestMeetingActionSignInterpreter(t *testing.T) { 6 | 7 | p := &SignParser{} 8 | 9 | p.Parse("rose -> tom") 10 | p.Result() 11 | 12 | p.Parse("rose <-> tom") 13 | p.Result() 14 | 15 | p.Parse("rose <- tom") 16 | 17 | p.Result() 18 | 19 | //should error 20 | p.Parse("rose + tom") 21 | 22 | p.Result() 23 | 24 | } 25 | 26 | func TestCalculatorInterpreter(t *testing.T) { 27 | p := &Parser{} 28 | p.Parse("1 + 2 + 3 - 4 + 5 - 6") 29 | res := p.Result().Interpret() 30 | expect := 1 31 | if res != expect { 32 | t.Fatalf("expect %d got %d", expect, res) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /behavior/09_memento/README.md: -------------------------------------------------------------------------------- 1 | # 备忘录模式 2 | 3 | 备忘录模式,又叫快照模式,备份模式,用于在不暴露内部状态的情况下,保存程序内部状态到外部。 4 | 5 | 主要保存的是数据(也就是状态),这些数据可以是静态数据,也可以是一个操作描述。 6 | 7 | 在CQRS中的事件溯源模式(Event Sourcing)中,就是保存事件的操作(包含操作参数集)到持久化数据中,以达到事件回溯的目的. 8 | 9 | 现实生活中涉及备份和快照的例子就太多了,日常的打游戏存档,下次玩的时候,读取存档,读取进度,也是这个模式。 10 | 11 | 该模式中有三个关键角色: 12 | 13 | - 1. 发起人角色 Originator: 负责记录当前的内部状态,提供当前状态数据,并负责恢复备忘录数据。 14 | - 2. 备忘录角色 Memento : 负责存放发起人对象某个时刻的内部状态,这就是要保存的数据结构类型。 15 | - 3. 管理者角色 Caretaker: 负责保存备忘录对象。 16 | -------------------------------------------------------------------------------- /behavior/09_memento/memento.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | //////////////////////////////// 9 | //使用游戏玩家的角色存档和读取的例子 10 | 11 | //GamePlayer 是一个Originator 提供当前的游戏状态 12 | type GamePlayer struct { 13 | hp, mp, role, level int //血量,魔法值,当前关卡 14 | } 15 | 16 | //RoleStatusMemento 一条备忘数据,存放瞬时状态的数据结构,一个数据结构 17 | type RoleStatusMemento struct { 18 | tag string //存档记录本身的名称,以便下次识别读取 19 | hp, mp, level int //血量,魔法值,角色类型,当前关卡, 20 | timeMark string //存档的可视化时间 21 | } 22 | 23 | //RoleStatusCaretaker 负责保存角色当前的状态数据,提供存取能力 24 | //RoleStatusCaretaker 也是占内存/存储的地方,如果不停的读取,IO压力会变大的很大 25 | type RoleStatusCaretaker struct { 26 | memens map[string]*RoleStatusMemento 27 | } 28 | 29 | //SaveStatus 保存当前角色的游戏状态 30 | func (r *RoleStatusCaretaker) SaveStatus(item *RoleStatusMemento) { 31 | r.memens[item.tag] = item 32 | fmt.Printf("Game File %s Saved at %s\n", item.tag, item.timeMark) 33 | } 34 | 35 | //RetriveStatus 提供需要的状态 36 | func (r *RoleStatusCaretaker) RetriveStatus(savedTag string) *RoleStatusMemento { 37 | return r.memens[savedTag] 38 | 39 | } 40 | 41 | //Create 创建游戏的当前档案存档 42 | func (g *GamePlayer) Create(tagName string) *RoleStatusMemento { 43 | 44 | return &RoleStatusMemento{ 45 | tag: tagName, 46 | hp: g.hp, 47 | mp: g.mp, 48 | level: g.level, 49 | timeMark: time.Now().String(), 50 | } 51 | } 52 | 53 | //Load 载入存档,恢复数据 54 | func (g *GamePlayer) Load(rm *RoleStatusMemento) { 55 | g.mp = rm.mp 56 | g.hp = rm.hp 57 | g.level = rm.level 58 | 59 | fmt.Printf("Game Profile had been restored to %s : %s\n", rm.tag, rm.timeMark) 60 | } 61 | 62 | //Status 玩家角色的当前状态 63 | func (g *GamePlayer) Status() { 64 | fmt.Printf("Current Level :%d HP:%d, MP:%d\n", g.level, g.hp, g.mp) 65 | } 66 | -------------------------------------------------------------------------------- /behavior/09_memento/memento_test.go: -------------------------------------------------------------------------------- 1 | package memento 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestGameArchive(t *testing.T) { 9 | 10 | gamerole := GamePlayer{hp: 1000, mp: 232, level: 20} 11 | 12 | datakeeper := RoleStatusCaretaker{memens: make(map[string]*RoleStatusMemento)} 13 | 14 | archive1 := gamerole.Create("第一次存档") 15 | 16 | //交给管数据的人,存起来 17 | datakeeper.SaveStatus(archive1) 18 | 19 | //模拟,随机玩会儿游戏 20 | time.Sleep(time.Millisecond * 1132) 21 | 22 | //更新角色当前状态 23 | gamerole = GamePlayer{hp: 500, mp: 10, level: 30} 24 | 25 | //看一下状态 26 | gamerole.Status() 27 | 28 | archive2 := gamerole.Create("第二次存档") 29 | 30 | //交给管数据的人,存起来 31 | datakeeper.SaveStatus(archive2) 32 | 33 | //准备恢复第一次的存档 34 | 35 | //查找档案 36 | restore1 := datakeeper.RetriveStatus("第一次存档") 37 | 38 | //载入档案 39 | gamerole.Load(restore1) 40 | 41 | //看一下状态 42 | gamerole.Status() 43 | 44 | } 45 | -------------------------------------------------------------------------------- /behavior/10_state/README.md: -------------------------------------------------------------------------------- 1 | # 状态模式 2 | 3 | 状态模式的目的就是设计一个状态机,用状态的改变/流转驱动行为变化,同时将状态的实现放在外部,以方便扩展状态。 4 | 5 | 日常生活的很多情况都在跟状态打交道,比如闹装有,振铃,非振铃状态,商业有营业和关门状态,饭有生,熟,半生不熟三种状态,机器人,游戏角色的拟人状态有:走,跑,跳,充电(休息)等状态。 6 | 7 | 状态模式的关键角色: 8 | 9 | + Context: 拥有多种状态的上下文对象(struct), 持有状态属性State 10 | + State: 封装特定状态行为的interface 11 | + ImplementedState: 具体的状态,继承接口State,不同的状态执行Context的不同行为。 12 | 13 | 状态模式常常需要耦合有状态的对象本身的引用,或者相关接口,以获取对象的完整的状态。 14 | -------------------------------------------------------------------------------- /behavior/10_state/state.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | //////////////////////////////// 9 | //使用机器人状态转换的例子 10 | //简化示例,假定机器人有:走,跑,静止三种活动状态 11 | 12 | //IRobotState 代表机器人此时的活动状态 13 | type IRobotState interface { 14 | //上下文可以用于传递参数 15 | Move(*SportRobot) 16 | } 17 | 18 | //SportRobot 定义一个运动机器人 19 | type SportRobot struct { 20 | Name string 21 | stateMachine IRobotState //状态机 22 | } 23 | 24 | //Move action,主动行为 25 | func (s *SportRobot) Move() { 26 | s.stateMachine.Move(s) 27 | } 28 | 29 | //UpdateState 更新状态 30 | func (s *SportRobot) UpdateState(newState IRobotState) { 31 | s.stateMachine = newState 32 | //触发行为 33 | s.stateMachine.Move(s) 34 | } 35 | 36 | //NewSportRobot 生产一个机器人 37 | func NewSportRobot(name string) *SportRobot { 38 | //默认一个状态 39 | return &SportRobot{ 40 | Name: name, 41 | stateMachine: &StillState{}, 42 | } 43 | 44 | } 45 | 46 | //RuningState 奔跑状态 47 | type RuningState struct { 48 | } 49 | 50 | //Move 实现跑的状态 51 | func (r *RuningState) Move(robot *SportRobot) { 52 | 53 | fmt.Println("i'm robot:", robot.Name) 54 | fmt.Println("i am running", time.Now()) 55 | 56 | } 57 | 58 | //WalkState 行走状态 59 | type WalkState struct { 60 | } 61 | 62 | //Move 实现行走状态 63 | func (w *WalkState) Move(robot *SportRobot) { 64 | fmt.Println("i'm robot:", robot.Name) 65 | fmt.Println("i am waling", time.Now()) 66 | 67 | } 68 | 69 | //StillState 静止状态 70 | type StillState struct { 71 | } 72 | 73 | //Move 实现静止状态 74 | func (s *StillState) Move(robot *SportRobot) { 75 | fmt.Println("i'm robot:", robot.Name) 76 | fmt.Println("i am sitting", time.Now()) 77 | 78 | } 79 | -------------------------------------------------------------------------------- /behavior/10_state/state_clock.go: -------------------------------------------------------------------------------- 1 | // Package state is an example of the State Pattern. 2 | package state 3 | 4 | //////////////////////////////// 5 | //闹装有两种状态,震铃状态,非震铃状态 6 | 7 | // AlertStater provides a common interface for various states. 8 | type AlertStater interface { 9 | Alert() string 10 | } 11 | 12 | // Alert implements an alert depending on its state. 13 | type Alert struct { 14 | state AlertStater 15 | } 16 | 17 | // Alert returns a alert string 代表振铃 18 | func (a *Alert) Alert() string { 19 | return a.state.Alert() 20 | } 21 | 22 | // SetState changes state 23 | func (a *Alert) SetState(state AlertStater) { 24 | a.state = state 25 | } 26 | 27 | // NewAlert is the Alert constructor,默认振铃是震动 28 | func NewAlert() *Alert { 29 | return &Alert{state: &AlertVibration{}} 30 | } 31 | 32 | // AlertVibration implements vibration alert 33 | type AlertVibration struct { 34 | } 35 | 36 | // Alert returns a alert string ,默认振铃 37 | func (a *AlertVibration) Alert() string { 38 | return "vibrating humming ... vibrating humming...vibrating humming..." 39 | } 40 | 41 | // AlertSong implements beep alert 42 | type AlertSong struct { 43 | } 44 | 45 | // Alert returns a new alert string 歌曲振铃,设置这个状态模式,闹钟只会唱歌 46 | func (a *AlertSong) Alert() string { 47 | return "sun rise ,get up ,get up get up..." 48 | } 49 | -------------------------------------------------------------------------------- /behavior/10_state/state_test.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestRobotState(t *testing.T) { 9 | 10 | robot := NewSportRobot("home keeper") 11 | 12 | //主动驱动行为 13 | robot.Move() 14 | 15 | //跑 16 | robot.UpdateState(&RuningState{}) 17 | 18 | //跑一会 19 | time.Sleep(time.Millisecond * 234) 20 | 21 | //走 22 | robot.UpdateState(&WalkState{}) 23 | 24 | //走一会儿 25 | time.Sleep(time.Millisecond * 544) 26 | //继续跑 27 | robot.UpdateState(&RuningState{}) 28 | 29 | } 30 | func TestAlarmState(t *testing.T) { 31 | 32 | expect := "vibrating humming ... vibrating humming...vibrating humming..." + 33 | "vibrating humming ... vibrating humming...vibrating humming..." + 34 | "sun rise ,get up ,get up get up..." 35 | 36 | //创建一个手机闹铃 37 | mobile := NewAlert() 38 | 39 | ringsSounds := mobile.Alert() 40 | 41 | //叠加振铃声音,振铃响两遍 42 | ringsSounds += mobile.Alert() 43 | 44 | //设置振铃的铃声 45 | mobile.SetState(&AlertSong{}) 46 | 47 | ringsSounds += mobile.Alert() 48 | 49 | if ringsSounds != expect { 50 | t.Errorf("Expect result to equal %s, but %s.\n", expect, ringsSounds) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /behavior/11_command/README.md: -------------------------------------------------------------------------------- 1 | # 命令模式 2 | 3 | 命令模式也是一个响应模式,其主要作用是分离命令发出者和命令执行者,两者只要按照命令定义的约束交互,就可以各自变化。 4 | 5 | 现实生活中的例子也很多,比如,告知店员买XX奶茶,遥控器控制电视,就是命令模式,以及现在AI能力,大部分情况下,也是基于命令识别进行响应的,还有军训的时候,队列根据教官的各种口令进行变化,更多典型的命令模式。 6 | 7 | 命令模式中中的一个特点是每个`命令`都是一个类型(struct)。 8 | 9 | 10 | 命令模式的关键角色: 11 | 12 | *接收者(Receiver): 执行请求相关的操作Execute() 13 | *调用者(Invoker): 发出命令的一方 14 | *命令接口(Command) 15 | *具体命令的结构体(Command) 16 | 17 | 18 | -------------------------------------------------------------------------------- /behavior/11_command/command.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "fmt" 4 | 5 | //////////////////////////////// 6 | //这里使用军训的例子,使用队列向左转,向右转,向后转的口令 7 | //命令发出者是教官,命令执行者是队列 8 | 9 | //ICommand 命令接口,队列要进行响应 10 | type ICommand interface { 11 | Execute() 12 | } 13 | 14 | //Troop 队列 15 | type Troop struct{ name string } 16 | 17 | //Execute cmd 18 | func (t *Troop) Execute() { 19 | fmt.Println("cmd had been executed by", t.name) 20 | } 21 | 22 | //TurnLeftCommand 向左转 23 | type TurnLeftCommand struct { 24 | //可以携带参数,每个命令有一个接收者,去执行 25 | receiver ICommand 26 | } 27 | 28 | //Execute 执行向左转 29 | func (t *TurnLeftCommand) Execute() { 30 | fmt.Print("Troop Turn Left\n") 31 | t.receiver.Execute() 32 | } 33 | 34 | //TurnRightCommand 向右转 35 | type TurnRightCommand struct { 36 | //可以携带参数 37 | receiver ICommand 38 | } 39 | 40 | //Execute 执行向右转 41 | func (t *TurnRightCommand) Execute() { 42 | fmt.Print("Troop Turn Right\n") 43 | t.receiver.Execute() 44 | } 45 | 46 | //TurnBackCommand 向后转 47 | type TurnBackCommand struct { 48 | //可以携带参数 49 | holdTime int //保持时间 50 | receiver ICommand 51 | } 52 | 53 | //Execute 执行向后转 54 | func (t *TurnBackCommand) Execute() { 55 | fmt.Print("Troop Turn Back\n") 56 | t.receiver.Execute() 57 | } 58 | 59 | // Instructor 教官 60 | type Instructor struct { 61 | commands []ICommand 62 | } 63 | 64 | //AddCommand 教官喊口令一般都是一连串 65 | func (i *Instructor) AddCommand(c ICommand) { 66 | i.commands = append(i.commands, c) 67 | } 68 | 69 | //Execute 教官的Execute是发出命令 70 | func (i *Instructor) Execute() { 71 | 72 | for _, cmd := range i.commands { 73 | cmd.Execute() 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /behavior/11_command/command_draw.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type command interface { 8 | Execute() string 9 | } 10 | 11 | //PathPainter invoke an draw command 12 | type PathPainter struct { 13 | commands []command 14 | } 15 | 16 | //Execute run cmd 17 | func (p *PathPainter) Execute() string { 18 | var result string 19 | for _, command := range p.commands { 20 | result += command.Execute() + "\n" 21 | } 22 | return result 23 | } 24 | 25 | //Append new cmd PathPainter 26 | func (p *PathPainter) Append(command command) { 27 | p.commands = append(p.commands, command) 28 | } 29 | 30 | //Undo last step cmd 31 | func (p *PathPainter) Undo() { 32 | if len(p.commands) != 0 { 33 | p.commands = p.commands[:len(p.commands)-1] 34 | } 35 | } 36 | 37 | //Clear all 38 | func (p *PathPainter) Clear() { 39 | p.commands = []command{} 40 | } 41 | 42 | //Position pos 43 | type Position struct { 44 | X, Y int 45 | } 46 | 47 | //DrawCommand 命令执行者 48 | //DrawCommand line to 49 | type DrawCommand struct { 50 | Position *Position 51 | } 52 | 53 | //Execute cmd 54 | func (d *DrawCommand) Execute() string { 55 | return strconv.Itoa(d.Position.X) + "." + strconv.Itoa(d.Position.Y) 56 | } 57 | -------------------------------------------------------------------------------- /behavior/11_command/command_test.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import "testing" 4 | 5 | func TestTroopCommand(t *testing.T) { 6 | 7 | //教官 8 | in := Instructor{} 9 | 10 | //受训队伍1 11 | tr1 := &Troop{name: "girl team"} 12 | turnLeft := &TurnLeftCommand{receiver: tr1} 13 | 14 | //受训队伍2 15 | tr2 := &Troop{name: "boy team"} 16 | 17 | turnRight := &TurnLeftCommand{receiver: tr2} 18 | 19 | //准备命令发给不同的队伍 20 | in.AddCommand(turnRight) 21 | in.AddCommand(turnLeft) 22 | 23 | turnback := &TurnBackCommand{receiver: tr2} 24 | 25 | in.AddCommand(turnback) 26 | 27 | turnback = &TurnBackCommand{receiver: tr1} 28 | 29 | in.AddCommand(turnback) 30 | 31 | //开始发布指令 32 | in.Execute() 33 | 34 | } 35 | 36 | func TestDrawCommand(t *testing.T) { 37 | 38 | painter := PathPainter{} 39 | 40 | painter.Append(&DrawCommand{&Position{1, 1}}) 41 | painter.Append(&DrawCommand{&Position{2, 2}}) 42 | painter.Append(&DrawCommand{&Position{1, 3}}) 43 | 44 | expect := "1.1\n2.2\n1.3\n" 45 | if painter.Execute() != expect { 46 | t.Errorf("Expect result to equal %s, but %s.\n", expect, painter.Execute()) 47 | } 48 | 49 | painter.Undo() 50 | expect = "1.1\n2.2\n" 51 | if painter.Execute() != expect { 52 | t.Errorf("Expect result to equal %s, but %s.\n", expect, painter.Execute()) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /behavior/12_strategy/README.md: -------------------------------------------------------------------------------- 1 | # 策略模式 2 | 3 | 策略模式是定义一系列具有相同签名的接口方法,让这些方法在运行时可以互换. 4 | 5 | 通俗的讲就是同一个函数、动作、行为等接口,在不同类型的对象上有不同的实现,所以,以在运行替换,这也非常符合开闭原则. 6 | 7 | 常用的例子是计算加减乘除,这里用一个存款的例子,大陆居民用人民币存款,香港居民用港币. 8 | 9 | 向银行执行存款动作时候,给出的现金钱是不同的类型,大陆居民拿出人民币,香港居民拿出港币. 10 | -------------------------------------------------------------------------------- /behavior/12_strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import "fmt" 4 | 5 | //money kind 6 | const ( 7 | RMB = "RMB" 8 | HK = "HK" 9 | ) 10 | 11 | //StoreContext for Store 要包存钱的上下文信息 12 | type StoreContext struct { 13 | Kind, CardID string 14 | Money int 15 | } 16 | 17 | //IStore 要实现的存钱接口 18 | type IStore interface { 19 | Store(*StoreContext) 20 | } 21 | 22 | //MainLandCitizen 大陆居民 23 | type MainLandCitizen struct{ Name string } 24 | 25 | //Store Money to bank 26 | func (m *MainLandCitizen) Store(ctx *StoreContext) { 27 | fmt.Println("i am: ", m.Name, "i want to store: ", ctx.Money, ctx.Kind, "to: ", ctx.CardID) 28 | } 29 | 30 | //HongKongCitizen 香港居民 31 | type HongKongCitizen struct{ Name string } 32 | 33 | //Store Money to bank 34 | func (h *HongKongCitizen) Store(ctx *StoreContext) { 35 | fmt.Println("i am: ", h.Name, "i want to store: ", ctx.Money, ctx.Kind, "to: ", ctx.CardID) 36 | } 37 | 38 | //Bank handle moneyholder 39 | type Bank struct { 40 | moneyHolder IStore 41 | } 42 | 43 | //Recept a user 44 | func (b *Bank) Recept(moneyHolder IStore) { 45 | b.moneyHolder = moneyHolder 46 | fmt.Println("Bank: ", "Recept a New User") 47 | } 48 | 49 | //AccountUserMoney 动态替换的过程在这里,这里调用任何实现了Store的接口对象 50 | //AccountUserMoney to handle User's Money 51 | func (b *Bank) AccountUserMoney(ctx *StoreContext) { 52 | b.moneyHolder.Store(ctx) 53 | fmt.Println("Bank: ", "Processing Store", ctx.Money, ctx.Kind, "to: ", ctx.CardID) 54 | } 55 | -------------------------------------------------------------------------------- /behavior/12_strategy/strategy_test.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import "testing" 4 | 5 | func TestStoreMoney(t *testing.T) { 6 | 7 | bank := Bank{&MainLandCitizen{"Miss White"}} 8 | ctx := &StoreContext{ 9 | Kind: "RMB", 10 | CardID: "12345678921", 11 | Money: 10000, 12 | } 13 | bank.AccountUserMoney(ctx) 14 | 15 | hkUser := &HongKongCitizen{"Miss Black"} 16 | 17 | bank.Recept(hkUser) 18 | 19 | ctx = &StoreContext{ 20 | Kind: "HK", 21 | CardID: "987345678456", 22 | Money: 8723, 23 | } 24 | bank.AccountUserMoney(ctx) 25 | } 26 | -------------------------------------------------------------------------------- /behavior/README.md: -------------------------------------------------------------------------------- 1 | # 行为模式 2 | 3 | 对象之间行为关系的一些常用套路,行为类的模式,重点用于模式对象间行为的套路。 4 | 所谓行为模式,就是为这些行为明确一些框框/规则/方式等。 5 | 6 | 举个例子,好比古代人两个人见面,常常先作揖,同时问候. 7 | 8 | 作揖 ---> 问候 这就是一个行为模式. 9 | 10 | 再比如,我们常说家里来客人了,要端茶、倒水进行招待。 11 | 12 | 端茶(壶)--> (向客人杯子)倒水 也是一个行为模式. 13 | 14 | 从代码的微观角度,也会有很多常用的、很好用的、并且往往很高效的模式/套路,我们称之为最佳实践。 15 | -------------------------------------------------------------------------------- /creation/01_new/REDME.md: -------------------------------------------------------------------------------- 1 | # New pattern 2 | 3 | New 模式 4 | 5 | 最最最简单,最最最常用的模式。 6 | 7 | 以至于很多地方都不把它当成模式,都已经熟悉到了被忽略的地步,我觉得这是不公平的:),要恢复它地位。 8 | 9 | New模式是就是使用类似New()/new()/NewXXX()之类的方法,返回一个实现了某个接口的对象的引用。 10 | 11 | 须要注意的是的: 12 | 13 | + 一定是返回 结构对象的地址(引用) 14 | + 该结构对象往往返回一个实现了某接口的方法. 15 | -------------------------------------------------------------------------------- /creation/01_new/new_test.go: -------------------------------------------------------------------------------- 1 | package newpattern 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | type homecat interface { 8 | sleep() 9 | } 10 | 11 | type blackCat struct { 12 | name string 13 | } 14 | 15 | func newBlackCat(name string) homecat{ 16 | return &blackCat{name} 17 | } 18 | 19 | 20 | func (b blackCat) sleep() { 21 | fmt.Print( b.name + " is sleeping") 22 | } 23 | 24 | func TestNewMode(t *testing.T){ 25 | 26 | cat := newBlackCat("pi") 27 | cat.sleep() 28 | } -------------------------------------------------------------------------------- /creation/02_simple_factory/README.md: -------------------------------------------------------------------------------- 1 | # 简单工厂模式 2 | 3 | go 可以使用自定义的New()来初始化相关类。 4 | New 函数返回接口时就是简单工厂模式,也是golang一般推荐做法。 5 | 6 | simplefactory包中只有Mouth接口和New()函数可见。 7 | -------------------------------------------------------------------------------- /creation/02_simple_factory/simple.go: -------------------------------------------------------------------------------- 1 | package simplefactory 2 | 3 | import "fmt" 4 | 5 | type schoolmember int 6 | 7 | const ( 8 | student schoolmember = iota 9 | teacher 10 | ) 11 | 12 | //Mouth is interface that people can Say words 13 | type Mouth interface { 14 | Say(name string) string 15 | } 16 | 17 | //New return instance object that have `Mouth` and can speak 18 | func New(t schoolmember) Mouth { 19 | switch t { 20 | case student: 21 | return &studentType{} 22 | case teacher: 23 | return &teacherType{} 24 | } 25 | return nil 26 | } 27 | 28 | //teacherType is one of Mouth implement 29 | type teacherType struct{} 30 | 31 | //Say teacher's name 32 | func (*teacherType) Say(name string) string { 33 | return fmt.Sprintf("I am Teacher: %s", name) 34 | } 35 | 36 | //studentType is another Mouth implement 37 | type studentType struct{} 38 | 39 | //Say student's name 40 | func (*studentType) Say(name string) string { 41 | return fmt.Sprintf("I am Student: %s", name) 42 | } 43 | -------------------------------------------------------------------------------- /creation/02_simple_factory/simple_test.go: -------------------------------------------------------------------------------- 1 | package simplefactory 2 | 3 | import "testing" 4 | 5 | //TestTeacher test get New with factory 6 | func TestTeacher(t *testing.T) { 7 | te := New(teacher) 8 | name := te.Say("Tom") 9 | if name != "I am Teacher: Tom" { 10 | t.Fatal("Teacher test fail") 11 | } 12 | } 13 | 14 | func TestStudent(t *testing.T) { 15 | st := New(student) 16 | name := st.Say("Tom") 17 | if name != "I am student: Tom" { 18 | t.Fatal("student test fail") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /creation/03_builder/README.md: -------------------------------------------------------------------------------- 1 | # 创建者模式 2 | 3 | 重点分块创造,返回创建者接口 4 | 演示一个造车的例子,并稍微增加一点链式创建的能力 5 | -------------------------------------------------------------------------------- /creation/03_builder/builder.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "fmt" 4 | 5 | //ICar 汽车,我们要造车了 6 | //ICar 车具有以下能力 7 | type ICar interface { 8 | Speed() int 9 | Brand() string 10 | Brief() 11 | } 12 | 13 | //ICarBuilder 造一辆车需要具有的部件 14 | type ICarBuilder interface { 15 | Wheel(wheel int) ICarBuilder 16 | Engine(engine string) ICarBuilder 17 | Speed(max int) ICarBuilder 18 | Brand(brand string) ICarBuilder 19 | Build() ICar 20 | } 21 | 22 | //CarProto 车的原型 23 | type CarProto struct { 24 | Wheel int 25 | Engine string 26 | MaxSpeed int 27 | BrandName string 28 | } 29 | 30 | //Speed 最大车速 31 | func (c *CarProto) Speed() int { 32 | return c.MaxSpeed 33 | } 34 | 35 | //Brand 车品牌 36 | func (c *CarProto) Brand() string { 37 | return c.BrandName 38 | } 39 | 40 | //Brief 简介 41 | func (c *CarProto) Brief() { 42 | fmt.Println("this is a cool car") 43 | fmt.Println("car wheel size: ", c.Wheel) 44 | fmt.Println("car MaxSpeed: ", c.MaxSpeed) 45 | fmt.Println("car Engine: ", c.Engine) 46 | } 47 | 48 | //CarStudio 打算通过成立造车实验室进行造车 49 | type CarStudio struct { 50 | prototype CarProto 51 | } 52 | 53 | // NewCarStudio 造车工作室 54 | func NewCarStudio() ICarBuilder { 55 | return &CarStudio{} 56 | } 57 | 58 | // Wheel of car 59 | func (c *CarStudio) Wheel(wheel int) ICarBuilder { 60 | c.prototype.Wheel = wheel 61 | return c 62 | } 63 | 64 | // Engine of car 65 | func (c *CarStudio) Engine(engine string) ICarBuilder { 66 | c.prototype.Engine = engine 67 | return c 68 | } 69 | 70 | // Speed of car 71 | func (c *CarStudio) Speed(max int) ICarBuilder { 72 | c.prototype.MaxSpeed = max 73 | return c 74 | } 75 | 76 | // Brand of car 77 | func (c *CarStudio) Brand(brand string) ICarBuilder { 78 | c.prototype.BrandName = brand 79 | return c 80 | } 81 | 82 | // Build return a car 83 | func (c *CarStudio) Build() ICar { 84 | 85 | car := &CarProto{ 86 | Wheel: c.prototype.Wheel, 87 | Engine: c.prototype.Engine, 88 | MaxSpeed: c.prototype.MaxSpeed, 89 | BrandName: c.prototype.BrandName, 90 | } 91 | 92 | return car 93 | } 94 | -------------------------------------------------------------------------------- /creation/03_builder/builder_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestBuilderCar(t *testing.T) { 9 | builder := NewCarStudio() 10 | builder.Brand("sky").Speed(120).Engine("audi") 11 | car := builder.Build() 12 | if car.Speed() != 120 { 13 | t.Fatalf("Builder1 fail expect 120 ,but get %d", car.Speed()) 14 | } 15 | if car.Brand() != "sky" { 16 | t.Fatalf("Builder1 fail expect sky ,but get %s", car.Brand()) 17 | } 18 | 19 | fmt.Println(car.Speed()) 20 | fmt.Println(car.Brand()) 21 | } 22 | 23 | func TestBuilderCarMore(t *testing.T) { 24 | builder := NewCarStudio() 25 | builder.Brand("land").Speed(110).Engine("bmw") 26 | builder.Engine("man made").Brand("panda").Wheel(15) 27 | car := builder.Build() 28 | 29 | fmt.Println(car.Speed()) 30 | fmt.Println(car.Brand()) 31 | car.Brief() 32 | } 33 | -------------------------------------------------------------------------------- /creation/04_object_pool/README.md: -------------------------------------------------------------------------------- 1 | # Object Pool 2 | 3 | 对象池创建模式,根据业务和执行的需要,用于一次性准备大量的预置对象 4 | -------------------------------------------------------------------------------- /creation/04_object_pool/obejct_poo.go: -------------------------------------------------------------------------------- 1 | package objectpool 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type doctor struct { 9 | name string 10 | kind int //科室,1内科,2外科 11 | } 12 | 13 | type pool chan *doctor 14 | 15 | func newPool(total int) pool { 16 | p := make(pool, total) 17 | 18 | for i := 0; i < total; i++ { 19 | 20 | dc := new(doctor) 21 | dc.name = "doctor: " + strconv.Itoa(i) 22 | p <- dc 23 | } 24 | 25 | return p 26 | } 27 | 28 | //surgery 29 | func (d doctor) surgery(someone string) { 30 | fmt.Println("doctor:", d.name, "do surgery for", someone) 31 | } 32 | -------------------------------------------------------------------------------- /creation/04_object_pool/object_pool_test.go: -------------------------------------------------------------------------------- 1 | package objectpool 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestObjectPool(t *testing.T) { 9 | 10 | p := newPool(3) 11 | 12 | doc1 := <-p 13 | doc1.surgery("tom") 14 | 15 | doc2 := <-p 16 | doc2.surgery("rose") 17 | 18 | doc3 := <-p 19 | doc3.surgery("kate") 20 | 21 | select { 22 | case obj := <-p: 23 | obj.surgery("lily") 24 | p <- obj 25 | default: 26 | // No more objects left — retry later or fail 27 | fmt.Println("No more objects left, this moment") 28 | return 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /creation/05_factory_method/README.md: -------------------------------------------------------------------------------- 1 | # 工厂方法模式 2 | 3 | 工厂方法模式使用子类的方式延迟生成对象到子类中实现。 4 | 5 | 不同的工厂对象,返回一个实现了某接口的类型对象,不同的工厂返回不同实现了相同接口的不同对象,在只能使用特定的类简单工厂的基础上,使用的代码和类生成的灵活性大大增加。 6 | 7 | Go中的继承关系是使用匿名组合来实现的。 8 | -------------------------------------------------------------------------------- /creation/05_factory_method/factorymethod.go: -------------------------------------------------------------------------------- 1 | package factorymethod 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | //Assistant 是robot能做的事情 9 | type Assistant interface { 10 | Clean(int) 11 | Speak(string) 12 | Work() string 13 | } 14 | 15 | //IRobotFactory must be implemented by Factory 16 | //different Factory create different robot 17 | type IRobotFactory interface { 18 | Build() Assistant 19 | } 20 | 21 | //BasicRobotModel 是基本的机器人模型 22 | type BasicRobotModel struct { 23 | words string 24 | workTime int 25 | } 26 | 27 | //Clean 打扫 28 | func (b *BasicRobotModel) Clean(a int) { 29 | b.workTime = a 30 | fmt.Printf("i can clean :%d hours\n", a) 31 | } 32 | 33 | //Speak 说话 34 | func (b *BasicRobotModel) Speak(w string) { 35 | b.words = w 36 | fmt.Printf("my name is: %s\n", w) 37 | } 38 | 39 | //Work main work 40 | func (b *BasicRobotModel) Work() string { 41 | return fmt.Sprint("my main work is do somthing") 42 | } 43 | 44 | //FightingRobotFactory 生产各类军工机器人 45 | type FightingRobotFactory struct{} 46 | 47 | //Build a robot from FightingRobotFactory 48 | func (FightingRobotFactory) Build() Assistant { 49 | return &FightingRobot{ 50 | BasicRobotModel: &BasicRobotModel{}, 51 | } 52 | } 53 | 54 | //FightingRobot 实际的战斗机器人 55 | type FightingRobot struct { 56 | *BasicRobotModel 57 | } 58 | 59 | //Work for FightingRobot to do some fighting 60 | func (f FightingRobot) Work() string { 61 | fmt.Printf("%s\n", "i can fighting") 62 | return "i can fighting" + strconv.Itoa(f.workTime) 63 | } 64 | 65 | //HomeRobotFactory 生产各类家用机器人 66 | type HomeRobotFactory struct{} 67 | 68 | //Build a robot from HomeRobotFactory 69 | func (HomeRobotFactory) Build() Assistant { 70 | return &HomeRobot{ 71 | BasicRobotModel: &BasicRobotModel{}, 72 | } 73 | } 74 | 75 | //HomeRobot 实际的家用机器人 76 | type HomeRobot struct { 77 | *BasicRobotModel 78 | } 79 | 80 | //Work robot do some work 81 | func (h HomeRobot) Work() string { 82 | fmt.Printf("%s\n", "i can do homework") 83 | return "i can do homework" + strconv.Itoa(h.workTime) 84 | } 85 | -------------------------------------------------------------------------------- /creation/05_factory_method/factorymethod_test.go: -------------------------------------------------------------------------------- 1 | package factorymethod 2 | 3 | import "testing" 4 | 5 | func doWork(factory IRobotFactory, cleanhour int) string { 6 | robot := factory.Build() 7 | robot.Clean(cleanhour) 8 | 9 | robot.Speak("robot name") 10 | 11 | return robot.Work() 12 | 13 | } 14 | 15 | func TestRobotFactory(t *testing.T) { 16 | var factory IRobotFactory 17 | 18 | factory = FightingRobotFactory{} 19 | if doWork(factory, 2) != "i can fighting2" { 20 | t.Fatal("error with factory method pattern") 21 | } 22 | 23 | factory = HomeRobotFactory{} 24 | if doWork(factory, 1) != "i can do homework1" { 25 | t.Fatal("error with factory method pattern") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /creation/06_singleton/README.md: -------------------------------------------------------------------------------- 1 | # 单例模式 2 | 3 | 大名鼎鼎的单例模式,永远返回相同内存位置的绝对的、同一个实例对象。 4 | 5 | Go有两种常见的单例模式: 6 | 7 | + 使用懒惰模式的单例模式,使用`once.Do()`的双重同步检查保证线程安全生成单实例 8 | 9 | + 使用初始化的`init(){}`能力保证只生成一个实例 10 | -------------------------------------------------------------------------------- /creation/06_singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import "sync" 4 | 5 | //////////////////////////////// 6 | //way 1 7 | //使用 sync的 once.Do(){}确保执行一次 8 | //////////////////////////////// 9 | 10 | //Worker Singleton 是单例模式类 11 | type Worker struct{} 12 | 13 | //better to be pointer 14 | var onlyTheWorker *Worker 15 | 16 | // init a control 17 | var once sync.Once 18 | 19 | //GetWorkerInstance 总是获取到同一个Worker对象(内存位置相同) 20 | func GetWorkerInstance() *Worker { 21 | 22 | //be sure ,to do this,only once! 23 | once.Do(func() { 24 | onlyTheWorker = &Worker{} 25 | }) 26 | 27 | return onlyTheWorker 28 | } 29 | 30 | //Manager Singleton 是单例模式类 31 | type Manager struct{} 32 | 33 | //better to be pointer 34 | var instance *Manager 35 | 36 | //better to be pointer 37 | var onlyTheManager *Manager 38 | 39 | //////////////////////////////// 40 | //way2 41 | //使用func init(){}函数来初始化保证,只初始化一次,更简单. 42 | //////////////////////////////// 43 | 44 | func init() { 45 | onlyTheManager = &Manager{} 46 | } 47 | 48 | //GetManagerInstance 总是获取到同一个Manager对象(内存位置相同) 49 | func GetManagerInstance() *Manager { 50 | return onlyTheManager 51 | } 52 | -------------------------------------------------------------------------------- /creation/06_singleton/singleton_test.go: -------------------------------------------------------------------------------- 1 | package singleton 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | ) 7 | 8 | const workerCount = 500 9 | 10 | func TestWorkerSingleton(t *testing.T) { 11 | ins1 := GetWorkerInstance() 12 | ins2 := GetWorkerInstance() 13 | if ins1 != ins2 { 14 | t.Fatal("worker(instance) is not exactly the same") 15 | } 16 | } 17 | 18 | // 获取500次,Worker 是否总是同一个worker 19 | func TestParallelWorkerSingleton(t *testing.T) { 20 | wg := sync.WaitGroup{} 21 | wg.Add(workerCount) 22 | instances := [workerCount]*Worker{} 23 | for i := 0; i < workerCount; i++ { 24 | go func(index int) { 25 | instances[index] = GetWorkerInstance() 26 | wg.Done() 27 | }(i) 28 | } 29 | wg.Wait() 30 | for i := 1; i < workerCount; i++ { 31 | if instances[i] != instances[i-1] { 32 | t.Fatal("Worker instance is not equal") 33 | } 34 | } 35 | } 36 | 37 | func TestManagerSingleton(t *testing.T) { 38 | ins1 := GetManagerInstance() 39 | ins2 := GetManagerInstance() 40 | if ins1 != ins2 { 41 | t.Fatal("Manager(instance) is not exactly the same") 42 | } 43 | } 44 | 45 | // 获取500次,Manager 是否总是同一个Manager 46 | func TestParallelManagerSingleton(t *testing.T) { 47 | wg := sync.WaitGroup{} 48 | wg.Add(workerCount) 49 | instances := [workerCount]*Manager{} 50 | for i := 0; i < workerCount; i++ { 51 | go func(index int) { 52 | instances[index] = GetManagerInstance() 53 | wg.Done() 54 | }(i) 55 | } 56 | wg.Wait() 57 | for i := 1; i < workerCount; i++ { 58 | if instances[i] != instances[i-1] { 59 | t.Fatal("Manager instance is not exactly equal") 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /creation/07_prototype/README.md: -------------------------------------------------------------------------------- 1 | # 原型模式 2 | 3 | 原型模式用于快速复制具有相同属性的自身对象. 4 | 5 | 一般是通过实现实现: `Clone()`接口来实现,注意**必须返回新的内存实例**. 6 | -------------------------------------------------------------------------------- /creation/07_prototype/prototype.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | //Cloneable is the key interface 4 | type Cloneable interface { 5 | Clone() Cloneable 6 | } 7 | 8 | //cloneLab 克隆实验室,可以克隆很多动物 9 | type cloneLab struct { 10 | animals map[string]Cloneable 11 | } 12 | 13 | //new 返回一个 14 | func newCloneLab() *cloneLab { 15 | return &cloneLab{ 16 | animals: make(map[string]Cloneable), 17 | } 18 | } 19 | 20 | func (c *cloneLab) Get(name string) Cloneable { 21 | return c.animals[name] 22 | } 23 | 24 | func (c *cloneLab) Set(name string, newObject Cloneable) { 25 | c.animals[name] = newObject 26 | } 27 | -------------------------------------------------------------------------------- /creation/07_prototype/prototype_test.go: -------------------------------------------------------------------------------- 1 | package prototype 2 | 3 | import "testing" 4 | 5 | //cloneLab 克隆实验室 6 | var lab *cloneLab 7 | 8 | type sheep struct { 9 | name string 10 | weight int 11 | } 12 | 13 | func (s *sheep) Clone() Cloneable { 14 | tc := *s 15 | return &tc 16 | } 17 | 18 | type cow struct { 19 | name string 20 | gender bool 21 | } 22 | 23 | func (c *cow) Clone() Cloneable { 24 | newCow := &cow{ 25 | gender: c.gender, 26 | name: c.name, 27 | } 28 | return newCow 29 | } 30 | 31 | func TestClone(t *testing.T) { 32 | 33 | sheep1 := &sheep{ 34 | name: "sheep", 35 | weight: 10, 36 | } 37 | 38 | sheep2 := sheep1.Clone() 39 | 40 | if sheep1 == sheep2 { 41 | t.Fatal("error! get clone not working") 42 | } 43 | } 44 | 45 | func TestCloneFromLab(t *testing.T) { 46 | 47 | lab := newCloneLab() 48 | 49 | lab.Set("cow", &cow{name: "i am cow", gender: true}) 50 | 51 | c := lab.Get("cow").Clone() 52 | 53 | cw := c.(*cow) 54 | if cw.name != "i am cow" { 55 | t.Fatal("error") 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /creation/08_abstract_factory/README.md: -------------------------------------------------------------------------------- 1 | # 抽象工厂模式 2 | 3 | 抽象工厂模式用于生成具有多产品种类生产能力的的工厂,所生成的对象往往是有关联的。 4 | 5 | 如果抽象工厂退化成生成的对象无关联的或者单一的产品种类则成为工厂函数模式。 6 | 7 | 参考:[对比](https://blog.csdn.net/wyxhd2008/article/details/5597975) 8 | 9 | ![对比图片](../../images/abstract-factorys-method.png) 10 | 11 | 这里使用以下例子: 12 | 13 | 1、抽象机械制造工厂(IProduce)制造机器人和电池的例子,机器人可以干活,电池可以为机器人充电 14 | 15 | 2、抽象数据仓工厂(IRepository)生产订单和订单详情的例子,以分库分表的需求为基础进行演示 16 | 17 | 抽象工厂的好处在于可以自由替换工厂,用符合目标的需求的对象来做想要的事情 18 | -------------------------------------------------------------------------------- /creation/08_abstract_factory/abstractfactory_db.go: -------------------------------------------------------------------------------- 1 | package abstractfactory 2 | 3 | import "fmt" 4 | 5 | ///这里一个分库分表的场景 6 | ///主订单,放在常规MySQL数据库 7 | //订单详情,单条数据量过多,所以放在NewSQL数据库 8 | 9 | //Order : 订单主记录 10 | type Order interface { 11 | SaveOrder() 12 | //.....BALABALA..可以继续写很多接口方法 13 | //每个接口方法做一件事 14 | } 15 | 16 | //OrderDetail 订单详情 17 | type OrderDetail interface { 18 | SaveOrderDetail() 19 | //.....BALABALA..可以继续写很多接口方法 20 | //每个接口方法做一件事 21 | } 22 | 23 | //IRepository 是当抽象工厂模式例子中的关键接口 24 | //IRepository 返回一组数据处理对象,用于处理不同的数据类型 25 | //IRepository 本质是创建工作对象,但必须以接口方式返回 26 | type IRepository interface { 27 | CreateOrderWorker() Order 28 | CreateOrderDetailWorker() OrderDetail 29 | //.....BALABALA..可以继续写很多接口方法 30 | //每个接口方法都要返回一个接口 31 | } 32 | 33 | //////////////////////////////// 34 | //接口定义好了,开始进行实现和应用 35 | //////////////////////////////// 36 | 37 | //MySQLOrderWorker 处理主订单的worker 38 | type MySQLOrderWorker struct{} 39 | 40 | //SaveOrder 实现SaveOrder接口,保存订单 41 | func (*MySQLOrderWorker) SaveOrder() { 42 | fmt.Print("MySQL save main order\n") 43 | } 44 | 45 | //NewSQLOrderWorker 处理订单详情的worker 46 | type NewSQLOrderWorker struct{} 47 | 48 | // SaveOrderDetail SaveOrderDetail接口,保存订单细节 49 | func (*NewSQLOrderWorker) SaveOrderDetail() { 50 | fmt.Print("NewSQL save OrderDetail\n") 51 | } 52 | 53 | //SQLFactory 第一个工厂 54 | //SQLFactory SQL关系型数据库工厂实现DAOFactory接口 55 | type SQLFactory struct{} 56 | 57 | //CreateOrderWorker 创建实现了能够保存主订单记录的工作对象 58 | func (*SQLFactory) CreateOrderWorker() Order { 59 | return &MySQLOrderWorker{} 60 | } 61 | 62 | //CreateOrderDetailWorker 创建实现了能够保存订单详情的工作对象 63 | func (*SQLFactory) CreateOrderDetailWorker() OrderDetail { 64 | return &NewSQLOrderWorker{} 65 | } 66 | 67 | ///假设说:现在CTO说要迁移到非关系数据库,我们进行下一个实现 68 | ///主订单,放在常规MySQL数据库 69 | //订单详情,单条数据量过多,所以放在NewSQL数据库 70 | 71 | //MongoDBWorker 处理主订单 72 | //MongoDBWorker No-SQL非关系型数据库工厂实现DAOFactory接口 73 | type MongoDBWorker struct{} 74 | 75 | //SaveOrder by MongoDBWorker ... 76 | func (*MongoDBWorker) SaveOrder() { 77 | fmt.Print("MongoDB save main order\n") 78 | } 79 | 80 | //PouchDBWorker 处理订单细节 81 | type PouchDBWorker struct{} 82 | 83 | // SaveOrderDetail ... 84 | func (*PouchDBWorker) SaveOrderDetail() { 85 | fmt.Print("PouchDBWorker save OrderDetail\n") 86 | } 87 | 88 | //NoSQLFactory 第二个工厂,用于处理非关系型存储 89 | //NoSQLFactory No-SQL非关系型数据库工厂实现DAOFactory接口 90 | type NoSQLFactory struct{} 91 | 92 | //CreateOrderWorker 创建主订单工作对象 93 | func (*NoSQLFactory) CreateOrderWorker() Order { 94 | return &MongoDBWorker{} 95 | } 96 | 97 | //CreateOrderDetailWorker 创建订单详情工作对象 98 | func (*NoSQLFactory) CreateOrderDetailWorker() OrderDetail { 99 | return &PouchDBWorker{} 100 | } 101 | -------------------------------------------------------------------------------- /creation/08_abstract_factory/abstractfactory_robot.go: -------------------------------------------------------------------------------- 1 | package abstractfactory 2 | 3 | import "fmt" 4 | 5 | //IRobot : 机器人能做的事情 6 | type IRobot interface { 7 | Name() string 8 | DoWork() 9 | //.....BALABALA..可以继续写很多接口方法 10 | //每个接口方法做一件事 11 | } 12 | 13 | //IBattery 电池能做的事情 14 | type IBattery interface { 15 | Charge(robot IRobot) 16 | //.....BALABALA..可以继续写很多接口方法 17 | //每个接口方法做一件事 18 | } 19 | 20 | //IProduce 是当抽象工厂模式例子中的关键接口 21 | //IProduce 返回一组产品对象 22 | //IProduce 本质是创建工作对象,但必须以接口方式返回 23 | type IProduce interface { 24 | CreateRobot() IRobot 25 | CreateBattery() IBattery 26 | //.....BALABALA..可以继续写很多接口方法 27 | //每个接口方法都要返回一个接口 28 | } 29 | 30 | //////////////////////////////// 31 | //接口定义好了,开始进行实现和应用 32 | //////////////////////////////// 33 | 34 | //HomeRobot 家用机器人 35 | type HomeRobot struct{} 36 | 37 | //DoWork 机器人可以做工作 38 | func (*HomeRobot) DoWork() { 39 | fmt.Print("robot is cleaning home\n") 40 | } 41 | 42 | //Name 机器人的名字 43 | func (*HomeRobot) Name() string { 44 | return fmt.Sprint("home robot") 45 | } 46 | 47 | //HomeBattery 家用电池 48 | type HomeBattery struct{} 49 | 50 | // Charge SaveOrderDetail接口,保存订单细节 51 | func (*HomeBattery) Charge(robot IRobot) { 52 | 53 | rn := robot.Name() 54 | fmt.Print("HomeBattery is charging for:", rn) 55 | fmt.Println() 56 | } 57 | 58 | //HomeRobotFactory 家用机器人工厂 59 | type HomeRobotFactory struct{} 60 | 61 | //CreateRobot 创建机器人 62 | func (*HomeRobotFactory) CreateRobot() IRobot { 63 | return &HomeRobot{} 64 | } 65 | 66 | //CreateBattery 创建电池 67 | func (*HomeRobotFactory) CreateBattery() IBattery { 68 | return &HomeBattery{} 69 | } 70 | -------------------------------------------------------------------------------- /creation/08_abstract_factory/abstractfactory_test.go: -------------------------------------------------------------------------------- 1 | package abstractfactory 2 | 3 | import "testing" 4 | 5 | func TestRobotBatteryFactory(t *testing.T) { 6 | 7 | factory := &HomeRobotFactory{} 8 | robot := factory.CreateRobot() 9 | robot.DoWork() 10 | battery := factory.CreateBattery() 11 | battery.Charge(robot) 12 | } 13 | 14 | func TestSQLFactory(t *testing.T) { 15 | 16 | factory := &SQLFactory{} 17 | orderWorker := factory.CreateOrderWorker() 18 | orderWorker.SaveOrder() 19 | detailWorker := factory.CreateOrderDetailWorker() 20 | detailWorker.SaveOrderDetail() 21 | } 22 | 23 | func TestNoSqlFactory(t *testing.T) { 24 | 25 | factory := &NoSQLFactory{} 26 | orderWorker := factory.CreateOrderWorker() 27 | orderWorker.SaveOrder() 28 | detailWorker := factory.CreateOrderDetailWorker() 29 | detailWorker.SaveOrderDetail() 30 | } 31 | -------------------------------------------------------------------------------- /creation/README.md: -------------------------------------------------------------------------------- 1 | # creation pattern 2 | 3 | 创建模式就是用于创建对象的模式,什么是创建,通俗讲就是创造、制造、生产、打造? 家具厂造一件家具,叫创建,艺术大师创造一副画作也就创建. 4 | 5 | 所谓创建模式,就是创造、制造、生产等这些问题上有很多成熟的套路,举个例子,木匠可以手工打造一把凳子,家具工厂同样可以用自动化生产线批量生产凳子,这就是生产凳子的不同套路,也就模式. 6 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1", 3 | "enabledLanguageIds": [ 4 | "go", 5 | "markdown" 6 | ], 7 | "enabled": true, 8 | // words - list of words to be always considered correct 9 | "words": [ 10 | "bber", 11 | "cnter", 12 | "crazybber", 13 | "fanin", 14 | "gobreaker", 15 | "gomore", 16 | "Goroutines", 17 | "ifelse", 18 | "iostream", 19 | "istack", 20 | "karlseguin", 21 | "logrusorgru", 22 | "mongodb", 23 | "nums", 24 | "postgres", 25 | "retrier", 26 | "semp", 27 | "stackless", 28 | "Stateful", 29 | "stretchr", 30 | "struct", 31 | "Structs", 32 | "tscb", 33 | "xargs" 34 | ], 35 | // flagWords - list of words to be always considered incorrect 36 | // This is useful for offensive words and common spelling errors. 37 | // For example "hte" should be "the" 38 | "flagWords": [ 39 | "hte" 40 | ] 41 | } -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crazybber/go-fucking-patterns 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 7 | github.com/stretchr/testify v1.5.1 8 | go.uber.org/zap v1.15.0 9 | google.golang.org/grpc v1.29.1 10 | ) 11 | -------------------------------------------------------------------------------- /gomore/01_messages/README.md: -------------------------------------------------------------------------------- 1 | # 发布订阅模式 2 | 3 | 发布-订阅是一种消息传递模式,基本设计原则是将消息发布者和提供者分开,常常用于在不同组件之间传递消息。 4 | 5 | ![图示关系](../../images/pub-sub-pattern-0.png) 6 | 发布订阅模式往往实现为<消息代理,消息中间件,消息队列>等 7 | 8 | 该模式中的三个关键类型:消息本身、消息主题、订阅用户。 9 | ![图示关系](../../images/pub-sub-pattern-1.png) 10 | 11 | 图片来源:[pubsub-pattern.md](https://github.com/imsardine/dev-notes/blob/source/docs/pubsub-pattern.md) 12 | 13 | 现实生活中的各种信息平台,就是很好的发布订阅的例子,比如某八戒、某无忧等。 14 | 15 | 在发布订阅模型中,每条消息都会传送给多个订阅者。发布者通常不会知道、也不关心哪一个订阅者正在接收主题消息。订阅者和发布者可以在运行时动态添加是一种松散的耦合关系,这使得系统的复杂性可以随时间的推移而增长。在现实生活中,不同城市的象天气预报也是这个模式。 16 | 17 | 这里演示,一个拼车例子,车主发布拼车(Topic)消息,消息推送到订阅拼车(Topic)信息的所有用户. 18 | 19 | 并模拟以下情形: 20 | 21 | + 车主(Topic)发布拼车消息 22 | + 拼车用户订阅拼车消息(Topic) 23 | + 拼车用户处理收到的拼车消息 24 | -------------------------------------------------------------------------------- /gomore/01_messages/message.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | //Message for msg in Message bus 11 | type Message struct { 12 | Seq int 13 | Text string 14 | From Session //消息来源 15 | } 16 | 17 | //User for user 18 | type User struct { 19 | ID uint64 20 | Name string 21 | } 22 | 23 | //Session inherit user 24 | type Session struct { 25 | User 26 | Timestamp time.Time 27 | } 28 | 29 | //Subscription for user 30 | //Subscription is a session 31 | type Subscription struct { 32 | Session 33 | topicName string 34 | ctx context.Context 35 | cancel context.CancelFunc 36 | Ch chan<- Message //发送队列 37 | Inbox chan Message //接收消息的队列 38 | } 39 | 40 | func newSubscription(uid uint64, topicName string, UserQueueSize int) Subscription { 41 | 42 | ctx, cancel := context.WithCancel(context.Background()) 43 | 44 | return Subscription{ 45 | Session: Session{User{ID: uid}, time.Now()}, 46 | ctx: ctx, 47 | cancel: cancel, 48 | Ch: make(chan<- Message, UserQueueSize), //用于跟单个用户通信的消息队列,用户发送消息 49 | Inbox: make(chan Message, UserQueueSize), //用于跟单个用户通信的消息队列,用户接收消息 50 | } 51 | 52 | } 53 | 54 | //Cancel Message 55 | func (s *Subscription) Cancel() { 56 | s.cancel() 57 | } 58 | 59 | //Publish 这个表示用户订阅到感兴趣的主题的时候,同时也可以发送消息, 60 | //Publish 但是,示例中不演示这个用途 61 | //Publish 只有当channel无数据,且channel被close了,才会返回ok=false 62 | //Publish a message to subscription queue 63 | func (s *Subscription) Publish(msg Message) error { 64 | 65 | select { 66 | case <-s.ctx.Done(): 67 | return errors.New("Topic has been closed") 68 | default: 69 | s.Ch <- msg 70 | } 71 | return nil 72 | } 73 | 74 | //Receive message 75 | func (s *Subscription) Receive(out *Message) error { 76 | 77 | select { 78 | case <-s.ctx.Done(): 79 | return errors.New("Topic has been closed") 80 | case <-time.After(time.Millisecond * 100): 81 | return errors.New("time out error") 82 | case *out = <-s.Inbox: 83 | return nil 84 | } 85 | } 86 | 87 | //Topic that user is interested in 88 | //Topic should locate in MQ 89 | type Topic struct { 90 | UserQueueSize int 91 | Name string 92 | Subscribers map[uint64]Subscription //user list 93 | MessageHistory []Message //当前主题的消息历史,实际项目中可能需要限定大小并设置过期时间 94 | } 95 | 96 | //Publish 只有当channel无数据,且channel被close了,才会返回ok=false 97 | //Publish a message to subscription queue 98 | func (t *Topic) Publish(msg Message) error { 99 | 100 | //将消息发布给当前Topic的所有人 101 | for usersID, subscription := range t.Subscribers { 102 | if subscription.ID == usersID { 103 | subscription.Inbox <- msg 104 | } 105 | } 106 | //save message history 107 | t.MessageHistory = append(t.MessageHistory, msg) 108 | 109 | fmt.Println("current histroy message count: ", len(t.MessageHistory)) 110 | 111 | return nil 112 | } 113 | 114 | func (t *Topic) findUserSubscription(uid uint64, topicName string) (Subscription, bool) { 115 | // Get session or create one if it's the first 116 | 117 | if topicName != t.Name || t.Subscribers == nil || len(t.Subscribers) == 0 { 118 | return Subscription{}, false 119 | } 120 | if subscription, found := t.Subscribers[uid]; found { 121 | return subscription, true 122 | } 123 | return Subscription{}, false 124 | } 125 | 126 | //Subscribe a spec topic 127 | func (t *Topic) Subscribe(uid uint64, topicName string) (Subscription, bool) { 128 | 129 | if t.Name != topicName { 130 | return Subscription{}, false 131 | } 132 | 133 | // Get session or create one if it's the first 134 | if _, found := t.findUserSubscription(uid, topicName); !found { 135 | if t.Subscribers == nil { 136 | t.Subscribers = make(map[uint64]Subscription) 137 | } 138 | t.Subscribers[uid] = newSubscription(uid, topicName, t.UserQueueSize) 139 | } 140 | 141 | return t.Subscribers[uid], true 142 | } 143 | 144 | //Unsubscribe remove Subscription 145 | func (t *Topic) Unsubscribe(s Subscription) error { 146 | if _, found := t.findUserSubscription(s.ID, s.topicName); found { 147 | delete(t.Subscribers, s.ID) 148 | } 149 | return nil 150 | } 151 | 152 | //Delete topic 153 | func (t *Topic) Delete() error { 154 | t.Subscribers = nil 155 | t.Name = "" 156 | t.MessageHistory = nil 157 | return nil 158 | } 159 | -------------------------------------------------------------------------------- /gomore/01_messages/message_queue.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | //Queue hold all topics 4 | type Queue struct { 5 | Topics map[string]Topic //topic ID<-----> topic Object 6 | } 7 | 8 | //AddTopic to Queue 9 | func (q *Queue) AddTopic(topicName string, topicUserQueueSize int) Topic { 10 | if q.Topics == nil { 11 | q.Topics = make(map[string]Topic) 12 | } 13 | if _, found := q.Topics[topicName]; !found { 14 | q.Topics[topicName] = Topic{UserQueueSize: topicUserQueueSize, Name: topicName} 15 | } 16 | return q.Topics[topicName] 17 | } 18 | -------------------------------------------------------------------------------- /gomore/01_messages/message_test.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "sync" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | //////////////////////////////// 12 | //通常意义上是,连接消息队列之后就可以发送消息 13 | //当订阅著之后才会收到相关Topic消息的推送 14 | //当前,省略连接队列的步骤和操作代码 15 | //////////////////////////////// 16 | 17 | func TestMessageSubAndPubWithTopic(t *testing.T) { 18 | var wg sync.WaitGroup 19 | 20 | topicName := "seeking passengers" 21 | //假设消息队列已经收到数据,加下来由topic处理 22 | topic := Topic{ 23 | Name: topicName, 24 | UserQueueSize: 5, 25 | } 26 | 27 | ctx, cancel := context.WithCancel(context.Background()) 28 | 29 | wg.Add(1) 30 | 31 | //user 1 32 | //用户tom订阅拼车消息,订阅的是车主发布的拼车消息 33 | if subScriberTom, ok := topic.Subscribe(123, topicName); ok { 34 | 35 | go func() { 36 | defer wg.Done() 37 | EXIT: 38 | for { 39 | select { 40 | case <-ctx.Done(): 41 | fmt.Println("tom receive cancel, exit") 42 | break EXIT 43 | default: 44 | msg := Message{} 45 | err := subScriberTom.Receive(&msg) 46 | if err == nil { 47 | fmt.Println("tom receive subscribed msg:", msg) 48 | } 49 | } 50 | time.Sleep(200) 51 | } 52 | }() 53 | } 54 | 55 | wg.Add(1) 56 | 57 | //订阅成功了 58 | //发送一个消息 59 | 60 | //用户Lily订阅拼车消息,订阅的是车主发布的拼车消息 61 | if subSCriptionLily, ok := topic.Subscribe(456, topicName); ok { 62 | go func() { 63 | defer wg.Done() 64 | EXIT: 65 | for { 66 | select { 67 | case <-ctx.Done(): 68 | fmt.Println("lily receive cancel, exit") 69 | break EXIT 70 | default: 71 | msg := Message{} 72 | err := subSCriptionLily.Receive(&msg) 73 | if err == nil { 74 | fmt.Println("lily receive subscribed msg:", msg) 75 | } 76 | } 77 | time.Sleep(200) 78 | } 79 | }() 80 | } 81 | 82 | go func() { 83 | //模拟发送消息 84 | msg := Message{ 85 | Text: "i am looking for 1 passenger", 86 | From: Session{User{123, "lily"}, time.Now()}, 87 | } 88 | topic.Publish(msg) 89 | 90 | msg = Message{ 91 | Text: "i am looking for 2 passenger", 92 | From: Session{User{123, "lucy"}, time.Now()}, 93 | } 94 | 95 | topic.Publish(msg) 96 | 97 | msg = Message{ 98 | Text: "i am looking for passenger as many as i can", 99 | From: Session{User{123, "rose"}, time.Now()}, 100 | } 101 | 102 | topic.Publish(msg) 103 | time.Sleep(time.Second) 104 | cancel() 105 | 106 | }() 107 | 108 | wg.Wait() 109 | fmt.Println("all message done,exit it") 110 | 111 | } 112 | -------------------------------------------------------------------------------- /gomore/01_messages/message_weather.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | type ( 9 | subscriber chan interface{} // 订阅者为一个管道 10 | topicFunc func(v interface{}) bool // 主题为一个过滤器 11 | ) 12 | 13 | //Publisher 发布者对象 14 | type Publisher struct { 15 | m sync.RWMutex // 读写锁 16 | buffer int // 订阅队列的缓存大小 17 | timeout time.Duration // 发布超时时间 18 | subscribers map[subscriber]topicFunc // 订阅者信息 19 | } 20 | 21 | //NewPublisher 构建一个发布者对象, 可以设置发布超时时间和缓存队列的长度 22 | func NewPublisher(publishTimeout time.Duration, buffer int) *Publisher { 23 | return &Publisher{ 24 | buffer: buffer, 25 | timeout: publishTimeout, 26 | subscribers: make(map[subscriber]topicFunc), 27 | } 28 | } 29 | 30 | //Subscribe 添加一个新的订阅者,订阅全部主题 31 | func (p *Publisher) Subscribe() chan interface{} { 32 | return p.SubscribeTopic(nil) 33 | } 34 | 35 | //SubscribeTopic 添加一个新的订阅者,订阅过滤器筛选后的主题 36 | func (p *Publisher) SubscribeTopic(topic topicFunc) chan interface{} { 37 | ch := make(chan interface{}, p.buffer) 38 | p.m.Lock() 39 | p.subscribers[ch] = topic 40 | p.m.Unlock() 41 | return ch 42 | } 43 | 44 | //Evict 退出订阅 45 | func (p *Publisher) Evict(sub chan interface{}) { 46 | p.m.Lock() 47 | defer p.m.Unlock() 48 | delete(p.subscribers, sub) 49 | close(sub) 50 | } 51 | 52 | //Publish 发布一个主题 53 | func (p *Publisher) Publish(v interface{}) { 54 | p.m.RLock() 55 | defer p.m.RUnlock() 56 | var wg sync.WaitGroup 57 | for sub, topic := range p.subscribers { 58 | wg.Add(1) 59 | go p.sendTopic(sub, topic, v, &wg) 60 | } 61 | wg.Wait() 62 | } 63 | 64 | //Close 关闭发布者对象,同时关闭所有的订阅者管道。 65 | func (p *Publisher) Close() { 66 | p.m.Lock() 67 | defer p.m.Unlock() 68 | for sub := range p.subscribers { 69 | delete(p.subscribers, sub) 70 | close(sub) 71 | } 72 | } 73 | 74 | //sendTopic 发送主题,可以容忍一定的超时 75 | func (p *Publisher) sendTopic(sub subscriber, topic topicFunc, v interface{}, wg *sync.WaitGroup) { 76 | defer wg.Done() 77 | if topic != nil && !topic(v) { 78 | return 79 | } 80 | select { 81 | case sub <- v: 82 | case <-time.After(p.timeout): 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /gomore/01_messages/message_weather_test.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | /*不同城市的象天气预报可以应用这个模式。*/ 11 | //这个是一个第三方的例子 12 | func TestMessageSubPub(t *testing.T) { 13 | p := NewPublisher(100*time.Millisecond, 10) 14 | defer p.Close() 15 | 16 | //订阅全部 17 | all := p.SubscribeTopic(nil) 18 | 19 | //订阅包含天气的消息 20 | onlyweathers := p.SubscribeTopic(func(v interface{}) bool { 21 | if s, ok := v.(string); ok { 22 | return strings.Contains(s, "weather") 23 | } 24 | return false 25 | }) 26 | 27 | p.Publish("weather bad, SH") 28 | p.Publish("weather fine,SZ") 29 | 30 | go func() { 31 | for msg := range all { 32 | fmt.Println("all:", msg) 33 | } 34 | }() 35 | 36 | go func() { 37 | for msg := range onlyweathers { 38 | fmt.Println("Received:", msg) 39 | } 40 | }() 41 | // 运行一定时间后退出 42 | time.Sleep(3 * time.Second) 43 | } 44 | -------------------------------------------------------------------------------- /gomore/02_profiles/README.md: -------------------------------------------------------------------------------- 1 | # 时间差模式 2 | 3 | 主要用来计算,一段是代码的执行时差: 4 | 5 | `defer`后面跟的函数的参数,会被先被计算并被存储为函数本地变量 6 | 并且,当`defer`后的函数代码在整个代码块离开作用域被调用时,就会利用前面已经存储的值进行计算,这也算是一个技巧,示例如下: 7 | 8 | ```go 9 | func YourFunction(input SomeType) error { 10 | 11 | defer YourFunc(time.Now(), ....) //一定要放在函数的第一行或者你想要计算时差的代码的前面. 12 | 13 | //其他代码.. 14 | //其他你想计算允许时间的函数代码 15 | 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /gomore/02_profiles/time_profile_test.go: -------------------------------------------------------------------------------- 1 | package timeprofile 2 | 3 | import ( 4 | "log" 5 | "math/big" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestBigintProfile(t *testing.T) { 11 | 12 | bigint := big.NewInt(14468) 13 | 14 | BigIntFactorial(bigint) 15 | 16 | bigint = big.NewInt(24566) 17 | 18 | BigIntFactorial(bigint) 19 | 20 | } 21 | 22 | //Duration for time differ 23 | func Duration(invocation time.Time, name string) { 24 | elapsed := time.Since(invocation) 25 | 26 | log.Printf("%s lasted %s", name, elapsed) 27 | } 28 | 29 | //BigIntFactorial ... 30 | func BigIntFactorial(input *big.Int) *big.Int { 31 | 32 | //关键点是这一句. 33 | defer Duration(time.Now(), "IntFactorial") 34 | 35 | x := input 36 | y := big.NewInt(1) 37 | for one := big.NewInt(1); x.Sign() > 0; x.Sub(x, one) { 38 | y.Mul(y, x) 39 | } 40 | 41 | return x.Set(y) 42 | } 43 | -------------------------------------------------------------------------------- /gomore/03_context/README.md: -------------------------------------------------------------------------------- 1 | # 上下文模式 2 | 3 | 上下文模式,就是使用上下传递参数,或者传递操作。 4 | 5 | 其实在,观察者模式中,就有用到上下文模式传递参数:[观察模式中的上下文模式](../../behavior/03_observer) 6 | 7 | 这是演示,使用context取消http请求的场景。 8 | -------------------------------------------------------------------------------- /gomore/03_context/context_test.go: -------------------------------------------------------------------------------- 1 | package contexts 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestContext(t *testing.T) { 12 | go func() { 13 | cContext() 14 | }() 15 | 16 | time.Sleep(time.Second) 17 | 18 | reqest, _ := http.NewRequest("GET", "http://localhost:8099/hello", nil) // http client get 请求 19 | 20 | client := &http.Client{} 21 | ctx, cancel := context.WithCancel(context.Background()) 22 | reqest = reqest.WithContext(ctx) 23 | 24 | go func() { 25 | select { 26 | case <-time.After(2 * time.Second): 27 | cancel() 28 | } 29 | }() 30 | 31 | client.Do(reqest) 32 | 33 | } 34 | 35 | func hello(w http.ResponseWriter, req *http.Request) { 36 | 37 | ctx := req.Context() 38 | defer fmt.Println("server: hello handler ended") 39 | 40 | select { 41 | case <-time.After(10 * time.Second): 42 | fmt.Fprintf(w, "hello\n") 43 | case <-ctx.Done(): 44 | err := ctx.Err() 45 | fmt.Println("server:", err) 46 | internalError := http.StatusInternalServerError 47 | http.Error(w, err.Error(), internalError) 48 | } 49 | } 50 | 51 | func cContext() { 52 | http.HandleFunc("/hello", hello) 53 | http.ListenAndServe(":8099", nil) 54 | } 55 | -------------------------------------------------------------------------------- /gomore/04_fan_in/README.md: -------------------------------------------------------------------------------- 1 | # 扇入模式 2 | 3 | 或者叫做汇聚模式,漏入模式,是一种消息传递模式,用于在工作组件之间创建聚合流。 4 | 5 | 对于Go来说,其操作是:将多个channel的消息/数据合并到一个,达到聚合的目的。 6 | 7 | 这里演示一个数据流,求和的例子。 8 | 9 | 这里演示,一个最佳实践,将多个Goroutine的输入同时进行合并到单个channel中. 10 | -------------------------------------------------------------------------------- /gomore/04_fan_in/fan_in.go: -------------------------------------------------------------------------------- 1 | package fanin 2 | 3 | import "sync" 4 | 5 | // Merge operate a FanIn to compose different channels into one 6 | func Merge(cs ...<-chan int) <-chan int { 7 | var wg sync.WaitGroup 8 | 9 | out := make(chan int, 3) 10 | 11 | wg.Add(len(cs)) 12 | 13 | // Start an send goroutine for each input channel in cs. send 14 | // copies values from c to out until c is closed, then calls wg.Done. 15 | send := func(c <-chan int) { 16 | for n := range c { 17 | out <- n 18 | } 19 | wg.Done() 20 | } 21 | 22 | //启动多个 go routine 开始工作 23 | for _, c := range cs { 24 | go send(c) 25 | } 26 | // Start a goroutine to close out once all the send goroutines are 27 | // done. This must start after the wg.Add call. 28 | //关闭动作,放在发送一方,会更好 29 | go func() { 30 | wg.Wait() 31 | close(out) 32 | }() 33 | 34 | return out 35 | } 36 | 37 | func generateNumbersPipeline(numbers []int) <-chan int { 38 | out := make(chan int) 39 | go func() { 40 | for _, n := range numbers { 41 | out <- n 42 | } 43 | //发送完成之后关闭 44 | close(out) 45 | }() 46 | return out 47 | } 48 | 49 | func squareNumber(in <-chan int) <-chan int { 50 | out := make(chan int) 51 | go func() { 52 | for n := range in { 53 | out <- n * n 54 | } 55 | //发送完成之后关闭 56 | close(out) 57 | }() 58 | return out 59 | } 60 | -------------------------------------------------------------------------------- /gomore/04_fan_in/fan_in_out_test.go: -------------------------------------------------------------------------------- 1 | package fanin 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestFanInOutNumbersSeq(t *testing.T) { 9 | 10 | //一路输入源 11 | dataStreams := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 12 | // generate the common channel with inputs 13 | inputChan1 := generateNumbersPipeline(dataStreams) 14 | inputChan2 := generateNumbersPipeline(dataStreams) 15 | 16 | //this is a fanout operation 17 | // Fan-out to 2 Go-routine 18 | c1 := squareNumber(inputChan1) 19 | c2 := squareNumber(inputChan2) 20 | 21 | //fanIn data for the squared numbers 22 | out := Merge(c1, c2) 23 | 24 | sum := 0 25 | 26 | for c := range out { 27 | sum += c 28 | } 29 | 30 | fmt.Printf("Total Sum of Squares by FanIn : %d\n", sum) 31 | } 32 | -------------------------------------------------------------------------------- /gomore/04_fan_in/fan_in_test.go: -------------------------------------------------------------------------------- 1 | package fanin 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestFanInNumbersSeq(t *testing.T) { 9 | 10 | //第一路输入源 11 | dataStreams1 := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 12 | // generate the common channel with inputs 13 | inputChan1 := generateNumbersPipeline(dataStreams1) 14 | 15 | //第二路输入源 16 | dataStreams2 := []int{2, 4, 6, 9, 1, 1, 2, 3, 7, 8} 17 | 18 | inputChan2 := generateNumbersPipeline(dataStreams2) 19 | 20 | c1 := squareNumber(inputChan1) 21 | 22 | c2 := squareNumber(inputChan2) 23 | 24 | //fanIn data for the squared numbers 25 | out := Merge(c1, c2) 26 | 27 | sum := 0 28 | 29 | for c := range out { 30 | sum += c 31 | } 32 | 33 | fmt.Printf("Total Sum of Squares by FanIn : %d\n", sum) 34 | } 35 | -------------------------------------------------------------------------------- /gomore/05_fan_out/README.md: -------------------------------------------------------------------------------- 1 | # 扇出模式 2 | 3 | 或者叫做发散模式,用于将单个源的数据分发给多个对象,或者重新分发到多个输入源,形成数据广播。 4 | 5 | 这个模式比较适合1对多的数据copy比如音频流广播,一到多,视频流,一到多。 6 | 7 | 对于Go来说,操作往往是将一个channel的数据,重新分发到多个channel,数据广播的目的。 8 | 9 | 常见的Fan-Out有两种场景: 10 | 11 | + 随机分发单一输入源的数据,到不同的多个目的地。 12 | + 将输入源的数据复制多份,分发到不同的目的地。 13 | -------------------------------------------------------------------------------- /gomore/05_fan_out/fan_out.go: -------------------------------------------------------------------------------- 1 | package fanout 2 | 3 | import "sync" 4 | 5 | // Split2 多工作者重复分发,每个worker分发一波数据 6 | // Split2 a channel into n channels that receive messages in a round-robin fashion. 7 | func Split2(ch <-chan int, n int) []chan int { 8 | 9 | cs := []chan int{} 10 | for i := 0; i < n; i++ { 11 | cs = append(cs, make(chan int)) 12 | } 13 | 14 | var wg sync.WaitGroup 15 | //Distributes one value to channels 16 | distributeToChannels := func(ch <-chan int, cs []chan int) { 17 | // // Close every channel when the execution ends. 18 | 19 | //get a target from ch 20 | val, ok := <-ch 21 | if !ok { 22 | return // channel closed 23 | } 24 | //send value to all channels 25 | for _, c := range cs { 26 | c <- val 27 | } 28 | wg.Done() 29 | } 30 | 31 | for i := 0; i < n; i++ { 32 | wg.Add(1) 33 | // a worker to distribute message 34 | go distributeToChannels(ch, cs) 35 | } 36 | 37 | go func() { 38 | wg.Wait() 39 | for _, c := range cs { 40 | close(c) 41 | } 42 | }() 43 | return cs 44 | } 45 | 46 | // Split 重复分发数据为多份 47 | // Split a channel into n channels that receive messages in a round-robin fashion. 48 | func Split(ch <-chan int, n int) []chan int { 49 | 50 | //get a queue of chan 51 | //cs := make([]chan int, n) //创建了个chan 数组但是空的 52 | 53 | cs := []chan int{} 54 | for i := 0; i < n; i++ { 55 | cs = append(cs, make(chan int)) 56 | } 57 | 58 | //Distributes the work in a round robin fashion among the stated number of channels 59 | //until the main channel has been closed. In that case, close all channels and return. 60 | distributeToChannels := func(ch <-chan int, cs []chan int) { 61 | // Close every channel when the execution ends. 62 | defer func() { 63 | for _, c := range cs { 64 | close(c) 65 | } 66 | }() 67 | 68 | //this version will block 69 | for { 70 | //get a target from ch 71 | select { 72 | case val, ok := <-ch: 73 | if !ok { 74 | return // channel closed 75 | } 76 | //send value to all channels 77 | for _, c := range cs { 78 | c <- val 79 | } 80 | } 81 | } 82 | } 83 | 84 | // a worker to distribute message 85 | go distributeToChannels(ch, cs) 86 | 87 | return cs 88 | } 89 | 90 | //Split3 随机分发到不同的目的地 91 | //Split3 a channel into n channels that receive messages in a round-robin fashion. 92 | func Split3(ch <-chan int, n int) []chan int { 93 | 94 | cs := make([]chan int, 0) 95 | for i := 0; i < n; i++ { 96 | cs = append(cs, make(chan int)) 97 | } 98 | 99 | // Distributes the work in a round robin fashion among the stated number 100 | // of channels until the main channel has been closed. In that case, close 101 | // all channels and return. 102 | distributeToChannels := func(ch <-chan int, cs []chan int) { 103 | // Close every channel when the execution ends. 104 | defer func() { 105 | for _, c := range cs { 106 | close(c) 107 | } 108 | }() 109 | 110 | for { 111 | for _, c := range cs { 112 | select { 113 | case val, ok := <-ch: 114 | if !ok { 115 | return 116 | } 117 | c <- val 118 | } 119 | } 120 | } 121 | } 122 | 123 | go distributeToChannels(ch, cs) 124 | 125 | return cs 126 | } 127 | 128 | //The first stage, gen, is a function that converts a list of integers to a channel that emits the integers in the list. 129 | // The gen function starts a goroutine that sends the integers on the channel and closes the channel when all 130 | // the values have been sent: 131 | func gen(nums ...int) <-chan int { 132 | out := make(chan int) 133 | go func() { 134 | defer close(out) 135 | for _, n := range nums { 136 | out <- n 137 | } 138 | }() 139 | return out 140 | } 141 | 142 | //The second stage, sq, receives integers from a channel and returns a channel that emits the square of 143 | // each received integer. After the inbound channel is closed and this stage has sent all the values downstream, 144 | // it closes the outbound channel: 145 | func sq(in <-chan int) <-chan int { 146 | out := make(chan int) 147 | go func() { 148 | for n := range in { 149 | out <- n * n 150 | } 151 | close(out) 152 | }() 153 | return out 154 | } 155 | -------------------------------------------------------------------------------- /gomore/05_fan_out/fan_out_complex.go: -------------------------------------------------------------------------------- 1 | package fanout 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "sync/atomic" 7 | 8 | "go.uber.org/zap" //https://github.com/uber-go/zap 9 | //Blazing fast, structured, leveled logging in Go. 10 | ) 11 | 12 | var ( 13 | log, _ = zap.NewDevelopment() 14 | ) 15 | 16 | //Settings of pipeline 17 | const ( 18 | MaxWorkers = 16 19 | MaxQueueSize = 512 20 | MasterQueueSize = MaxQueueSize * MaxWorkers 21 | ) 22 | 23 | //IDispatcher Message 24 | type IDispatcher interface { 25 | Before(context.Context) error 26 | After() error 27 | Process(interface{}) error 28 | } 29 | 30 | //worker each work will dispatch message to several channels 31 | type worker struct { 32 | index uint32 33 | mutex *sync.Mutex 34 | running bool 35 | chain chan interface{} 36 | debug bool 37 | idle uint32 38 | dispatcher IDispatcher //hold a dispacher,需要自己实现一个dispatcher 工厂 39 | } 40 | 41 | //Pipeline of workers 42 | type Pipeline struct { 43 | workers map[int]*worker 44 | chain chan interface{} 45 | } 46 | 47 | //DispatcherBuilder create Dispatcher 48 | type DispatcherBuilder func() IDispatcher 49 | 50 | //Start run 51 | func (p *Pipeline) Start(ctx context.Context) { 52 | go func(pipe *Pipeline) { 53 | for { 54 | expectationWorkers := len(pipe.chain) % MaxWorkers 55 | if expectationWorkers >= MaxWorkers { 56 | expectationWorkers = 0 57 | } 58 | select { 59 | case <-ctx.Done(): 60 | return 61 | case val, ok := <-pipe.chain: 62 | if !ok { 63 | return 64 | } 65 | go pipe.workers[expectationWorkers].stream(val) 66 | } 67 | } 68 | }(p) 69 | } 70 | 71 | //Dispatch message to chains 72 | func (p *Pipeline) Dispatch(msg interface{}) { 73 | p.chain <- msg 74 | } 75 | 76 | //NewPipeline create a Workflow with a dispacher builder and some workers 77 | func NewPipeline(d DispatcherBuilder, idle uint32, debug bool) *Pipeline { 78 | 79 | ch := make(chan interface{}, MasterQueueSize) 80 | 81 | wk := make(map[int]*worker) 82 | for i := 0; i < MaxWorkers; i++ { 83 | wk[i] = &worker{ 84 | index: uint32(i + 1), 85 | chain: make(chan interface{}, MaxQueueSize), 86 | mutex: new(sync.Mutex), 87 | debug: debug, 88 | idle: idle, 89 | dispatcher: d(), //build real dispatcher 90 | } 91 | } 92 | return &Pipeline{workers: wk, chain: ch} 93 | } 94 | 95 | func (c *worker) stream(val interface{}) { 96 | c.chain <- val 97 | if !c.running { 98 | c.mutex.Lock() 99 | c.running = true 100 | ctx, cancel := context.WithCancel(context.Background()) 101 | defer func(w *worker, cancel context.CancelFunc) { 102 | if w.debug { 103 | log.Info("Worker leaving", zap.Any("index", w.index), zap.Any("idle", w.idle)) 104 | } 105 | 106 | if c.dispatcher != nil { 107 | err := c.dispatcher.After() 108 | if err != nil { 109 | log.Error("can not finish track issue", zap.Error(err)) 110 | } 111 | } 112 | 113 | cancel() 114 | w.mutex.Unlock() 115 | w.running = false 116 | }(c, cancel) 117 | 118 | if c.dispatcher != nil { 119 | err := c.dispatcher.Before(ctx) 120 | if err != nil { 121 | log.Error("can not start worker", zap.Error(err)) 122 | } 123 | } 124 | 125 | var idle uint32 = 0 126 | for { 127 | select { 128 | case msg := <-c.chain: 129 | atomic.StoreUint32(&idle, 0) 130 | if msg != nil && c.dispatcher != nil { 131 | err := c.dispatcher.Process(msg) 132 | if err != nil { 133 | log.Error("can not process message", zap.Any("msg", &msg), zap.Error(err)) 134 | } 135 | } 136 | default: 137 | atomic.AddUint32(&idle, 1) 138 | if i := atomic.LoadUint32(&idle); i > 0 { 139 | if i > c.idle { 140 | return 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /gomore/05_fan_out/fan_out_complex_test.go: -------------------------------------------------------------------------------- 1 | package fanout 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "google.golang.org/grpc" 10 | ) 11 | 12 | //taggingDispatcher implement our dispatcher interface 13 | type taggingDispatcher struct { 14 | Address string 15 | // stream proto.StreamClient 16 | conn *grpc.ClientConn 17 | } 18 | type messageContent struct { 19 | content string 20 | priority int 21 | } 22 | 23 | func TestComplexStreamingFanOut(t *testing.T) { 24 | 25 | builder := func() IDispatcher { 26 | return &taggingDispatcher{Address: "127.0.0.2"} 27 | } 28 | tagging := &Tagging{ 29 | topic: "new topic", 30 | pipeline: NewPipeline(builder, 2, true), 31 | } 32 | 33 | tagging.pipeline.Dispatch(messageContent{"all,please stay home", 1000}) 34 | 35 | tagging.pipeline.Start(context.Background()) 36 | 37 | //模拟处理过程,让工作者线程完成工作 38 | time.Sleep(time.Second * 2) 39 | t.Log("Done") 40 | } 41 | 42 | type Tagging struct { 43 | topic string 44 | pipeline *Pipeline 45 | } 46 | 47 | func (d *taggingDispatcher) Before(ctx context.Context) error { 48 | 49 | fmt.Println("i'm doing somthing before processing") 50 | 51 | conn, err := grpc.Dial(d.Address, grpc.WithInsecure()) 52 | if err != nil { 53 | return err 54 | } 55 | d.conn = conn 56 | // // // client := proto.NewClient(conn) 57 | // // stream, err := client.StreamMetric(ctx) 58 | // // if err != nil { 59 | // // return err 60 | // // } 61 | // // d.stream = stream 62 | 63 | return nil 64 | } 65 | 66 | func (d *taggingDispatcher) After() error { 67 | // _, err := d.stream.CloseAndRecv() 68 | // e := d.conn.Close() 69 | // if e != nil { 70 | // log.Error("close connection error", field.Error(e)) 71 | // } 72 | //return err 73 | fmt.Println("i'm doing somthing After processing") 74 | return nil 75 | } 76 | 77 | func (d *taggingDispatcher) Process(msg interface{}) error { 78 | 79 | content := msg.(messageContent) 80 | fmt.Println("i'm doing processing,with conten", content) 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /gomore/05_fan_out/fan_out_test.go: -------------------------------------------------------------------------------- 1 | package fanout 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | //多工作者,重复分发 10 | func TestFanOutDuplicateMultiWorkers(t *testing.T) { 11 | 12 | //一路输入源 13 | dataStreams := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 14 | //generator integer stream 15 | inputChan := gen(dataStreams...) 16 | 17 | // transfer to 18 | ch := sq(inputChan) 19 | 20 | // split it to 3 channel 21 | // 重复分发 22 | outArray := Split2(ch, 3) 23 | 24 | length := len(outArray) 25 | t.Log("length of out channel:", length) 26 | var wg sync.WaitGroup 27 | wg.Add(length) 28 | for i := 0; i < length; i++ { 29 | 30 | go func(in <-chan int, index int) { 31 | sum := 0 32 | for item := range in { 33 | sum += item 34 | } 35 | fmt.Println("worker:", index, sum) 36 | 37 | wg.Done() 38 | }(outArray[i], i) 39 | } 40 | wg.Wait() 41 | } 42 | 43 | //单个工作者,重复分发 44 | func TestFanOutDuplicate(t *testing.T) { 45 | 46 | //一路输入源 47 | dataStreams := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 48 | //generator integer stream 49 | inputChan := gen(dataStreams...) 50 | 51 | // transfer to 52 | ch := sq(inputChan) 53 | 54 | // split it to 3 channel 55 | // 重复分发 56 | outArray := Split(ch, 3) 57 | 58 | length := len(outArray) 59 | t.Log("length of out channel:", length) 60 | var wg sync.WaitGroup 61 | wg.Add(length) 62 | for i := 0; i < length; i++ { 63 | 64 | go func(in <-chan int, index int) { 65 | sum := 0 66 | for item := range in { 67 | sum += item 68 | } 69 | fmt.Println("worker:", index, sum) 70 | 71 | wg.Done() 72 | }(outArray[i], i) 73 | } 74 | wg.Wait() 75 | } 76 | 77 | //随机分发 78 | // worker: 2 11245 79 | // worker: 0 14988 80 | // worker: 1 10117 81 | func TestFanOutRandom(t *testing.T) { 82 | 83 | //一路输入源 84 | dataStreams := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 85 | //generator integer stream 86 | inputChan := gen(dataStreams...) 87 | 88 | // transfer to 89 | ch := sq(inputChan) 90 | 91 | // split it to 3 channel 92 | // 重复分发 93 | outArray := Split3(ch, 3) 94 | 95 | length := len(outArray) 96 | t.Log("length of out channel:", length) 97 | var wg sync.WaitGroup 98 | wg.Add(length) 99 | for i := 0; i < length; i++ { 100 | 101 | go func(in <-chan int, index int) { 102 | sum := 0 103 | for item := range in { 104 | sum += item 105 | } 106 | fmt.Println("worker:", index, sum) 107 | 108 | wg.Done() 109 | }(outArray[i], i) 110 | } 111 | wg.Wait() 112 | } 113 | 114 | func TestManualFanOutNumbersSeq(T *testing.T) { 115 | 116 | //一路输入源 117 | dataStreams := []int{13, 44, 56, 99, 9, 45, 67, 90, 78, 23} 118 | // generate the common channel with inputs 119 | inputChan1 := gen(dataStreams...) 120 | inputChan2 := gen(dataStreams...) 121 | 122 | //Manual Fan-out to 2 Go-routine 123 | c1 := sq(inputChan1) 124 | c2 := sq(inputChan2) 125 | 126 | fmt.Print("c1 queue: ") 127 | for n := range c1 { 128 | fmt.Print(n, " ") 129 | } 130 | fmt.Println() 131 | 132 | fmt.Print("c2 queue: ") 133 | for n := range c2 { 134 | fmt.Print(n, " ") 135 | } 136 | fmt.Println() 137 | 138 | } 139 | -------------------------------------------------------------------------------- /gomore/08_semaphore/README.md: -------------------------------------------------------------------------------- 1 | # 信号量模式 2 | 3 | 就是使用信号量进行同步的一种方式,Go里面有种同步方式,信号量是其中一种. 4 | -------------------------------------------------------------------------------- /gomore/08_semaphore/semaphore.go: -------------------------------------------------------------------------------- 1 | package semaphore 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | //error info 9 | var ( 10 | ErrNoTickets = errors.New("could not acquire semaphore") 11 | ErrIllegalRelease = errors.New("can't release the semaphore without acquiring it first") 12 | ) 13 | 14 | // ISemaphore contains the behavior of a semaphore that can be acquired and/or released. 15 | type ISemaphore interface { 16 | Acquire() error 17 | Release() error 18 | } 19 | 20 | type semp struct { 21 | sem chan struct{} 22 | timeout time.Duration 23 | } 24 | 25 | func (s *semp) Acquire() error { 26 | select { 27 | case s.sem <- struct{}{}: 28 | return nil 29 | case <-time.After(s.timeout): 30 | return ErrNoTickets 31 | } 32 | } 33 | 34 | func (s *semp) Release() error { 35 | select { 36 | case <-s.sem: 37 | return nil 38 | case <-time.After(s.timeout): 39 | return ErrIllegalRelease 40 | } 41 | 42 | } 43 | 44 | //New return a new Semaphore 45 | func New(tickets int, timeout time.Duration) ISemaphore { 46 | return &semp{ 47 | sem: make(chan struct{}, tickets), 48 | timeout: timeout, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /gomore/08_semaphore/semaphore/semaphore.go: -------------------------------------------------------------------------------- 1 | // Package semaphore implements the semaphore resiliency pattern for Go. 2 | package semaphore 3 | 4 | import ( 5 | "errors" 6 | "time" 7 | ) 8 | 9 | // ErrNoTickets is the error returned by Acquire when it could not acquire 10 | // a ticket from the semaphore within the configured timeout. 11 | var ErrNoTickets = errors.New("could not acquire semaphore ticket") 12 | 13 | // Semaphore implements the semaphore resiliency pattern 14 | type Semaphore struct { 15 | sem chan struct{} 16 | timeout time.Duration 17 | } 18 | 19 | // New constructs a new Semaphore with the given ticket-count 20 | // and timeout. 21 | func New(tickets int, timeout time.Duration) *Semaphore { 22 | return &Semaphore{ 23 | sem: make(chan struct{}, tickets), 24 | timeout: timeout, 25 | } 26 | } 27 | 28 | // Acquire tries to acquire a ticket from the semaphore. If it can, it returns nil. 29 | // If it cannot after "timeout" amount of time, it returns ErrNoTickets. It is 30 | // safe to call Acquire concurrently on a single Semaphore. 31 | func (s *Semaphore) Acquire() error { 32 | select { 33 | case s.sem <- struct{}{}: 34 | return nil 35 | case <-time.After(s.timeout): 36 | return ErrNoTickets 37 | } 38 | } 39 | 40 | // Release releases an acquired ticket back to the semaphore. It is safe to call 41 | // Release concurrently on a single Semaphore. It is an error to call Release on 42 | // a Semaphore from which you have not first acquired a ticket. 43 | func (s *Semaphore) Release() { 44 | <-s.sem 45 | } 46 | 47 | // IsEmpty will return true if no tickets are being held at that instant. 48 | // It is safe to call concurrently with Acquire and Release, though do note 49 | // that the result may then be unpredictable. 50 | func (s *Semaphore) IsEmpty() bool { 51 | return len(s.sem) == 0 52 | } 53 | -------------------------------------------------------------------------------- /gomore/08_semaphore/semaphore/semaphore_test.go: -------------------------------------------------------------------------------- 1 | package semaphore 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestSemaphoreAcquireRelease(t *testing.T) { 9 | sem := New(3, 1*time.Second) 10 | 11 | for i := 0; i < 10; i++ { 12 | if err := sem.Acquire(); err != nil { 13 | t.Error(err) 14 | } 15 | if err := sem.Acquire(); err != nil { 16 | t.Error(err) 17 | } 18 | if err := sem.Acquire(); err != nil { 19 | t.Error(err) 20 | } 21 | sem.Release() 22 | sem.Release() 23 | sem.Release() 24 | } 25 | } 26 | 27 | func TestSemaphoreBlockTimeout(t *testing.T) { 28 | sem := New(1, 200*time.Millisecond) 29 | 30 | if err := sem.Acquire(); err != nil { 31 | t.Error(err) 32 | } 33 | 34 | start := time.Now() 35 | if err := sem.Acquire(); err != ErrNoTickets { 36 | t.Error(err) 37 | } 38 | if start.Add(200 * time.Millisecond).After(time.Now()) { 39 | t.Error("semaphore did not wait long enough") 40 | } 41 | 42 | sem.Release() 43 | if err := sem.Acquire(); err != nil { 44 | t.Error(err) 45 | } 46 | } 47 | 48 | func TestSemaphoreEmpty(t *testing.T) { 49 | sem := New(2, 200*time.Millisecond) 50 | 51 | if !sem.IsEmpty() { 52 | t.Error("semaphore should be empty") 53 | } 54 | 55 | sem.Acquire() 56 | 57 | if sem.IsEmpty() { 58 | t.Error("semaphore should not be empty") 59 | } 60 | 61 | sem.Release() 62 | 63 | if !sem.IsEmpty() { 64 | t.Error("semaphore should be empty") 65 | } 66 | } 67 | 68 | func ExampleSemaphore() { 69 | sem := New(3, 1*time.Second) 70 | 71 | for i := 0; i < 10; i++ { 72 | go func() { 73 | if err := sem.Acquire(); err != nil { 74 | return //could not acquire semaphore 75 | } 76 | defer sem.Release() 77 | 78 | // do something semaphore-guarded 79 | }() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /gomore/09_parallelism/README.md: -------------------------------------------------------------------------------- 1 | # 并行模式 2 | 3 | 在资源没有限制的情况,如何完成大量任务 4 | -------------------------------------------------------------------------------- /gomore/09_parallelism/parallelism.go: -------------------------------------------------------------------------------- 1 | package parallelism 2 | 3 | import ( 4 | "crypto/md5" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path/filepath" 10 | "sort" 11 | "sync" 12 | ) 13 | 14 | // A result is the product of reading and summing a file using MD5. 15 | type result struct { 16 | path string 17 | sum [md5.Size]byte 18 | err error 19 | } 20 | 21 | // sumFiles starts goroutines to walk the directory tree at root and digest each 22 | // regular file. These goroutines send the results of the digests on the result 23 | // channel and send the result of the walk on the error channel. If done is 24 | // closed, sumFiles abandons its work. 25 | func sumFiles(done <-chan struct{}, root string) (<-chan result, <-chan error) { 26 | // For each regular file, start a goroutine that sums the file and sends 27 | // the result on c. Send the result of the walk on errc. 28 | c := make(chan result) 29 | errc := make(chan error, 1) 30 | go func() { // HL 31 | var wg sync.WaitGroup 32 | err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 33 | if err != nil { 34 | return err 35 | } 36 | if !info.Mode().IsRegular() { 37 | return nil 38 | } 39 | wg.Add(1) 40 | go func() { // HL 41 | data, err := ioutil.ReadFile(path) 42 | select { 43 | case c <- result{path, md5.Sum(data), err}: // HL 44 | case <-done: // HL 45 | } 46 | wg.Done() 47 | }() 48 | // Abort the walk if done is closed. 49 | select { 50 | case <-done: // HL 51 | return errors.New("walk canceled") 52 | default: 53 | return nil 54 | } 55 | }) 56 | // Walk has returned, so all calls to wg.Add are done. Start a 57 | // goroutine to close c once all the sends are done. 58 | go func() { // HL 59 | wg.Wait() 60 | close(c) // HL 61 | }() 62 | // No select needed here, since errc is buffered. 63 | errc <- err // HL 64 | }() 65 | return c, errc 66 | } 67 | 68 | // MD5All reads all the files in the file tree rooted at root and returns a map 69 | // from file path to the MD5 sum of the file's contents. If the directory walk 70 | // fails or any read operation fails, MD5All returns an error. In that case, 71 | // MD5All does not wait for inflight read operations to complete. 72 | func MD5All(root string) (map[string][md5.Size]byte, error) { 73 | // MD5All closes the done channel when it returns; it may do so before 74 | // receiving all the values from c and errc. 75 | done := make(chan struct{}) // HLdone 76 | defer close(done) // HLdone 77 | 78 | c, errc := sumFiles(done, root) // HLdone 79 | 80 | m := make(map[string][md5.Size]byte) 81 | for r := range c { // HLrange 82 | if r.err != nil { 83 | return nil, r.err 84 | } 85 | m[r.path] = r.sum 86 | } 87 | if err := <-errc; err != nil { 88 | return nil, err 89 | } 90 | return m, nil 91 | } 92 | 93 | func main() { 94 | // Calculate the MD5 sum of all files under the specified directory, 95 | // then print the results sorted by path name. 96 | m, err := MD5All(os.Args[1]) 97 | if err != nil { 98 | fmt.Println(err) 99 | return 100 | } 101 | var paths []string 102 | for path := range m { 103 | paths = append(paths, path) 104 | } 105 | sort.Strings(paths) 106 | for _, path := range paths { 107 | fmt.Printf("%x %s\n", m[path], path) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /gomore/09_parallelism/parallelism_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-11 10:44:52 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-11 10:44:52 7 | */ 8 | 9 | package parallelism 10 | 11 | import ( 12 | "fmt" 13 | "os" 14 | "sort" 15 | "testing" 16 | ) 17 | 18 | func TestParallelism(t *testing.T) { 19 | // Calculate the MD5 sum of all files under the specified directory, 20 | // then print the results sorted by path name. 21 | m, err := MD5All(os.Args[1]) 22 | if err != nil { 23 | fmt.Println(err) 24 | return 25 | } 26 | var paths []string 27 | for path := range m { 28 | paths = append(paths, path) 29 | } 30 | sort.Strings(paths) 31 | for _, path := range paths { 32 | fmt.Printf("%x %s\n", m[path], path) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gomore/10_generators/README.md: -------------------------------------------------------------------------------- 1 | # 生成器模式 2 | 3 | 解决批量生成值序列的问题 4 | -------------------------------------------------------------------------------- /gomore/11_n_barrier/README.md: -------------------------------------------------------------------------------- 1 | # 屏障模式 2 | 3 | 解决多个进程同步处理的常用模式 4 | -------------------------------------------------------------------------------- /gomore/12_bounded_parallelism/README.md: -------------------------------------------------------------------------------- 1 | # 有限并行模式 2 | 3 | 解决如何在有限资源的场景下,依然能够最大限度的并行处理的问题 4 | 5 | 6 | https://github.com/ajithcnambiar/boundedparallelism/blob/master/pipeline/pipeline.go 7 | 8 | https://github.com/keybase/pipeliner/blob/master/pipeliner.go 9 | 10 | 11 | ![](https://github.com/ajithcnambiar/boundedparallelism/raw/master/boundedParallelism.png) 12 | -------------------------------------------------------------------------------- /gomore/13_batcher/README.md: -------------------------------------------------------------------------------- 1 | batcher 2 | ======= 3 | 4 | [![Build Status](https://travis-ci.org/eapache/go-resiliency.svg?branch=master)](https://travis-ci.org/eapache/go-resiliency) 5 | [![GoDoc](https://godoc.org/github.com/eapache/go-resiliency/batcher?status.svg)](https://godoc.org/github.com/eapache/go-resiliency/batcher) 6 | [![Code of Conduct](https://img.shields.io/badge/code%20of%20conduct-active-blue.svg)](https://eapache.github.io/conduct.html) 7 | 8 | The batching resiliency pattern for golang. 9 | 10 | Creating a batcher takes two parameters: 11 | - the timeout to wait while collecting a batch 12 | - the function to run once a batch has been collected 13 | 14 | You can also optionally set a prefilter to fail queries before they enter the 15 | batch. 16 | 17 | ```go 18 | b := batcher.New(10*time.Millisecond, func(params []interface{}) error { 19 | // do something with the batch of parameters 20 | return nil 21 | }) 22 | 23 | b.Prefilter(func(param interface{}) error { 24 | // do some sort of sanity check on the parameter, and return an error if it fails 25 | return nil 26 | }) 27 | 28 | for i := 0; i < 10; i++ { 29 | go b.Run(i) 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /gomore/13_batcher/batcher.go: -------------------------------------------------------------------------------- 1 | // Package batcher implements the batching resiliency pattern for Go. 2 | package batcher 3 | 4 | import ( 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type work struct { 10 | param interface{} 11 | future chan error 12 | } 13 | 14 | // Batcher implements the batching resiliency pattern 15 | type Batcher struct { 16 | timeout time.Duration 17 | prefilter func(interface{}) error 18 | 19 | lock sync.Mutex 20 | submit chan *work 21 | doWork func([]interface{}) error 22 | } 23 | 24 | // New constructs a new batcher that will batch all calls to Run that occur within 25 | // `timeout` time before calling doWork just once for the entire batch. The doWork 26 | // function must be safe to run concurrently with itself as this may occur, especially 27 | // when the timeout is small. 28 | func New(timeout time.Duration, doWork func([]interface{}) error) *Batcher { 29 | return &Batcher{ 30 | timeout: timeout, 31 | doWork: doWork, 32 | } 33 | } 34 | 35 | // Run runs the work function with the given parameter, possibly 36 | // including it in a batch with other calls to Run that occur within the 37 | // specified timeout. It is safe to call Run concurrently on the same batcher. 38 | func (b *Batcher) Run(param interface{}) error { 39 | if b.prefilter != nil { 40 | if err := b.prefilter(param); err != nil { 41 | return err 42 | } 43 | } 44 | 45 | if b.timeout == 0 { 46 | return b.doWork([]interface{}{param}) 47 | } 48 | 49 | w := &work{ 50 | param: param, 51 | future: make(chan error, 1), 52 | } 53 | 54 | b.submitWork(w) 55 | 56 | return <-w.future 57 | } 58 | 59 | // Prefilter specifies an optional function that can be used to run initial checks on parameters 60 | // passed to Run before being added to the batch. If the prefilter returns a non-nil error, 61 | // that error is returned immediately from Run and the batcher is not invoked. A prefilter 62 | // cannot safely be specified for a batcher if Run has already been invoked. The filter function 63 | // specified must be concurrency-safe. 64 | func (b *Batcher) Prefilter(filter func(interface{}) error) { 65 | b.prefilter = filter 66 | } 67 | 68 | func (b *Batcher) submitWork(w *work) { 69 | b.lock.Lock() 70 | defer b.lock.Unlock() 71 | 72 | if b.submit == nil { 73 | b.submit = make(chan *work, 4) 74 | go b.batch() 75 | } 76 | 77 | b.submit <- w 78 | } 79 | 80 | func (b *Batcher) batch() { 81 | var params []interface{} 82 | var futures []chan error 83 | input := b.submit 84 | 85 | go b.timer() 86 | 87 | for work := range input { 88 | params = append(params, work.param) 89 | futures = append(futures, work.future) 90 | } 91 | 92 | ret := b.doWork(params) 93 | 94 | for _, future := range futures { 95 | future <- ret 96 | close(future) 97 | } 98 | } 99 | 100 | func (b *Batcher) timer() { 101 | time.Sleep(b.timeout) 102 | 103 | b.lock.Lock() 104 | defer b.lock.Unlock() 105 | 106 | close(b.submit) 107 | b.submit = nil 108 | } 109 | -------------------------------------------------------------------------------- /gomore/13_batcher/batcher_test.go: -------------------------------------------------------------------------------- 1 | package batcher 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "sync/atomic" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | var errSomeError = errors.New("errSomeError") 12 | 13 | func returnsError(params []interface{}) error { 14 | return errSomeError 15 | } 16 | 17 | func returnsSuccess(params []interface{}) error { 18 | return nil 19 | } 20 | 21 | func TestBatcherSuccess(t *testing.T) { 22 | b := New(10*time.Millisecond, returnsSuccess) 23 | 24 | wg := &sync.WaitGroup{} 25 | for i := 0; i < 10; i++ { 26 | wg.Add(1) 27 | go func() { 28 | if err := b.Run(nil); err != nil { 29 | t.Error(err) 30 | } 31 | wg.Done() 32 | }() 33 | } 34 | wg.Wait() 35 | 36 | b = New(0, returnsSuccess) 37 | for i := 0; i < 10; i++ { 38 | if err := b.Run(nil); err != nil { 39 | t.Error(err) 40 | } 41 | } 42 | } 43 | 44 | func TestBatcherError(t *testing.T) { 45 | b := New(10*time.Millisecond, returnsError) 46 | 47 | wg := &sync.WaitGroup{} 48 | for i := 0; i < 10; i++ { 49 | wg.Add(1) 50 | go func() { 51 | if err := b.Run(nil); err != errSomeError { 52 | t.Error(err) 53 | } 54 | wg.Done() 55 | }() 56 | } 57 | wg.Wait() 58 | } 59 | 60 | func TestBatcherPrefilter(t *testing.T) { 61 | b := New(1*time.Millisecond, returnsSuccess) 62 | 63 | b.Prefilter(func(param interface{}) error { 64 | if param == nil { 65 | return errSomeError 66 | } 67 | return nil 68 | }) 69 | 70 | if err := b.Run(nil); err != errSomeError { 71 | t.Error(err) 72 | } 73 | 74 | if err := b.Run(1); err != nil { 75 | t.Error(err) 76 | } 77 | } 78 | 79 | func TestBatcherMultipleBatches(t *testing.T) { 80 | var iters uint32 81 | 82 | b := New(10*time.Millisecond, func(params []interface{}) error { 83 | atomic.AddUint32(&iters, 1) 84 | return nil 85 | }) 86 | 87 | wg := &sync.WaitGroup{} 88 | 89 | for group := 0; group < 5; group++ { 90 | for i := 0; i < 10; i++ { 91 | wg.Add(1) 92 | go func() { 93 | if err := b.Run(nil); err != nil { 94 | t.Error(err) 95 | } 96 | wg.Done() 97 | }() 98 | } 99 | time.Sleep(15 * time.Millisecond) 100 | } 101 | 102 | wg.Wait() 103 | 104 | if iters != 5 { 105 | t.Error("Wrong number of iters:", iters) 106 | } 107 | } 108 | 109 | func ExampleBatcher() { 110 | b := New(10*time.Millisecond, func(params []interface{}) error { 111 | // do something with the batch of parameters 112 | return nil 113 | }) 114 | 115 | b.Prefilter(func(param interface{}) error { 116 | // do some sort of sanity check on the parameter, and return an error if it fails 117 | return nil 118 | }) 119 | 120 | for i := 0; i < 10; i++ { 121 | go b.Run(i) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gomore/README.md: -------------------------------------------------------------------------------- 1 | # go more 2 | 3 | 基于go的语言特性,在go的领域可以更容易的实现更多的高效并且有趣的模式. 4 | 5 | 同步并发处理中的一些常用模式 -------------------------------------------------------------------------------- /images/abstract-factorys-method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/abstract-factorys-method.png -------------------------------------------------------------------------------- /images/breaker-state-machine-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/breaker-state-machine-flow.png -------------------------------------------------------------------------------- /images/breaker-state-machine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/breaker-state-machine.png -------------------------------------------------------------------------------- /images/composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/composite.png -------------------------------------------------------------------------------- /images/pub-sub-pattern-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/pub-sub-pattern-0.png -------------------------------------------------------------------------------- /images/pub-sub-pattern-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crazybber/go-pattern-examples/e059a18ce39a8942a5f2930bdedb3d21441240a6/images/pub-sub-pattern-1.png -------------------------------------------------------------------------------- /index.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var contents = ` 4 | 5 | # Go语言设计模式示例集合(Go Patterns Examples) 6 | 7 | Go常用的、面向工程化和最佳实践的模式套路,包含常见的23种设计模式,重点是这里全部是例子、通俗易懂,每个模式,改一下名字就可以直接用在项目和工程中. 8 | 9 | ## 姿势 10 | 11 | + 所谓模式就是套路,如功夫,招有定式 12 | + 这里就是以实际代码示例展示设计模式,通俗易懂 13 | + 除了常见的23种普适的设计模式,Go也有一些属于自己的模式 14 | 15 | ## 创建型模式 16 | 17 | + [x] [简单工厂模式(Simple Factory)](./creation/00_simple_factory) 18 | + [x] [工厂方法模式(Factory Method)](./creation/04_factory_method) 19 | + [x] [抽象工厂模式(Abstract Factory)](./creation/05_abstract_factory) 20 | + [x] [创建者模式(Builder)](./creation/06_builder) 21 | + [x] [原型模式(Prototype)](./creation/07_prototype) 22 | + [x] [单例模式(Singleton)](./creation/03_singleton) 23 | + [ ] [对象池模式(Object Pool)](./creation/24_object_pool) 24 | + [x] [New模式(New)](./creation/25_new) 25 | 26 | ## 结构型模式 27 | 28 | + [x] [外观模式(Facade)](./structure/01_facade) 29 | + [x] [适配器模式(Adapter)](./structure/02_adapter) 30 | + [x] [代理模式(Proxy)](./structure/09_proxy) 31 | + [组合模式(Composite)](./structure/13_composite) 32 | + [享元模式(Flyweight)](./structure/18_flyweight) 33 | + [装饰模式(Decorator)](./structure/20_decorator) 34 | + [x] [桥模式(Bridge)](./structure/22_bridge) 35 | 36 | ## 行为型模式 37 | 38 | + [x] [中介者模式(Mediator)](./behavior/08_mediator) 39 | + [观察者模式(Observer)](./behavior/10_observer) 40 | + [命令模式(Command)](./behavior/11_command) 41 | + [迭代器模式(Iterator)](./behavior/12_iterator) 42 | + [模板方法模式(Template Method)](./behavior/14_template_method) 43 | + [x] [策略模式(Strategy)](./behavior/15_strategy) 44 | + [状态模式(State)](./behavior/behavior16_state) 45 | + [备忘录模式(Memento)](./behavior/17_memento) 46 | + [解释器模式(Interpreter)](./behavior/19_interpreter) 47 | + [职责链模式(Chain of Responsibility)](./behavior/21_chain_of_responsibility) 48 | + [访问者模式(Visitor)](./behavior/23_visitor) 49 | + [x] [闭包选项模式(Function Option)](./behavior/26_option) 50 | 51 | ## Go More 52 | 53 | + [x] [发布订阅模式(Pub-Sub)](./gomore/27_messages) 54 | + [x] [时差模式(Time Profile)](./gomore/28_profiles) 55 | + [x] [上下文模式(Context)](./gomore/29_context) 56 | 57 | ## 参考资料(Design patters Articles) 58 | 59 | [GO模式文档](https://github.com/nynicg/go-patterns) 60 | 61 | [菜鸟教程—设计模式](https://www.runoob.com/design-pattern/design-pattern-tutorial.html) 62 | 63 | [23-Pattern-in-Go](https://github.com/senghoo/golang-design-pattern) 64 | 65 | 66 | ## 更多 67 | 68 | 需要重新温习下Go基础?看这里 69 | 70 | [go-exercise](https://github.com/crazybber/go-exercise) 71 | 72 | ` 73 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/logrusorgru/aurora" //这是一个控制台可以多种颜色输出的颜色库 7 | ) 8 | 9 | func main() { 10 | 11 | startGo := letsGo() 12 | fmt.Sprintln(aurora.Green(startGo)) 13 | } 14 | 15 | func letsGo() string { 16 | fmt.Println("start go!") 17 | 18 | return fmt.Sprintln(contents) 19 | } 20 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/README.md: -------------------------------------------------------------------------------- 1 | # 熔断模式 2 | 3 | Circuit Breaker Pattern 也叫断路器模式,断路器设计模式是故障优先,一种处理失败的安全机制,防止更大的失败。 4 | 5 | 断路器类似于电路连接时防止着火的保险丝,当向电网输送了大量的电能时,会导致电线加热和燃烧,会导致供电临时中断。 6 | 7 | 熔断用于保护流量过大,是一种保护行为。 8 | 9 | ![熔断器状态机](../../images/breaker-state-machine.png) 10 | 11 | 状态变化流: 12 | 13 | ![状态变化流](../../images/breaker-state-machine-flow.png) 14 | 15 | 一些关键角色: 16 | 17 | ## Operation Counter 操作计数器 18 | 19 | 是一个简单的计数器,记录的成功和失败的状态。 20 | 21 | ## Circuit Breaker 断路器 22 | 23 | 电路连续故障,并且超过指定的阈值,它将返回一个快速错误,一段时间后,才会重试请求。 24 | 25 | ## Context 26 | 27 | 上下文用于传递参数. 28 | 29 | 参考: 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/breaker/breaker.go: -------------------------------------------------------------------------------- 1 | package breaker 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | // ErrBreakerOpen is the error returned from Run() when the function is not executed 11 | // because the breaker is currently open. 12 | var ErrBreakerOpen = errors.New("circuit breaker is open") 13 | 14 | const ( 15 | closed uint32 = iota 16 | open 17 | halfOpen 18 | ) 19 | 20 | // Breaker implements the circuit-breaker resiliency pattern 21 | type Breaker struct { 22 | errorThreshold, successThreshold int 23 | timeout time.Duration 24 | lock sync.Mutex 25 | state uint32 26 | errors, successes int 27 | lastError time.Time 28 | } 29 | 30 | // New constructs a new circuit-breaker that starts closed. 31 | // From closed, the breaker opens if "errorThreshold" errors are seen 32 | // without an error-free period of at least "timeout". From open, the 33 | // breaker half-closes after "timeout". From half-open, the breaker closes 34 | // after "successThreshold" consecutive successes, or opens on a single error. 35 | func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker { 36 | return &Breaker{ 37 | errorThreshold: errorThreshold, 38 | successThreshold: successThreshold, 39 | timeout: timeout, 40 | } 41 | } 42 | 43 | // Run will either return ErrBreakerOpen immediately if the circuit-breaker is 44 | // already open, or it will run the given function and pass along its return 45 | // value. It is safe to call Run concurrently on the same Breaker. 46 | func (b *Breaker) Run(work func() error) error { 47 | state := atomic.LoadUint32(&b.state) 48 | 49 | if state == open { 50 | return ErrBreakerOpen 51 | } 52 | 53 | return b.doWork(state, work) 54 | } 55 | 56 | // Go will either return ErrBreakerOpen immediately if the circuit-breaker is 57 | // already open, or it will run the given function in a separate goroutine. 58 | // If the function is run, Go will return nil immediately, and will *not* return 59 | // the return value of the function. It is safe to call Go concurrently on the 60 | // same Breaker. 61 | func (b *Breaker) Go(work func() error) error { 62 | state := atomic.LoadUint32(&b.state) 63 | 64 | if state == open { 65 | return ErrBreakerOpen 66 | } 67 | 68 | // errcheck complains about ignoring the error return value, but 69 | // that's on purpose; if you want an error from a goroutine you have to 70 | // get it over a channel or something 71 | go b.doWork(state, work) 72 | 73 | return nil 74 | } 75 | 76 | func (b *Breaker) doWork(state uint32, work func() error) error { 77 | var panicValue interface{} 78 | 79 | result := func() error { 80 | defer func() { 81 | panicValue = recover() 82 | }() 83 | return work() 84 | }() 85 | 86 | if result == nil && panicValue == nil && state == closed { 87 | // short-circuit the normal, success path without contending 88 | // on the lock 89 | return nil 90 | } 91 | 92 | // oh well, I guess we have to contend on the lock 93 | b.processResult(result, panicValue) 94 | 95 | if panicValue != nil { 96 | // as close as Go lets us come to a "rethrow" although unfortunately 97 | // we lose the original panicing location 98 | panic(panicValue) 99 | } 100 | 101 | return result 102 | } 103 | 104 | func (b *Breaker) processResult(result error, panicValue interface{}) { 105 | b.lock.Lock() 106 | defer b.lock.Unlock() 107 | 108 | if result == nil && panicValue == nil { 109 | if b.state == halfOpen { 110 | b.successes++ 111 | if b.successes == b.successThreshold { 112 | b.closeBreaker() 113 | } 114 | } 115 | } else { 116 | if b.errors > 0 { 117 | expiry := b.lastError.Add(b.timeout) 118 | if time.Now().After(expiry) { 119 | b.errors = 0 120 | } 121 | } 122 | 123 | switch b.state { 124 | case closed: 125 | b.errors++ 126 | if b.errors == b.errorThreshold { 127 | b.openBreaker() 128 | } else { 129 | b.lastError = time.Now() 130 | } 131 | case halfOpen: 132 | b.openBreaker() 133 | } 134 | } 135 | } 136 | 137 | func (b *Breaker) openBreaker() { 138 | b.changeState(open) 139 | go b.timer() 140 | } 141 | 142 | func (b *Breaker) closeBreaker() { 143 | b.changeState(closed) 144 | } 145 | 146 | func (b *Breaker) timer() { 147 | time.Sleep(b.timeout) 148 | 149 | b.lock.Lock() 150 | defer b.lock.Unlock() 151 | 152 | b.changeState(halfOpen) 153 | } 154 | 155 | func (b *Breaker) changeState(newState uint32) { 156 | b.errors = 0 157 | b.successes = 0 158 | atomic.StoreUint32(&b.state, newState) 159 | } 160 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/breaker/breaker_test.go: -------------------------------------------------------------------------------- 1 | package breaker 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | var errSomeError = errors.New("errSomeError") 10 | 11 | func alwaysPanics() error { 12 | panic("foo") 13 | } 14 | 15 | func returnsError() error { 16 | return errSomeError 17 | } 18 | 19 | func returnsSuccess() error { 20 | return nil 21 | } 22 | 23 | func TestExampleBreaker(t *testing.T) { 24 | breaker := New(3, 1, 5*time.Second) 25 | 26 | for { 27 | result := breaker.Run(func() error { 28 | // communicate with some external service and 29 | // return an error if the communication failed 30 | return nil 31 | }) 32 | 33 | switch result { 34 | case nil: 35 | // success! 36 | case ErrBreakerOpen: 37 | // our function wasn't run because the breaker was open 38 | default: 39 | // some other error 40 | } 41 | } 42 | } 43 | 44 | func TestBreakerErrorExpiry(t *testing.T) { 45 | breaker := New(2, 1, 1*time.Second) 46 | 47 | for i := 0; i < 3; i++ { 48 | if err := breaker.Run(returnsError); err != errSomeError { 49 | t.Error(err) 50 | } 51 | time.Sleep(1 * time.Second) 52 | } 53 | 54 | for i := 0; i < 3; i++ { 55 | if err := breaker.Go(returnsError); err != nil { 56 | t.Error(err) 57 | } 58 | time.Sleep(1 * time.Second) 59 | } 60 | } 61 | 62 | func TestBreakerPanicsCountAsErrors(t *testing.T) { 63 | breaker := New(3, 2, 1*time.Second) 64 | 65 | // three errors opens the breaker 66 | for i := 0; i < 3; i++ { 67 | func() { 68 | defer func() { 69 | val := recover() 70 | if val.(string) != "foo" { 71 | t.Error("incorrect panic") 72 | } 73 | }() 74 | if err := breaker.Run(alwaysPanics); err != nil { 75 | t.Error(err) 76 | } 77 | t.Error("shouldn't get here") 78 | }() 79 | } 80 | 81 | // breaker is open 82 | for i := 0; i < 5; i++ { 83 | if err := breaker.Run(returnsError); err != ErrBreakerOpen { 84 | t.Error(err) 85 | } 86 | } 87 | } 88 | 89 | func TestBreakerStateTransitions(t *testing.T) { 90 | breaker := New(3, 2, 1*time.Second) 91 | 92 | // three errors opens the breaker 93 | for i := 0; i < 3; i++ { 94 | if err := breaker.Run(returnsError); err != errSomeError { 95 | t.Error(err) 96 | } 97 | } 98 | 99 | // breaker is open 100 | for i := 0; i < 5; i++ { 101 | if err := breaker.Run(returnsError); err != ErrBreakerOpen { 102 | t.Error(err) 103 | } 104 | } 105 | 106 | // wait for it to half-close 107 | time.Sleep(2 * time.Second) 108 | // one success works, but is not enough to fully close 109 | if err := breaker.Run(returnsSuccess); err != nil { 110 | t.Error(err) 111 | } 112 | // error works, but re-opens immediately 113 | if err := breaker.Run(returnsError); err != errSomeError { 114 | t.Error(err) 115 | } 116 | // breaker is open 117 | if err := breaker.Run(returnsError); err != ErrBreakerOpen { 118 | t.Error(err) 119 | } 120 | 121 | // wait for it to half-close 122 | time.Sleep(2 * time.Second) 123 | // two successes is enough to close it for good 124 | for i := 0; i < 2; i++ { 125 | if err := breaker.Run(returnsSuccess); err != nil { 126 | t.Error(err) 127 | } 128 | } 129 | // error works 130 | if err := breaker.Run(returnsError); err != errSomeError { 131 | t.Error(err) 132 | } 133 | // breaker is still closed 134 | if err := breaker.Run(returnsSuccess); err != nil { 135 | t.Error(err) 136 | } 137 | } 138 | 139 | func TestBreakerAsyncStateTransitions(t *testing.T) { 140 | breaker := New(3, 2, 1*time.Second) 141 | 142 | // three errors opens the breaker 143 | for i := 0; i < 3; i++ { 144 | if err := breaker.Go(returnsError); err != nil { 145 | t.Error(err) 146 | } 147 | } 148 | 149 | // just enough to yield the scheduler and let the goroutines work off 150 | time.Sleep(1 * time.Millisecond) 151 | 152 | // breaker is open 153 | for i := 0; i < 5; i++ { 154 | if err := breaker.Go(returnsError); err != ErrBreakerOpen { 155 | t.Error(err) 156 | } 157 | } 158 | 159 | // wait for it to half-close 160 | time.Sleep(2 * time.Second) 161 | // one success works, but is not enough to fully close 162 | if err := breaker.Go(returnsSuccess); err != nil { 163 | t.Error(err) 164 | } 165 | // error works, but re-opens immediately 166 | if err := breaker.Go(returnsError); err != nil { 167 | t.Error(err) 168 | } 169 | // just enough to yield the scheduler and let the goroutines work off 170 | time.Sleep(1 * time.Millisecond) 171 | // breaker is open 172 | if err := breaker.Go(returnsError); err != ErrBreakerOpen { 173 | t.Error(err) 174 | } 175 | 176 | // wait for it to half-close 177 | time.Sleep(2 * time.Second) 178 | // two successes is enough to close it for good 179 | for i := 0; i < 2; i++ { 180 | if err := breaker.Go(returnsSuccess); err != nil { 181 | t.Error(err) 182 | } 183 | } 184 | // just enough to yield the scheduler and let the goroutines work off 185 | time.Sleep(1 * time.Millisecond) 186 | // error works 187 | if err := breaker.Go(returnsError); err != nil { 188 | t.Error(err) 189 | } 190 | // just enough to yield the scheduler and let the goroutines work off 191 | time.Sleep(1 * time.Millisecond) 192 | // breaker is still closed 193 | if err := breaker.Go(returnsSuccess); err != nil { 194 | t.Error(err) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/breaker_options.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-06-02 23:57:40 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-06-03 23:50:17 7 | */ 8 | 9 | package circuit 10 | 11 | import ( 12 | "context" 13 | "time" 14 | ) 15 | 16 | //BreakConditionWatcher check state 17 | type BreakConditionWatcher func(state State, cnter counters) bool 18 | 19 | //StateChangedEventHandler set event handle 20 | type StateChangedEventHandler func(name string, from State, to State) 21 | 22 | //Option set Options 23 | type Option func(opts *Options) 24 | 25 | //Options for breaker 26 | type Options struct { 27 | Name string 28 | Expiry time.Time 29 | Interval, Timeout time.Duration 30 | MaxRequests uint32 31 | CanOpen BreakConditionWatcher //是否应该断开电路(打开电路开关) 32 | CanClose BreakConditionWatcher //if we should close switch 33 | OnStateChanged StateChangedEventHandler 34 | ShoulderHalfToOpen uint32 35 | Ctx context.Context 36 | } 37 | 38 | //ActionName of breaker 39 | func ActionName(name string) Option { 40 | return func(opts *Options) { 41 | opts.Name = name 42 | } 43 | } 44 | 45 | //Interval of breaker 46 | func Interval(interval time.Duration) Option { 47 | return func(opts *Options) { 48 | opts.Interval = interval 49 | } 50 | } 51 | 52 | //Timeout of breaker 53 | func Timeout(timeout time.Duration) Option { 54 | return func(opts *Options) { 55 | opts.Timeout = timeout 56 | } 57 | } 58 | 59 | // MaxRequests is the maximum number of requests allowed to pass through 60 | // when the CircuitBreaker is half-open. 61 | // If MaxRequests is 0, the CircuitBreaker allows only 1 request. 62 | 63 | //MaxRequests of breaker 64 | func MaxRequests(maxRequests uint32) Option { 65 | return func(opts *Options) { 66 | opts.MaxRequests = maxRequests 67 | } 68 | } 69 | 70 | //WithShoulderHalfToOpen of breaker 71 | func WithShoulderHalfToOpen(shoulderHalfToOpen uint32) Option { 72 | return func(opts *Options) { 73 | opts.ShoulderHalfToOpen = shoulderHalfToOpen 74 | } 75 | } 76 | 77 | //Expiry of breaker 78 | func Expiry(expiry time.Time) Option { 79 | return func(opts *Options) { 80 | opts.Expiry = expiry 81 | } 82 | } 83 | 84 | //WithStateChanged set handle of ChangedHandle 85 | func WithStateChanged(handler StateChangedEventHandler) Option { 86 | return func(opts *Options) { 87 | opts.OnStateChanged = handler 88 | } 89 | } 90 | 91 | //WithBreakCondition check traffic state ,to see if request can go 92 | func WithBreakCondition(whenCondition BreakConditionWatcher) Option { 93 | return func(opts *Options) { 94 | opts.CanOpen = whenCondition 95 | } 96 | } 97 | 98 | //WithCloseCondition check traffic state ,to see if request can go 99 | func WithCloseCondition(whenCondition BreakConditionWatcher) Option { 100 | return func(opts *Options) { 101 | opts.CanClose = whenCondition 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/circuit_breaker_adv.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-10 22:00:58 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-06-03 23:54:29 7 | */ 8 | 9 | package circuit 10 | 11 | import ( 12 | "context" 13 | "errors" 14 | "fmt" 15 | "sync" 16 | "time" 17 | ) 18 | 19 | //////////////////////////////// 20 | ///使用HTTP请求的例子 21 | //每个搜索引擎时时刻刻都会遇到超大规模的请求的流量. 22 | //这里演示一个复杂一点的例子,同时使用Option 模式 23 | 24 | //ErrServiceUnavailable for error 25 | var ( 26 | ErrTooManyRequests = errors.New("too many requests") 27 | ErrServiceUnavailable = errors.New("service unavailable") 28 | FailureThreshold = 10 //最大失败次数--->失败阈值 29 | ) 30 | 31 | // 默认的超时时间 32 | const ( 33 | defaultTimeout = time.Second * 30 34 | defaultSuccessThreshold = 2 35 | ) 36 | 37 | //////////////////////////////// 38 | //way 2 对象式断路器 39 | // 高级模式 40 | // 支持多工作者模式 41 | //////////////////////////////// 42 | 43 | //RequestBreaker for protection 44 | type RequestBreaker struct { 45 | options Options 46 | mutex sync.Mutex 47 | state State 48 | cnter counters 49 | preState State 50 | } 51 | 52 | //NewRequestBreaker return a breaker 53 | func NewRequestBreaker(opts ...Option) *RequestBreaker { 54 | 55 | defaultOptions := Options{ 56 | Name: "defaultBreakerName", 57 | Expiry: time.Now().Add(time.Second * 20), 58 | Interval: time.Second * 10, // interval to check closed status,default 10 seconds 59 | Timeout: time.Second * 60, //timeout to check open, default 60 seconds 60 | MaxRequests: 5, 61 | CanOpen: func(current State, cnter counters) bool { return cnter.ConsecutiveFailures > 2 }, 62 | CanClose: func(current State, cnter counters) bool { return cnter.ConsecutiveSuccesses > 2 }, 63 | OnStateChanged: func(name string, fromPre State, toCurrent State) {}, 64 | } 65 | 66 | for _, setOption := range opts { 67 | setOption(&defaultOptions) 68 | 69 | } 70 | 71 | return &RequestBreaker{ 72 | options: defaultOptions, 73 | cnter: counters{}, 74 | state: StateUnknown, 75 | preState: StateUnknown, 76 | } 77 | } 78 | 79 | func (rb *RequestBreaker) changeStateTo(state State) { 80 | rb.preState = rb.state 81 | rb.state = state 82 | rb.cnter.Reset() 83 | } 84 | 85 | func (rb *RequestBreaker) beforeRequest() error { 86 | 87 | rb.mutex.Lock() 88 | defer rb.mutex.Unlock() 89 | fmt.Println("before do request:", rb.cnter.Total()) 90 | 91 | switch rb.state { 92 | case StateOpen: 93 | //如果是断开状态,并且超时了,转到半开状态,记录 94 | if rb.options.Expiry.Before(time.Now()) { 95 | rb.changeStateTo(StateHalfOpen) 96 | rb.options.Expiry = time.Now().Add(rb.options.Timeout) 97 | return nil 98 | } 99 | return ErrTooManyRequests 100 | case StateClosed: 101 | if rb.options.Expiry.Before(time.Now()) { 102 | rb.cnter.Reset() 103 | rb.options.Expiry = time.Now().Add(rb.options.Interval) 104 | } 105 | } 106 | 107 | return nil 108 | 109 | } 110 | 111 | // Do the given requested work if the RequestBreaker accepts it. 112 | // Do returns an error instantly if the RequestBreaker rejects the request. 113 | // Otherwise, Execute returns the result of the request. 114 | // If a panic occurs in the request, the RequestBreaker handles it as an error and causes the same panic again. 115 | func (rb *RequestBreaker) Do(work func(ctx context.Context) (interface{}, error)) (interface{}, error) { 116 | 117 | //before 118 | 119 | if err := rb.beforeRequest(); err != nil { 120 | return nil, err 121 | } 122 | 123 | //do work 124 | //do work from requested user 125 | result, err := work(rb.options.Ctx) 126 | 127 | //after work 128 | rb.afterRequest(err) 129 | 130 | return result, err 131 | } 132 | 133 | func (rb *RequestBreaker) afterRequest(resultErr error) { 134 | 135 | rb.mutex.Lock() 136 | defer rb.mutex.Unlock() 137 | //after 138 | fmt.Println("after do request:", rb.cnter.Total()) 139 | 140 | if resultErr != nil { 141 | //失败了,handle 失败 142 | rb.cnter.Count(FailureState, rb.preState == rb.state) 143 | switch rb.state { 144 | case StateHalfOpen, StateClosed: 145 | if rb.options.CanOpen(rb.state, rb.cnter) { 146 | rb.changeStateTo(StateOpen) //打开开关 147 | rb.options.OnStateChanged(rb.options.Name, rb.state, StateOpen) //关闭到打开 148 | } 149 | } 150 | } else { 151 | //success ! 152 | rb.cnter.Count(SuccessState, rb.preState == rb.state) 153 | 154 | switch rb.state { 155 | case StateHalfOpen: 156 | if rb.preState == StateOpen { 157 | // rb.changeStateTo(StateHalfOpen) //already handled in beforeRequest,Only fire StateChange Event 158 | rb.options.OnStateChanged(rb.options.Name, StateOpen, StateHalfOpen) //打开到半开 159 | } 160 | if rb.cnter.ConsecutiveSuccesses >= rb.options.ShoulderHalfToOpen { 161 | rb.changeStateTo(StateClosed) 162 | rb.options.Expiry = time.Now().Add(rb.options.Interval) 163 | rb.options.OnStateChanged(rb.options.Name, StateHalfOpen, StateClosed) //半开到关闭 164 | } 165 | 166 | } 167 | 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/circuit_breaker_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-11 10:55:28 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-22 17:21:06 7 | */ 8 | 9 | package circuit 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | "io/ioutil" 15 | "log" 16 | "net/http" 17 | "testing" 18 | ) 19 | 20 | var breaker *RequestBreaker 21 | 22 | var onStateChangeEvent = func(name string, from, to State) { 23 | fmt.Println("name:", name, "from:", from, "to", to) 24 | } 25 | 26 | var canOpenSwitch = func(current State, cnter counters) bool { 27 | 28 | if current == StateHalfOpen { 29 | return cnter.ConsecutiveFailures > 2 30 | } 31 | 32 | //失败率,可以由用户自己定义 33 | failureRatio := float64(cnter.TotalFailures) / float64(cnter.Requests) 34 | return cnter.Requests >= 3 && failureRatio >= 0.6 35 | } 36 | 37 | var canCloseSwitch = func(current State, cnter counters) bool { 38 | //失败率,可以由用户自己定义 39 | if cnter.ConsecutiveSuccesses > 2 { 40 | return true 41 | } 42 | // 43 | successRatio := float64(cnter.TotalFailures) / float64(cnter.Requests) 44 | return cnter.Requests >= 3 && successRatio >= 0.6 45 | } 46 | 47 | func TestObjectBreaker(t *testing.T) { 48 | 49 | jobToDo := func(ctx context.Context) (interface{}, error) { 50 | resp, err := http.Get("https://bing.com/robots.txt") 51 | if err != nil { 52 | return nil, err 53 | } 54 | defer resp.Body.Close() 55 | body, err := ioutil.ReadAll(resp.Body) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return body, nil 60 | } 61 | 62 | breaker = NewRequestBreaker(ActionName("HTTP GET"), 63 | WithBreakCondition(canOpenSwitch), 64 | WithCloseCondition(canCloseSwitch), 65 | WithShoulderHalfToOpen(2), 66 | WithStateChanged(onStateChangeEvent)) 67 | 68 | body, err := breaker.Do(jobToDo) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | 73 | fmt.Println(string(body.([]byte))) 74 | 75 | log.Print("\nresult:", body.([]byte)) 76 | } 77 | 78 | func TestFunctionalBreaker(t *testing.T) { 79 | 80 | //something need to do 81 | jobToDo := func(ctx context.Context) error { 82 | resp, err := http.Get("https://bing.com/robots.txt") 83 | if err != nil { 84 | return err 85 | } 86 | defer resp.Body.Close() 87 | body, err := ioutil.ReadAll(resp.Body) 88 | if err != nil { 89 | return err 90 | } 91 | fmt.Println(string(body)) 92 | return nil 93 | } 94 | 95 | //wrapper and control job with a breaker 96 | circuitWork := Breaker(jobToDo, 2 /* failureThreshold */) 97 | 98 | params := context.TODO() 99 | 100 | // do the job as usually 101 | res := circuitWork(params) 102 | 103 | log.Print("\nresult:", res) 104 | 105 | } 106 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/circuit_counter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-22 12:41:54 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-22 17:22:35 7 | */ 8 | 9 | package circuit 10 | 11 | import "time" 12 | 13 | //////////////////////////////// 14 | /// 计数器 用以维护断路器内部的状态 15 | /// 无论是对象式断路器还是函数式断路器 16 | /// 都要用到计数器 17 | //////////////////////////////// 18 | 19 | //State 断路器本身的状态的 20 | //State of switch int 21 | type State int 22 | 23 | // state for breaker 24 | const ( 25 | StateClosed State = iota //默认的闭合状态,可以正常执行业务 26 | StateHalfOpen 27 | StateOpen 28 | StateUnknown 29 | ) 30 | 31 | //OperationState of current 某一次操作的结果状态 32 | type OperationState int 33 | 34 | //ICounter interface 35 | type ICounter interface { 36 | Count(OperationState, bool) 37 | LastActivity() time.Time 38 | Reset() 39 | Total() uint32 40 | } 41 | 42 | type counters struct { 43 | Requests uint32 //连续的请求次数 44 | lastActivity time.Time 45 | TotalFailures uint32 46 | TotalSuccesses uint32 47 | ConsecutiveSuccesses uint32 48 | ConsecutiveFailures uint32 49 | } 50 | 51 | func (c *counters) Total() uint32 { 52 | return c.Requests 53 | } 54 | 55 | func (c *counters) LastActivity() time.Time { 56 | return c.lastActivity 57 | } 58 | 59 | func (c *counters) Reset() { 60 | ct := &counters{} 61 | ct.lastActivity = c.lastActivity 62 | c = ct 63 | } 64 | 65 | //Count the failure and success 66 | func (c *counters) Count(statue OperationState, isConsecutive bool) { 67 | 68 | switch statue { 69 | case FailureState: 70 | c.TotalFailures++ 71 | if isConsecutive || c.ConsecutiveFailures == 0 { 72 | c.ConsecutiveFailures++ 73 | } 74 | case SuccessState: 75 | c.TotalSuccesses++ 76 | if isConsecutive || c.ConsecutiveSuccesses == 0 { 77 | c.ConsecutiveSuccesses++ 78 | } 79 | } 80 | c.Requests++ 81 | c.lastActivity = time.Now() //更新活动时间 82 | // c.lastOpResult = statue 83 | //handle status change 84 | 85 | } 86 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/circuit_func_closure_basic.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-22 12:42:34 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-22 17:21:40 7 | */ 8 | 9 | package circuit 10 | 11 | import ( 12 | "context" 13 | "time" 14 | ) 15 | 16 | //////////////////////////////// 17 | //way 1 简单的函数式断路器 18 | // 一个func实例作为一个Breaker 允许多个worker(即goroutine)同时访问 19 | // 当前简单场景下,只考虑单个worker情况下的连续请求 20 | // 当前的设计思路,非常简单: 21 | // 1、不考虑三种状态之间的转换,只靠两种状态,电路打开与电路关闭 22 | // 2、当累计失败到达一定失败次数就端开请求(打开电路),并延迟一定的时间再允许请求 23 | //////////////////////////////// 24 | 25 | //states of CircuitBreaker 26 | //states: closed --->open ---> half open --> closed 27 | const ( 28 | UnknownState OperationState = iota 29 | FailureState 30 | SuccessState 31 | ) 32 | 33 | type simpleCounter struct { 34 | lastOpResult OperationState 35 | lastActivity time.Time 36 | ConsecutiveSuccesses uint32 37 | ConsecutiveFailures uint32 38 | } 39 | 40 | func (c *simpleCounter) LastActivity() time.Time { 41 | return c.lastActivity 42 | } 43 | 44 | func (c *simpleCounter) Reset() { 45 | ct := &simpleCounter{} 46 | ct.lastActivity = c.lastActivity 47 | ct.lastOpResult = UnknownState 48 | c = ct 49 | } 50 | 51 | //Count the failure and success 52 | func (c *simpleCounter) Count(lastState OperationState) { 53 | 54 | switch lastState { 55 | case FailureState: 56 | c.ConsecutiveFailures++ 57 | case SuccessState: 58 | c.ConsecutiveSuccesses++ 59 | } 60 | c.lastActivity = time.Now() //更新活动时间 61 | c.lastOpResult = lastState 62 | //handle status change 63 | } 64 | 65 | //Circuit of action stream,this is actually to do something. 66 | //Circuit hold the really action 67 | type Circuit func(context.Context) error 68 | 69 | //失败达到阈值后,过两秒重试 70 | var canRetry = func(cnt simpleCounter, failureThreshold uint32) bool { 71 | backoffLevel := cnt.ConsecutiveFailures - failureThreshold 72 | // Calculates when should the circuit breaker resume propagating requests 73 | // to the service 74 | shouldRetryAt := cnt.LastActivity().Add(time.Second << backoffLevel) 75 | return time.Now().After(shouldRetryAt) 76 | } 77 | 78 | //Breaker return a closure wrapper to hold Circuit Request 79 | func Breaker(c Circuit, failureThreshold uint32) Circuit { 80 | 81 | //闭包内部的全局计数器 和状态标志 82 | cnt := simpleCounter{} 83 | 84 | //ctx can be used hold parameters 85 | return func(ctx context.Context) error { 86 | 87 | //阻止请求 88 | if cnt.ConsecutiveFailures >= failureThreshold { 89 | if !canRetry(cnt, failureThreshold) { 90 | // Fails fast instead of propagating requests to the circuit since 91 | // not enough time has passed since the last failure to retry 92 | return ErrServiceUnavailable 93 | } 94 | //reset mark for failures 95 | cnt.ConsecutiveFailures = 0 96 | } 97 | 98 | // Unless the failure threshold is exceeded the wrapped service mimics the 99 | // old behavior and the difference in behavior is seen after consecutive failures 100 | if err := c(ctx); err != nil { 101 | //连续失败会增大backoff 时间 102 | cnt.Count(FailureState) 103 | return err 104 | } 105 | 106 | cnt.Count(SuccessState) 107 | return nil 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/gobreaker/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | 说明,这是一个由sony的作者完整实现的breaker模式,可以用于生产环境. 4 | -------------------------------------------------------------------------------- /resiliency/01_circuit_breaker/gobreaker/gobreaker_example_test.go: -------------------------------------------------------------------------------- 1 | package gobreaker 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "testing" 8 | 9 | ) 10 | 11 | var cb *CircuitBreaker 12 | 13 | 14 | func TestGoBreaker(t *testing.T) { 15 | 16 | initBreaker() 17 | 18 | body, err := Get("https://bing.com/robots.txt") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | 23 | fmt.Println(string(body)) 24 | } 25 | 26 | 27 | func initBreaker() { 28 | var st Settings 29 | st.Name = "HTTP GET" 30 | st.ReadyToTrip = func(counts Counts) bool { 31 | failureRatio := float64(counts.TotalFailures) / float64(counts.Requests) 32 | return counts.Requests >= 3 && failureRatio >= 0.6 33 | } 34 | 35 | cb = NewCircuitBreaker(st) 36 | } 37 | 38 | // Get wraps http.Get in CircuitBreaker. 39 | func Get(url string) ([]byte, error) { 40 | body, err := cb.Execute(func() (interface{}, error) { 41 | resp, err := http.Get(url) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | defer resp.Body.Close() 47 | body, err := ioutil.ReadAll(resp.Body) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | return body, nil 53 | }) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return body.([]byte), nil 59 | 60 | } 61 | -------------------------------------------------------------------------------- /resiliency/02_rate_limiting/README.md: -------------------------------------------------------------------------------- 1 | # 限流模式 2 | 3 | 就是限制控制突发和访问频率的一种模式 4 | 5 | 在go中是基于Channel的一种便利设计 6 | -------------------------------------------------------------------------------- /resiliency/02_rate_limiting/rate_limiting.go: -------------------------------------------------------------------------------- 1 | package ratelimit 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | /* 9 | 10 | Rate limiting is an very important mechanism 11 | With limiting you can controll resource utilization and maintain quality of service. 12 | Go supports rate limiting by using goroutines, channels, and tickers. 13 | */ 14 | 15 | func rateLimiting(requestQueue chan int, duration int64, allowedBurstCount int) { 16 | 17 | //根据允许的突发数量,创建ch 18 | //只要该队列中有内容,就可以一直取出来,即便ch已经关闭 19 | burstyLimiter := make(chan time.Time, allowedBurstCount) 20 | 21 | //初始化允许突发的数量 22 | for i := 0; i < allowedBurstCount; i++ { 23 | burstyLimiter <- time.Now() 24 | } 25 | 26 | //控制请求频率的计时器 27 | go func() { 28 | for t := range time.Tick(time.Duration(duration) * time.Millisecond) { 29 | burstyLimiter <- t 30 | } 31 | }() 32 | 33 | for req := range requestQueue { 34 | <-burstyLimiter //突发控制器是限流的关键 35 | fmt.Println("request", req, time.Now()) 36 | } 37 | } 38 | 39 | func simpleRateLimiting(duration int64, requestQueueSize int) { 40 | 41 | requests := make(chan int, requestQueueSize) 42 | for i := 1; i <= requestQueueSize; i++ { 43 | requests <- i 44 | } 45 | close(requests) 46 | 47 | limiter := time.Tick(time.Duration(duration) * time.Millisecond) 48 | 49 | for req := range requests { 50 | <-limiter 51 | fmt.Println("request", req, time.Now()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /resiliency/02_rate_limiting/rate_limiting_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-21 12:14:27 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-21 12:27:56 7 | */ 8 | 9 | package ratelimit 10 | 11 | import ( 12 | "testing" 13 | ) 14 | 15 | /* 16 | 17 | Rate limiting is an very important mechanism 18 | With limiting you can controll resource utilization and maintain quality of service. 19 | Go supports rate limiting by using goroutines, channels, and tickers. 20 | */ 21 | 22 | var ( 23 | requestQueueSize = 10 24 | ) 25 | 26 | func TestRateLimiting(t *testing.T) { 27 | 28 | //请求队列 29 | burstyRequests := make(chan int, requestQueueSize) 30 | 31 | for i := 1; i <= requestQueueSize; i++ { 32 | burstyRequests <- i 33 | } 34 | close(burstyRequests) 35 | 36 | //对请求进行限流 37 | 38 | //200ms允许一次请求,最多同时3个突发 39 | rateLimiting(burstyRequests, 200, 3) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /resiliency/03_deadline/README.md: -------------------------------------------------------------------------------- 1 | # deadline pattern 2 | 3 | do a thing ,until the deadline time point 4 | 5 | as known as timeout pattern 6 | 7 | which act like time.after(), but time.after() only do once. 8 | -------------------------------------------------------------------------------- /resiliency/03_deadline/deadline.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-06-05 12:43:39 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-06-05 17:34:37 7 | */ 8 | 9 | // Package deadline implements deadline (also known as "timeout") resiliency pattern for Go. 10 | package deadline 11 | 12 | import ( 13 | "errors" 14 | "time" 15 | ) 16 | 17 | // ErrTimedOut is the error returned from Run when the Worker expires. 18 | var ErrTimedOut = errors.New("timed out waiting for function to finish") 19 | 20 | // Worker implements the Deadline/timeout resiliency pattern. 21 | // worker do the target job 22 | type Worker struct { 23 | timeout time.Duration 24 | action string 25 | } 26 | 27 | // New create a new Worker with the given timeout.and tile 28 | func New(timeout time.Duration, someActionTitle string) *Worker { 29 | return &Worker{ 30 | timeout: timeout, 31 | action: someActionTitle, 32 | } 33 | } 34 | 35 | // Run runs the given function, passing it a stopper channel. If the Worker passes before 36 | // the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper 37 | // channel so that the work function can attempt to exit gracefully. It does not (and cannot) 38 | // simply kill the running function, so if it doesn't respect the stopper channel then it may 39 | // keep running after the Worker passes. If the function finishes before the Worker, then 40 | // the return value of the function is returned from Run. 41 | func (d *Worker) Run(work func(stopperSignal chan error) error) error { 42 | 43 | result := make(chan error) 44 | //we can stop the work in advance 45 | stopper := make(chan error, 1) 46 | 47 | go func() { 48 | value := work(stopper) 49 | select { 50 | case result <- value: 51 | case stopError := <-stopper: 52 | result <- stopError 53 | } 54 | }() 55 | 56 | //handle result 57 | 58 | select { 59 | case ret := <-result: 60 | return ret 61 | case <-time.After(d.timeout): 62 | close(stopper) 63 | return ErrTimedOut 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /resiliency/03_deadline/deadline_test.go: -------------------------------------------------------------------------------- 1 | package deadline 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func workerTakes5ms(stopper chan error) error { 11 | fmt.Println("i'm doing this job in 5ms") 12 | time.Sleep(5 * time.Millisecond) 13 | return nil 14 | } 15 | 16 | func workerTakes20ms(stopper chan error) error { 17 | fmt.Println("i'm doing this job in 20ms,so work will timeout") 18 | time.Sleep(20 * time.Millisecond) 19 | return nil 20 | } 21 | 22 | func cancelWork(stopper chan error) error { 23 | fmt.Println("i'm doing this job") 24 | stopper <- errors.New("canceled job") //cancel job 25 | time.Sleep(5 * time.Millisecond) 26 | fmt.Println("job canceled") 27 | return nil 28 | } 29 | 30 | func returnsError(stopper chan error) error { 31 | fmt.Println("i'm doing this job but error occurred") 32 | return errors.New("foo") 33 | } 34 | 35 | func TestMultiDeadline(t *testing.T) { 36 | 37 | dl := New(15*time.Millisecond, "test multi deadline case") 38 | 39 | if err := dl.Run(workerTakes5ms); err != nil { 40 | t.Error(err) 41 | } 42 | 43 | err := dl.Run(cancelWork) 44 | 45 | t.Log("cancelWork error:", err) 46 | 47 | if err.Error() != "canceled job" { 48 | t.Error(err) 49 | } 50 | 51 | err = dl.Run(workerTakes20ms) 52 | 53 | if err != ErrTimedOut { 54 | t.Error(err) 55 | } 56 | } 57 | 58 | func TestDeadline(t *testing.T) { 59 | 60 | dl := New(1*time.Second, "one time deadline case worker") 61 | 62 | done := make(chan error) 63 | 64 | err := dl.Run(func(stopper chan error) error { 65 | fmt.Println("i am doing something here") 66 | time.Sleep(time.Second * 2) 67 | close(done) 68 | return nil 69 | }) 70 | 71 | if err != ErrTimedOut { 72 | t.Error(err) 73 | } 74 | <-done 75 | 76 | } 77 | -------------------------------------------------------------------------------- /resiliency/04_retrier/backoffs.go: -------------------------------------------------------------------------------- 1 | package retrier 2 | 3 | import "time" 4 | 5 | // ConstantBackoff generates a simple back-off strategy of retrying 'n' times, and waiting 'amount' time after each one. 6 | func ConstantBackoff(n int, amount time.Duration) []time.Duration { 7 | ret := make([]time.Duration, n) 8 | for i := range ret { 9 | ret[i] = amount 10 | } 11 | return ret 12 | } 13 | 14 | // ExponentialBackoff generates a simple back-off strategy of retrying 'n' times, and doubling the amount of 15 | // time waited after each one. 16 | func ExponentialBackoff(n int, initialAmount time.Duration) []time.Duration { 17 | ret := make([]time.Duration, n) 18 | next := initialAmount 19 | for i := range ret { 20 | ret[i] = next 21 | next *= 2 22 | } 23 | return ret 24 | } 25 | -------------------------------------------------------------------------------- /resiliency/04_retrier/backoffs_test.go: -------------------------------------------------------------------------------- 1 | package retrier 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestConstantBackoff(t *testing.T) { 9 | b := ConstantBackoff(1, 10*time.Millisecond) 10 | if len(b) != 1 { 11 | t.Error("incorrect length") 12 | } 13 | for i := range b { 14 | if b[i] != 10*time.Millisecond { 15 | t.Error("incorrect value at", i) 16 | } 17 | } 18 | 19 | b = ConstantBackoff(10, 250*time.Hour) 20 | if len(b) != 10 { 21 | t.Error("incorrect length") 22 | } 23 | for i := range b { 24 | if b[i] != 250*time.Hour { 25 | t.Error("incorrect value at", i) 26 | } 27 | } 28 | } 29 | 30 | func TestExponentialBackoff(t *testing.T) { 31 | b := ExponentialBackoff(1, 10*time.Millisecond) 32 | if len(b) != 1 { 33 | t.Error("incorrect length") 34 | } 35 | if b[0] != 10*time.Millisecond { 36 | t.Error("incorrect value") 37 | } 38 | 39 | b = ExponentialBackoff(4, 1*time.Minute) 40 | if len(b) != 4 { 41 | t.Error("incorrect length") 42 | } 43 | if b[0] != 1*time.Minute { 44 | t.Error("incorrect value") 45 | } 46 | if b[1] != 2*time.Minute { 47 | t.Error("incorrect value") 48 | } 49 | if b[2] != 4*time.Minute { 50 | t.Error("incorrect value") 51 | } 52 | if b[3] != 8*time.Minute { 53 | t.Error("incorrect value") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /resiliency/04_retrier/classifier.go: -------------------------------------------------------------------------------- 1 | package retrier 2 | 3 | // Action is the type returned by a Classifier to indicate how the Retrier should proceed. 4 | type Action int 5 | 6 | const ( 7 | Succeed Action = iota // Succeed indicates the Retrier should treat this value as a success. 8 | Fail // Fail indicates the Retrier should treat this value as a hard failure and not retry. 9 | Retry // Retry indicates the Retrier should treat this value as a soft failure and retry. 10 | ) 11 | 12 | // Classifier is the interface implemented by anything that can classify Errors for a Retrier. 13 | type Classifier interface { 14 | Classify(error) Action 15 | } 16 | 17 | // DefaultClassifier classifies errors in the simplest way possible. If 18 | // the error is nil, it returns Succeed, otherwise it returns Retry. 19 | type DefaultClassifier struct{} 20 | 21 | // Classify implements the Classifier interface. 22 | func (c DefaultClassifier) Classify(err error) Action { 23 | if err == nil { 24 | return Succeed 25 | } 26 | 27 | return Retry 28 | } 29 | 30 | // WhitelistClassifier classifies errors based on a whitelist. If the error is nil, it 31 | // returns Succeed; if the error is in the whitelist, it returns Retry; otherwise, it returns Fail. 32 | type WhitelistClassifier []error 33 | 34 | // Classify implements the Classifier interface. 35 | func (list WhitelistClassifier) Classify(err error) Action { 36 | if err == nil { 37 | return Succeed 38 | } 39 | 40 | for _, pass := range list { 41 | if err == pass { 42 | return Retry 43 | } 44 | } 45 | 46 | return Fail 47 | } 48 | 49 | // BlacklistClassifier classifies errors based on a blacklist. If the error is nil, it 50 | // returns Succeed; if the error is in the blacklist, it returns Fail; otherwise, it returns Retry. 51 | type BlacklistClassifier []error 52 | 53 | // Classify implements the Classifier interface. 54 | func (list BlacklistClassifier) Classify(err error) Action { 55 | if err == nil { 56 | return Succeed 57 | } 58 | 59 | for _, pass := range list { 60 | if err == pass { 61 | return Fail 62 | } 63 | } 64 | 65 | return Retry 66 | } 67 | -------------------------------------------------------------------------------- /resiliency/04_retrier/classifier_test.go: -------------------------------------------------------------------------------- 1 | package retrier 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDefaultClassifier(t *testing.T) { 8 | c := DefaultClassifier{} 9 | 10 | if c.Classify(nil) != Succeed { 11 | t.Error("default misclassified nil") 12 | } 13 | 14 | if c.Classify(errFoo) != Retry { 15 | t.Error("default misclassified foo") 16 | } 17 | if c.Classify(errBar) != Retry { 18 | t.Error("default misclassified bar") 19 | } 20 | if c.Classify(errBaz) != Retry { 21 | t.Error("default misclassified baz") 22 | } 23 | } 24 | 25 | func TestWhitelistClassifier(t *testing.T) { 26 | c := WhitelistClassifier{errFoo, errBar} 27 | 28 | if c.Classify(nil) != Succeed { 29 | t.Error("whitelist misclassified nil") 30 | } 31 | 32 | if c.Classify(errFoo) != Retry { 33 | t.Error("whitelist misclassified foo") 34 | } 35 | if c.Classify(errBar) != Retry { 36 | t.Error("whitelist misclassified bar") 37 | } 38 | if c.Classify(errBaz) != Fail { 39 | t.Error("whitelist misclassified baz") 40 | } 41 | } 42 | 43 | func TestBlacklistClassifier(t *testing.T) { 44 | c := BlacklistClassifier{errBar} 45 | 46 | if c.Classify(nil) != Succeed { 47 | t.Error("blacklist misclassified nil") 48 | } 49 | 50 | if c.Classify(errFoo) != Retry { 51 | t.Error("blacklist misclassified foo") 52 | } 53 | if c.Classify(errBar) != Fail { 54 | t.Error("blacklist misclassified bar") 55 | } 56 | if c.Classify(errBaz) != Retry { 57 | t.Error("blacklist misclassified baz") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resiliency/04_retrier/retrier.go: -------------------------------------------------------------------------------- 1 | // Package retrier implements the "retriable" resiliency pattern for Go. 2 | package retrier 3 | 4 | import ( 5 | "context" 6 | "math/rand" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // Retrier implements the "retriable" resiliency pattern, abstracting out the process of retrying a failed action 12 | // a certain number of times with an optional back-off between each retry. 13 | type Retrier struct { 14 | backoff []time.Duration 15 | class Classifier 16 | jitter float64 17 | rand *rand.Rand 18 | randMu sync.Mutex 19 | } 20 | 21 | // New constructs a Retrier with the given backoff pattern and classifier. The length of the backoff pattern 22 | // indicates how many times an action will be retried, and the value at each index indicates the amount of time 23 | // waited before each subsequent retry. The classifier is used to determine which errors should be retried and 24 | // which should cause the retrier to fail fast. The DefaultClassifier is used if nil is passed. 25 | func New(backoff []time.Duration, class Classifier) *Retrier { 26 | if class == nil { 27 | class = DefaultClassifier{} 28 | } 29 | 30 | return &Retrier{ 31 | backoff: backoff, 32 | class: class, 33 | rand: rand.New(rand.NewSource(time.Now().UnixNano())), 34 | } 35 | } 36 | 37 | // Run executes the given work function by executing RunCtx without context.Context. 38 | func (r *Retrier) Run(work func() error) error { 39 | return r.RunCtx(context.Background(), func(ctx context.Context) error { 40 | // never use ctx 41 | return work() 42 | }) 43 | } 44 | 45 | // RunCtx executes the given work function, then classifies its return value based on the classifier used 46 | // to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is 47 | // returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy 48 | // before retrying. If the total number of retries is exceeded then the return value of the work function 49 | // is returned to the caller regardless. 50 | func (r *Retrier) RunCtx(ctx context.Context, work func(ctx context.Context) error) error { 51 | retries := 0 52 | for { 53 | ret := work(ctx) 54 | 55 | switch r.class.Classify(ret) { 56 | case Succeed, Fail: 57 | return ret 58 | case Retry: 59 | if retries >= len(r.backoff) { 60 | return ret 61 | } 62 | 63 | timeout := time.After(r.calcSleep(retries)) 64 | if err := r.sleep(ctx, timeout); err != nil { 65 | return err 66 | } 67 | 68 | retries++ 69 | } 70 | } 71 | } 72 | 73 | func (r *Retrier) sleep(ctx context.Context, t <-chan time.Time) error { 74 | select { 75 | case <-t: 76 | return nil 77 | case <-ctx.Done(): 78 | return ctx.Err() 79 | } 80 | } 81 | 82 | func (r *Retrier) calcSleep(i int) time.Duration { 83 | // lock unsafe rand prog 84 | r.randMu.Lock() 85 | defer r.randMu.Unlock() 86 | // take a random float in the range (-r.jitter, +r.jitter) and multiply it by the base amount 87 | return r.backoff[i] + time.Duration(((r.rand.Float64()*2)-1)*r.jitter*float64(r.backoff[i])) 88 | } 89 | 90 | // SetJitter sets the amount of jitter on each back-off to a factor between 0.0 and 1.0 (values outside this range 91 | // are silently ignored). When a retry occurs, the back-off is adjusted by a random amount up to this value. 92 | func (r *Retrier) SetJitter(jit float64) { 93 | if jit < 0 || jit > 1 { 94 | return 95 | } 96 | r.jitter = jit 97 | } 98 | -------------------------------------------------------------------------------- /resiliency/04_retrier/retrier_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: https://github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-11 17:43:55 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-11 17:43:55 7 | */ 8 | 9 | package retrier 10 | 11 | import ( 12 | "context" 13 | "errors" 14 | "testing" 15 | "time" 16 | ) 17 | 18 | var ( 19 | i int 20 | errFoo = errors.New("work cancel") 21 | errBar = errors.New("work cancel 2") 22 | errBaz = errors.New("work cancel 3") 23 | ) 24 | 25 | func genWork(returns []error) func() error { 26 | i = 0 27 | return func() error { 28 | i++ 29 | if i > len(returns) { 30 | return nil 31 | } 32 | return returns[i-1] 33 | } 34 | } 35 | 36 | func genWorkWithCtx() func(ctx context.Context) error { 37 | i = 0 38 | return func(ctx context.Context) error { 39 | select { 40 | case <-ctx.Done(): 41 | return errFoo 42 | default: 43 | i++ 44 | } 45 | return nil 46 | } 47 | } 48 | 49 | func TestRetrier(t *testing.T) { 50 | r := New([]time.Duration{0, 10 * time.Millisecond}, WhitelistClassifier{errFoo}) 51 | 52 | err := r.Run(genWork([]error{errFoo, errFoo})) 53 | if err != nil { 54 | t.Error(err) 55 | } 56 | if i != 3 { 57 | t.Error("run wrong number of times") 58 | } 59 | 60 | err = r.Run(genWork([]error{errFoo, errBar})) 61 | if err != errBar { 62 | t.Error(err) 63 | } 64 | if i != 2 { 65 | t.Error("run wrong number of times") 66 | } 67 | 68 | err = r.Run(genWork([]error{errBar, errBaz})) 69 | if err != errBar { 70 | t.Error(err) 71 | } 72 | if i != 1 { 73 | t.Error("run wrong number of times") 74 | } 75 | } 76 | 77 | func TestRetrierCtx(t *testing.T) { 78 | ctx, cancel := context.WithCancel(context.Background()) 79 | 80 | r := New([]time.Duration{0, 10 * time.Millisecond}, WhitelistClassifier{}) 81 | 82 | err := r.RunCtx(ctx, genWorkWithCtx()) 83 | if err != nil { 84 | t.Error(err) 85 | } 86 | if i != 1 { 87 | t.Error("run wrong number of times") 88 | } 89 | 90 | cancel() 91 | 92 | err = r.RunCtx(ctx, genWorkWithCtx()) 93 | if err != errFoo { 94 | t.Error("context must be cancelled") 95 | } 96 | if i != 0 { 97 | t.Error("run wrong number of times") 98 | } 99 | } 100 | 101 | func TestRetrierNone(t *testing.T) { 102 | r := New(nil, nil) 103 | 104 | i = 0 105 | err := r.Run(func() error { 106 | i++ 107 | return errFoo 108 | }) 109 | if err != errFoo { 110 | t.Error(err) 111 | } 112 | if i != 1 { 113 | t.Error("run wrong number of times") 114 | } 115 | 116 | i = 0 117 | err = r.Run(func() error { 118 | i++ 119 | return nil 120 | }) 121 | if err != nil { 122 | t.Error(err) 123 | } 124 | if i != 1 { 125 | t.Error("run wrong number of times") 126 | } 127 | } 128 | 129 | func TestRetrierJitter(t *testing.T) { 130 | r := New([]time.Duration{0, 10 * time.Millisecond, 4 * time.Hour}, nil) 131 | 132 | if r.calcSleep(0) != 0 { 133 | t.Error("Incorrect sleep calculated") 134 | } 135 | if r.calcSleep(1) != 10*time.Millisecond { 136 | t.Error("Incorrect sleep calculated") 137 | } 138 | if r.calcSleep(2) != 4*time.Hour { 139 | t.Error("Incorrect sleep calculated") 140 | } 141 | 142 | r.SetJitter(0.25) 143 | for i := 0; i < 20; i++ { 144 | if r.calcSleep(0) != 0 { 145 | t.Error("Incorrect sleep calculated") 146 | } 147 | 148 | slp := r.calcSleep(1) 149 | if slp < 7500*time.Microsecond || slp > 12500*time.Microsecond { 150 | t.Error("Incorrect sleep calculated") 151 | } 152 | 153 | slp = r.calcSleep(2) 154 | if slp < 3*time.Hour || slp > 5*time.Hour { 155 | t.Error("Incorrect sleep calculated") 156 | } 157 | } 158 | 159 | r.SetJitter(-1) 160 | if r.jitter != 0.25 { 161 | t.Error("Invalid jitter value accepted") 162 | } 163 | 164 | r.SetJitter(2) 165 | if r.jitter != 0.25 { 166 | t.Error("Invalid jitter value accepted") 167 | } 168 | } 169 | 170 | func TestRetrierThreadSafety(t *testing.T) { 171 | r := New([]time.Duration{0}, nil) 172 | for i := 0; i < 2; i++ { 173 | go func() { 174 | r.Run(func() error { 175 | return errors.New("error") 176 | }) 177 | }() 178 | } 179 | } 180 | 181 | func ExampleRetrier() { 182 | r := New(ConstantBackoff(3, 100*time.Millisecond), nil) 183 | 184 | err := r.Run(func() error { 185 | // do some work 186 | return nil 187 | }) 188 | 189 | if err != nil { 190 | // handle the case where the work failed three times 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /structure/01_facade/README.md: -------------------------------------------------------------------------------- 1 | # 外观模式 2 | 3 | 又称门面模式,APIs Gateway就是一种facade模式的,用户只要通过APIs就可访问系统功能. 4 | 5 | 这个模式正如New模式一样,已经熟悉到了被经常忽略的地步,常用在用集中访问的场景,比如政务部门集中办事窗口,开学集中报道注册等. 6 | 7 | Service Desk 或者Calling Center 呼叫中心也是典型门面模式场景,对一些问题的处理和访问,都通过一个集中的入口统一访问. 8 | 9 | 日常的超市/商店就是一个很典型门面模式,日常用品你不用去各个进货商那里买想要的东西,只需要去超市能集中买到你想要的所有东西. 10 | 11 | 门面模式,完全可以在脑子里想象一个步行街的两侧的门面(店铺),它货源来自大城市里面的多个供应商,这个场景对门面模式的描述简直就是绝配. 12 | -------------------------------------------------------------------------------- /structure/01_facade/facade.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | //ISupertMarketVendor is facade interface of facade package 8 | type ISupertMarketVendor interface { 9 | Sell(count int) 10 | } 11 | 12 | //SaltVendor 盐供应商 13 | type SaltVendor struct{} 14 | 15 | //MilkVendor 牛奶供应商 16 | type MilkVendor struct{} 17 | 18 | //RiceVendor 大米供应商 19 | type RiceVendor struct{} 20 | 21 | //三家供应商都能直接卖东西 22 | 23 | //Sell 卖完了 24 | func (SaltVendor) Sell(count int) { 25 | 26 | if count > 5 { 27 | fmt.Println("Salt out") 28 | 29 | } 30 | fmt.Println("Milk got") 31 | 32 | } 33 | 34 | //Sell 卖完了 35 | func (MilkVendor) Sell(count int) { 36 | 37 | if count > 20 { 38 | fmt.Println("Milk out") 39 | 40 | } 41 | fmt.Println("Milk got") 42 | 43 | } 44 | 45 | //Sell 卖完了 46 | func (RiceVendor) Sell(count int) { 47 | 48 | if count > 10 { 49 | fmt.Println("Rice out") 50 | 51 | } 52 | fmt.Println("Rice got") 53 | 54 | } 55 | 56 | //SuperMarket is facade implement 57 | //SuperMarket is Facade object 58 | //SuperMarket 具有集中进货能力 59 | type SuperMarket struct { 60 | saltsVendor ISupertMarketVendor 61 | milksVendor ISupertMarketVendor 62 | ricesVendor ISupertMarketVendor 63 | } 64 | 65 | //ISupertMarket market can do 66 | type ISupertMarket interface { 67 | Sell(salt, milk, rice int) 68 | } 69 | 70 | //Sell 集中购买 71 | func (s *SuperMarket) Sell(salt, milk, rice int) { 72 | s.saltsVendor.Sell(salt) 73 | s.milksVendor.Sell(milk) 74 | s.ricesVendor.Sell(rice) 75 | } 76 | 77 | //NewSuperMarket get a market 78 | func NewSuperMarket() ISupertMarket { 79 | 80 | return &SuperMarket{ 81 | saltsVendor: MilkVendor{}, 82 | milksVendor: MilkVendor{}, 83 | ricesVendor: RiceVendor{}, 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /structure/01_facade/facade_test.go: -------------------------------------------------------------------------------- 1 | package facade 2 | 3 | import "testing" 4 | 5 | func TestFacadeSuperMarket(t *testing.T) { 6 | 7 | supermarket := NewSuperMarket() 8 | 9 | supermarket.Sell(1, 3, 5) 10 | supermarket.Sell(2, 11, 30) 11 | 12 | supermarket.Sell(8, 8, 30) 13 | } 14 | -------------------------------------------------------------------------------- /structure/02_adapter/README.md: -------------------------------------------------------------------------------- 1 | # 适配器模式 2 | 3 | 适配器模式就是用来做适配的,实际应用过程中最容易遇到的就是,面对多种多样的设备和协议类型,做接入的时候,需要将设备的协议数据转换为系统内部能够识别的统一数据结构,对这就是适配器模式的一个实际场景,适配器用于转换一种形态到另一种形态,这种所谓的形态可能指数据结构、协议、计算方法、处理过程等 4 | 5 | 现实生活中的U形水管接头、插头转换器就是一个简单明了的适配器模式的例子,两针插头,是无法插入三孔插座的,所以三转二的电源插座/插头转换器,就是一个是适配器. 6 | 7 | 代码中的适配器模式其实就是要实现一个,`三转二`的插头转换器,使两针插头可以正常充电. 8 | -------------------------------------------------------------------------------- /structure/02_adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import "fmt" 4 | 5 | //IPlug 插头 6 | type IPlug interface { 7 | GetPin() int 8 | } 9 | 10 | //TwoPinPlugin 造一个两针的插头 11 | type TwoPinPlugin struct { 12 | } 13 | 14 | //GetPin 获取插头针数 15 | func (t *TwoPinPlugin) GetPin() int { 16 | return 2 17 | } 18 | 19 | //IPowerSocket 电源插座 20 | type IPowerSocket interface { 21 | PlugCharging(p IPlug) 22 | } 23 | 24 | //IThreePinPowerSocket 三孔插座 25 | type IThreePinPowerSocket interface { 26 | ThreePinCharging(p IPlug) 27 | } 28 | 29 | //ThreePinPowerSocket 是被适配的目标类 30 | type ThreePinPowerSocket struct{} 31 | 32 | //ThreePinCharging 只能为三针的插头通电 33 | func (*ThreePinPowerSocket) ThreePinCharging(p IPlug) { 34 | if p.GetPin() != 3 { 35 | fmt.Println("i can not charge for this type") 36 | return 37 | } 38 | fmt.Println("charging for three pin plug") 39 | } 40 | 41 | //NewPowerAdapter 生产一个新的电源适配器 42 | func NewPowerAdapter(threePinPowerSocket IThreePinPowerSocket) IPowerSocket { 43 | return &PowerAdapter{IThreePinPowerSocket(threePinPowerSocket), 0} 44 | } 45 | 46 | //PowerAdapter 是能充电的关键电源转换器 47 | type PowerAdapter struct { 48 | IThreePinPowerSocket 49 | pin int 50 | } 51 | 52 | //GetPin Adapter 的兼容能力 53 | func (p *PowerAdapter) GetPin() int { 54 | return p.pin 55 | } 56 | 57 | //PlugCharging 在PowerAdapter上进行实现 58 | func (p *PowerAdapter) PlugCharging(ip IPlug) { 59 | if ip.GetPin() == 2 { 60 | p.pin = 3 61 | p.ThreePinCharging(p) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /structure/02_adapter/adapter_test.go: -------------------------------------------------------------------------------- 1 | package adapter 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPowerSocketAdapter(t *testing.T) { 8 | 9 | plug := &TwoPinPlugin{} 10 | 11 | threePinSocket := ThreePinPowerSocket{} 12 | 13 | //三孔插头是不能为两针插头充电的,可以试试看 14 | threePinSocket.ThreePinCharging(plug) 15 | 16 | //只好加一个电源适配器 17 | powersocket := NewPowerAdapter(&threePinSocket) 18 | 19 | //再试试能不能充电 20 | powersocket.PlugCharging(plug) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /structure/03_bridge/README.md: -------------------------------------------------------------------------------- 1 | # 桥接模式 2 | 3 | 桥接模式类似于策略模式,原因在于他们都将行为交给了另一个接口实现,再组合实现完整功能/流程/业务. 4 | 5 | 区别在于策略模式封装一系列算法/方法/功能/使得他们可以互相替换,关键点在于策略模式的实现方法可以动态替换,但是桥接模式的特点是,一旦组合运行时一般不会发生变化. 6 | 7 | 桥接模式的变化点在于调用方是变化的. 8 | 9 | 在策略模式的例子中,银行就一个,但是储户类型是多个,银行需要为不同类型的用户服务,银行有同一个规则流程和处理方法,但是不同的用户对相同处理过程的响应和处理不一样。 10 | 11 | 在桥接模式中,消息类型是多个,转给不同的处理对象. 12 | 13 | 演示1个例子,场景如下: 14 | 15 | 对于Web应用系统,当平台后端收到消息并需要推送的时候,常见有四种选择: 16 | 1、通过第三方的的Internet通道,离线推送到手机,通知用户 17 | 2、通过邮件通知到用户 18 | 3、通过在线的长连接进行websocket推送 19 | 4、通过手机4G/5G的短信通道通知到用户 20 | 21 | 选择其中的二三两类进行演示。 22 | 23 | 桥接体现在发送`WSMessage`类型消息的时候,`WSMessage`不是直接发送的而是顺序转交给`EmergencyWSMessage`,由于`EmergencyWSMessage`执行发送动作,并且一开始`EmergencyWSMessage`就知道自己要发送`EmergencyWSMessage`消息,因为在初始化时候`EmergencyWSMessage`对象就直接绑定在了`WSMessage`上. 24 | -------------------------------------------------------------------------------- /structure/03_bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import "fmt" 4 | 5 | //IMessage 发送消息接口 6 | type IMessage interface { 7 | NoticeUser(text string) 8 | Priority() int 9 | } 10 | 11 | //ISMSMessage send SMS MSG 12 | type ISMSMessage interface { 13 | //延迟接口的实现到其他类中 14 | NoticeUser(text string, noticeMessage IMessage) 15 | } 16 | 17 | //WSMessage MSG 18 | type WSMessage struct { 19 | Handler ISMSMessage //持有进一步实现的引用关系 20 | Level int 21 | } 22 | 23 | //NoticeUser by SMS 24 | func (w *WSMessage) NoticeUser(text string) { 25 | //转递消息给其他对象,相当于承上启下 26 | fmt.Println("Websocket Notice User...", text) 27 | //转递消息给其他对象,相当于承上启下,并且需要把上下文变量传递下去 28 | if w.Handler != nil { 29 | w.Handler.NoticeUser(text, w) 30 | } 31 | 32 | } 33 | 34 | //Priority of SMS 35 | func (w *WSMessage) Priority() int { 36 | return w.Level 37 | } 38 | 39 | //EmailMessage MSG 40 | type EmailMessage struct { 41 | Handler ISMSMessage 42 | Level int 43 | } 44 | 45 | //NoticeUser by SMS 46 | func (e *EmailMessage) NoticeUser(text string) { 47 | //转递消息给其他对象,相当于承上启下,并且需要把上下文变量传递下去 48 | fmt.Println("Email Notice User...", text) 49 | if e.Handler != nil { 50 | e.Handler.NoticeUser(text, e) 51 | } 52 | 53 | } 54 | 55 | //Priority of SMS 56 | func (e *EmailMessage) Priority() int { 57 | return e.Level 58 | } 59 | 60 | ///需要实现具体的消息发送行为 61 | 62 | //EmergencyWSMessage 紧急的短信消息 63 | type EmergencyWSMessage struct { 64 | } 65 | 66 | //NoticeUser by email 67 | func (e *EmergencyWSMessage) NoticeUser(text string, noticeMessage IMessage) { 68 | fmt.Println("Notice User", text, " By Websocket:", "with Level: ", noticeMessage.Priority()) 69 | } 70 | 71 | //EmergencyEmailMessage 紧急的短信消息 72 | type EmergencyEmailMessage struct { 73 | } 74 | 75 | //NoticeUser by email 76 | func (e *EmergencyEmailMessage) NoticeUser(text string, noticeMessage IMessage) { 77 | fmt.Println("Notice User:", text, " By Email:", "with Level: ", noticeMessage.Priority()) 78 | 79 | } 80 | -------------------------------------------------------------------------------- /structure/03_bridge/bridge_test.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import "testing" 4 | 5 | func TestSendMessage(t *testing.T) { 6 | 7 | //注意看这里,接口的实例关系在初始化时候是固定的 8 | ws := &WSMessage{&EmergencyWSMessage{}, 100} 9 | 10 | //注意看这里,接口的实例关系在初始化时候是固定的 11 | email := &EmailMessage{&EmergencyEmailMessage{}, 10} 12 | 13 | ws.NoticeUser("Miss White ,Let's Drink") 14 | email.NoticeUser("Miss White,Fire!,Fire!") 15 | 16 | ews := &EmergencyWSMessage{} 17 | eem := &EmergencyEmailMessage{} 18 | list := []IMessage{ 19 | &WSMessage{ews, 50}, 20 | &WSMessage{ews, 100}, 21 | &EmailMessage{eem, 10}, 22 | &EmailMessage{eem, 20}, 23 | } 24 | for _, v := range list { 25 | v.NoticeUser("Let’s go for fun") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /structure/04_flyweight/README.md: -------------------------------------------------------------------------------- 1 | # 享元模式 2 | 3 | 享元模式是细粒度控制主体构成的一种方式,英文叫 flyweight,单词分开看反倒更容易理解,fly-weight,看起来是要消减对象的大小/重量(size/weight),对!就是这个意思!抽取对象中的不变部分,使其能够容易的被复用或者共用,进而能够减小内存占用,优化性能,提高运行时效率,比如,会减少对象在内存分配/克隆时的空间大小,减小对象内存占用损耗,缩短响应时间等。 4 | 5 | 注意:实际上完整的享元模式,包含两部分可以被共享的部分,不可以被共享部分,两者组合起来才构成一个完整的整体,设计享元模式,严谨的讲需要针对可共享部分与不可共享两部分设计接口。 6 | 7 | 8 | 单纯享元模式:在单纯享元模式中,所有的具体享元类都是可以共享的,不存在非共享具体享元类。 9 | 复合享元模式:将一些单纯享元对象使用组合模式加以组合,还可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。 10 | 11 | 享元的的可共享部分在程序是设计,一般是集中管理的,一般设计为一个存储map的,享元的不可共享部分,一般是即用即创建,完全的专有模式. 12 | 13 | 现实生活中的快递送货任务就一个很好的享元模式,快递公司的快递员是共享的,并且可以重复使用,快递公司不可能送一个快递雇一个快递员,否则,这个代价太大巨大了,但是快递员送的包裹是不一样,这是每个人的私有物品,所以不变的快递员+一直在变化的快递,才能构成一次完整的送货任务。 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /structure/04_flyweight/flyweight.go: -------------------------------------------------------------------------------- 1 | package flyweight 2 | 3 | /* 4 | * @Description: github.com/crazybber 5 | * @Author: Edward 6 | * @Date: 2020-05-01 18:10:57 7 | * @Last Modified by: Edward 8 | * @Last Modified time: 2020-05-01 19:38:21 9 | */ 10 | import ( 11 | "fmt" 12 | ) 13 | 14 | //IDeliverCompany 公司的能力 15 | type IDeliverCompany interface { 16 | //雇人 17 | Hire(name string) 18 | //送货任务 19 | DeliverTask(name string, packets []string) 20 | 21 | GetDeliver(name string) IDeliver 22 | } 23 | 24 | //DeliverCompany 快递公司 25 | type DeliverCompany struct { 26 | Employees map[string]IDeliver 27 | } 28 | 29 | //IDeliver 快递员能做的事情 30 | type IDeliver interface { 31 | //送货 32 | DeliverPackets(packets []string) 33 | } 34 | 35 | //Deliver 快递员工,员工是一个享元对象 36 | type Deliver struct { 37 | Name string //快递员的名字 38 | Packets []string //快递员的携带的快递,这一部分是变化的 39 | } 40 | 41 | //Hire 雇佣新员工,员工是全公司共享 42 | func (d *DeliverCompany) Hire(name string) { 43 | if d.Employees == nil || len(d.Employees) == 0 { 44 | d.Employees = make(map[string]IDeliver) 45 | } 46 | if _, ok := d.Employees[name]; ok { 47 | fmt.Println("already hired") 48 | return 49 | } 50 | d.Employees[name] = &Deliver{Name: name} 51 | 52 | fmt.Println("hired") 53 | } 54 | 55 | //GetDeliver return Deliver 56 | func (d *DeliverCompany) GetDeliver(name string) IDeliver { 57 | 58 | return d.Employees[name] 59 | 60 | } 61 | 62 | //DeliverTask 派员工送货 63 | func (d *DeliverCompany) DeliverTask(name string, packets []string) { 64 | 65 | d.Employees[name].DeliverPackets(packets) 66 | 67 | } 68 | 69 | //DeliverPackets 送货了 70 | func (d *Deliver) DeliverPackets(packets []string) { 71 | 72 | fmt.Println(d.Name, ": Delivered:", packets) 73 | } 74 | -------------------------------------------------------------------------------- /structure/04_flyweight/flyweight_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | * @Description: github.com/crazybber 3 | * @Author: Edward 4 | * @Date: 2020-05-01 17:24:28 5 | * @Last Modified by: Edward 6 | * @Last Modified time: 2020-05-01 19:46:30 7 | */ 8 | package flyweight 9 | 10 | import ( 11 | "fmt" 12 | "testing" 13 | ) 14 | 15 | func TestDeliveryPackets(t *testing.T) { 16 | 17 | dc := &DeliverCompany{Employees: make(map[string]IDeliver)} 18 | 19 | dc.Hire("bob") 20 | dc.Hire("lily") 21 | dc.Hire("bob") 22 | 23 | deliver1 := dc.GetDeliver("lily") 24 | 25 | deliver2 := dc.GetDeliver("lily") 26 | 27 | //一次送货任务 28 | dc.DeliverTask("lily", []string{"box1", "box2"}) 29 | 30 | dc.DeliverTask("lily", []string{"box6", "box7", "box8"}) 31 | 32 | deliver2.DeliverPackets([]string{"box9", "box10", "box11"}) 33 | 34 | deliver1.DeliverPackets([]string{"box12"}) 35 | 36 | } 37 | 38 | func TestDeliverEmployee(t *testing.T) { 39 | dc := &DeliverCompany{Employees: make(map[string]IDeliver)} 40 | 41 | dc.Hire("bob") 42 | dc.Hire("lily") 43 | 44 | deliver1 := dc.GetDeliver("lily") 45 | 46 | deliver2 := dc.GetDeliver("lily") 47 | 48 | dp1 := fmt.Sprintf("%p", deliver1) 49 | 50 | dp2 := fmt.Sprintf("%p", deliver2) 51 | 52 | if dp1 != dp2 { 53 | t.Error(dp1, "not the same with", dp2) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /structure/05_composite/README.md: -------------------------------------------------------------------------------- 1 | # 复合模式 2 | 3 | 复合模式,目的是为了实现对一类对象的一致性访问,复合模式也叫组合模式,但是不同于go本身的组合设计理念,go本身的组合宽泛的多,用于结构与结构、结构与接口、接口与接口的嵌套组合. 4 | 5 | 我认为用复合表述更准确一些,复合模式的要点在于设计复合类型,兼容多种有相关关系类型,使得外部可以使用的使用方式使用相同接口(往往可能需要传入不同的参数)访问对象或者对象集。 6 | 7 | 复合模式在内存中的数据结构逻辑上常常表达为一个多叉树结构,所以有时候更直接叫树模式,用于统一叶子节点对象和非叶子节点对象的访问,很明显复合模式可用于与类别相关的问题处理,并且尤其擅长类别相似的问题的处理。 8 | 9 | 所以反过来想,当遇到分类相关并且关系可以表达为<整体-局部>关系的,或者遇到可以用树状逻辑梳理和表达的问题时候,复合模式可能就是最佳的问题解决思路了 10 | 11 | 引申一下,在我们处理算法的时候,往往是一个输入一个输出,但是在处理过程中往往是要将问题进行分类,所以复合模式思想也可强化算法思路. 12 | 13 | 符合模式中的核心重点是一个 :复合对象,这个复合对象,能同时代表叶子节点和非叶子节点的对接。 14 | 15 | 16 | 现实生活中的出海码头的货运集装箱就是一个很好复合模式的例子,集装箱里面可以装很多包装箱,包装箱里面可以装具体的货物,不论是货还是集装箱本身,对于出海的货运公司来讲都是集装箱,而不是具体的一个类货物,所以的货物都通过大小集装箱复合成统一的集装箱,对货运公司提供统一的使用方式,如下图,整个集装箱构成一棵树. 17 | 18 | - 集装箱 19 | - 包装箱A 20 | - 货物B 21 | - 包装箱C 22 | - 货物D 23 | 24 | ![一个示例](../../images/Composite.png) 25 | 26 | 叶子是在集装箱例子中就是具体的物品,它没有能力将其他物品复合进自身。 27 | 28 | [设计模式之:组合模式(Composite)](https://www.cnblogs.com/dragonflyyi/p/5043144.html) 29 | -------------------------------------------------------------------------------- /structure/05_composite/composite.go: -------------------------------------------------------------------------------- 1 | package composite 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | //////////////////////////////// 9 | //使用集装箱与货物的例子 10 | //下面三个是对象的该模式的关键结构 11 | //////////////////////////////// 12 | 13 | //Cargo class 基本的货物类型可以被继承 14 | type Cargo struct { 15 | Volume uint //货物需要要尺寸 16 | Description string //描述 17 | } 18 | 19 | //GetCargoType return type 20 | func (c *Cargo) GetCargoType() string { 21 | return reflect.TypeOf(c).String() 22 | } 23 | 24 | //ShowContent return type 25 | func (c *Cargo) ShowContent() { 26 | typeName := reflect.TypeOf(c).String() 27 | fmt.Println("Type: ", typeName, " Content ", c.Description) 28 | 29 | } 30 | 31 | //Box 复合类型,表示集装箱 32 | //Box 复合类型,集装箱里面装具体的货物,也可以继续放箱子 33 | type Box struct { 34 | Cargo //继承货物类 35 | InnerSpace uint //内部空间 36 | Children []interface{} //有子对象 37 | } 38 | 39 | //PutInCargo 增加新的能力 40 | //PutInCargo (cargo ICargo) //放入一个子类型 41 | func (b *Box) PutInCargo(cargo interface{}) { 42 | 43 | switch cargo.(type) { 44 | case *Box: 45 | fmt.Println("get a Box: Type: ", cargo.(*Box).GetCargoType()) 46 | case *SingleCargo: 47 | fmt.Println("get a SingleCargo Type: ", cargo.(*SingleCargo).GetCargoType()) 48 | } 49 | 50 | b.Children = append(b.Children, cargo) 51 | 52 | } 53 | 54 | //GetChildren () []ICompositedCargo 55 | func (b *Box) GetChildren() []interface{} { 56 | return b.Children 57 | } 58 | 59 | //ShowContent 覆盖继承实现 60 | //ShowContent display children content 61 | func (b *Box) ShowContent() { 62 | typeName := reflect.TypeOf(b).String() 63 | fmt.Println("Type: ", typeName, " InnerSpace ", b.InnerSpace, " Children: ", b.Description) 64 | count := len(b.Children) 65 | fmt.Println("Children Count: ", count, " Description ", b.Description) 66 | for _, child := range b.Children { 67 | //判断类型 68 | switch child.(type) { 69 | case *Box: 70 | fmt.Println("Current Child is a Box: Type cast to (*Box) ") 71 | child.(*Box).ShowContent() 72 | case *SingleCargo: 73 | fmt.Println("Current Child is a Single Cargo: Type cast to (*SingleCargo) ") 74 | child.(*SingleCargo).ShowContent() 75 | } 76 | } 77 | 78 | } 79 | 80 | //SingleCargo 具体的非复合类型,对应多叉树的叶子节点 81 | type SingleCargo struct { 82 | Cargo //继承货物类,具有基本的货物熟悉 83 | From, To string //货是从谁发到谁的 84 | } 85 | 86 | //ShowContent return type 87 | func (s *SingleCargo) ShowContent() { 88 | typeName := reflect.TypeOf(s).String() 89 | fmt.Println("Type: ", typeName, " From ", s.From, " To ", s.To, " Content ", s.Description) 90 | 91 | } 92 | -------------------------------------------------------------------------------- /structure/05_composite/composite_test.go: -------------------------------------------------------------------------------- 1 | package composite 2 | 3 | import "testing" 4 | 5 | //我们目的是:使用一致性的方式访问一类具有共性并相互组合起来的对象 6 | 7 | func TestCompositeconnections(t *testing.T) { 8 | 9 | box1 := &Box{Cargo: Cargo{1, "Big Box"}, InnerSpace: 130000} 10 | 11 | box2 := &Box{Cargo{2, "Middle Box"}, 80000, nil} 12 | 13 | box1.PutInCargo(box2) 14 | 15 | cargo1 := &SingleCargo{Cargo{3, "Hat"}, "CN", "CA"} 16 | 17 | box1.PutInCargo(cargo1) 18 | 19 | cargo2 := &SingleCargo{Cargo: Cargo{4, "Men Cloth"}, From: "China", To: "UK"} 20 | 21 | cargo3 := &SingleCargo{Cargo: Cargo{5, "Women Cloth"}, From: "HK", To: "TW"} 22 | 23 | box2.PutInCargo(cargo2) 24 | 25 | box2.PutInCargo(cargo3) 26 | 27 | box1.ShowContent() 28 | 29 | //Box1 30 | // -Box2 31 | // -Cargo 2 32 | // -Cargo 3 33 | // -Cargo1 34 | 35 | } 36 | 37 | func TestComposite(t *testing.T) { 38 | 39 | box1 := Box{Cargo: Cargo{1, "Big Box"}, InnerSpace: 130000} 40 | box1.ShowContent() 41 | 42 | box2 := Box{Cargo{2, "Middle Box"}, 80000, nil} 43 | 44 | box2.ShowContent() 45 | 46 | cargo1 := SingleCargo{Cargo{1, "Hat"}, "CN", "CA"} 47 | 48 | cargo1.ShowContent() 49 | 50 | cargo2 := SingleCargo{Cargo: Cargo{2, "Men Cloth"}, From: "China", To: "UK"} 51 | 52 | cargo2.ShowContent() 53 | 54 | cargo3 := &SingleCargo{Cargo: Cargo{2, "Women Cloth"}, From: "HK", To: "TW"} 55 | 56 | cargo3.ShowContent() 57 | 58 | //Call base class 59 | cargo3.Cargo.ShowContent() 60 | } 61 | -------------------------------------------------------------------------------- /structure/06_decorator/README.md: -------------------------------------------------------------------------------- 1 | # 装饰器模式 2 | 3 | 装饰器模式就是比较简单了,就是做装饰,其目的是在不改变原有类型/功能/对象的情况下,对其进行装饰,扩展,以达到公共强化和扩展的目的。 4 | 5 | 装饰模式的目的是在原有功能或者能力的基础上提供额外的更多的能力和特性,这一点和代理模式、复合模式是有很大不同的,了解每一种模式,重点是要了解这个模式的最初的设计者应用场景和其解决目的,以便加以利用。 6 | 7 | Go语言借助于匿名组合和非入侵式接口可以很方便实现装饰模式。使用匿名组合,在装饰器中不必显式定义转调原对象方法。 8 | 9 | 现实生活中例子就太多了,可以想一下带 "装饰"的东西,车子不漂亮,搞个车贴,脸不好看,化个妆,屋墙不好看,贴个墙纸。 10 | 11 | 都是在在不改变原有东西的情况下,做一些扩展和强化。 12 | -------------------------------------------------------------------------------- /structure/06_decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | //////////////////////////////// 4 | //拿化妆做例子 5 | 6 | //IFaceLooks 脸部的颜值 7 | type IFaceLooks interface { 8 | FaceLooks() int 9 | } 10 | 11 | //NatureGirl 天然无化妆的小姐姐 12 | type NatureGirl struct { 13 | faceValue int 14 | } 15 | 16 | //FaceLooks 获取小姐姐的颜值 17 | func (n *NatureGirl) FaceLooks() int { 18 | return n.faceValue 19 | } 20 | 21 | //GirlWithMakeups 化妆后的小姐姐 22 | type GirlWithMakeups struct { 23 | origin IFaceLooks //这就是那个自然美的小姐姐 24 | facePlus int //脸部加成,说,你想化成什么样子吧。 25 | } 26 | 27 | //NewGirlWithMakeup 返回一个化妆后的小姐姐 28 | func NewGirlWithMakeup(origin IFaceLooks, facePlus int) IFaceLooks { 29 | return &GirlWithMakeups{ 30 | origin: origin, 31 | facePlus: facePlus, 32 | } 33 | } 34 | 35 | //FaceLooks 我要开始化妆了.. 36 | func (g *GirlWithMakeups) FaceLooks() int { 37 | return g.origin.FaceLooks() + g.facePlus 38 | } 39 | 40 | //FaceReal 实际的颜值.. 41 | func (g *GirlWithMakeups) FaceReal() int { 42 | return g.origin.FaceLooks() 43 | } 44 | -------------------------------------------------------------------------------- /structure/06_decorator/decorator_log_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "log" 5 | "testing" 6 | ) 7 | 8 | //装饰一个函数,或者说包装一个函数 9 | type FuncToDecorate func(int) int 10 | 11 | func TestDecorateLog(t *testing.T) { 12 | f := LogDecorate(Double) 13 | f(5) 14 | } 15 | 16 | func Double(n int) int { 17 | return n * 2 18 | } 19 | 20 | func LogDecorate(fn FuncToDecorate) FuncToDecorate { 21 | return func(n int) int { 22 | log.Println("Starting the execution with the integer", n) 23 | 24 | result := fn(n) 25 | 26 | log.Println("Execution is completed with the result", result) 27 | 28 | return result 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /structure/06_decorator/decorator_test.go: -------------------------------------------------------------------------------- 1 | package decorator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestDecorateNatureGirl(t *testing.T) { 9 | 10 | //准备天然的妹子一枚 11 | origin := &NatureGirl{faceValue: 6} 12 | 13 | fmt.Println("face looks ", origin.FaceLooks()) 14 | 15 | //只需要略施粉黛 16 | makeupGirl := NewGirlWithMakeup(origin, 2) 17 | 18 | fmt.Println("after makeup face looks ", makeupGirl.FaceLooks()) 19 | 20 | fmt.Println("girl's real face ", makeupGirl.(*GirlWithMakeups).FaceReal()) 21 | 22 | } 23 | -------------------------------------------------------------------------------- /structure/07_proxy/README.md: -------------------------------------------------------------------------------- 1 | # 代理模式 2 | 3 | 代理模式用于延迟处理操作或者在进行实际操作前后进行其它处理。 4 | 5 | 代理模式看起似乎像中介者(中间人)模式,一定要明确两者是完全的不同的模式,目标应用场景完全不同。 6 | 7 | 代理模式强调的是对一方的隐藏,对另一方则是完全透明的 8 | 代理模式,在动作上,强调的是在单向数据流下,对一方到另一方数据的在进一步转发前,会进行必要的过滤、拦截,校验等操作. 9 | 10 | 中间人模式中的两方对象是地位平等的会话关系,中间人代表的是两边,持有两边的信息,比如房东和中介,房客和中介,中间人同时代表两边. 11 | 12 | 现实生活中,代记账公司,代注册工程是最形象的代理模式之一,当你想注册公司的时候,不要自己直接跟工商局打交道,搞各种材料,交给代理公司,代理公司会替你做这些乱七八糟的时候,然后给你一个结果。 13 | 14 | ## 在我们的应用系统中常见的代理有 15 | 16 | + nginx流量代理(反向) 17 | + 上网代理(正向) 18 | + 防火墙代理(保护内部) 19 | 20 | 对于被代理的的一侧,往往都是需要针对代理进行一定配置,对另一层是透明的,代注公司代理用户,需要需要针对用户做一些配置和了解。对于工商局来说,工作过程中,代理可以认为是透明的,本质上,工商局只是在和用户(的信息)打交道. 21 | -------------------------------------------------------------------------------- /structure/07_proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import "errors" 4 | 5 | //////////////////////////////// 6 | //用一个代理注册公司的例子,正向代理 7 | //////////////////////////////// 8 | 9 | //Nation 国籍 10 | type Nation int 11 | 12 | //nation catalog 13 | const ( 14 | CN Nation = iota 15 | UK 16 | JP 17 | ) 18 | 19 | //IPioneer 一个创业者,要注册公司 20 | type IPioneer interface { 21 | RegisterCompany(companyName string) (name, enterpriseNo string, err error) 22 | 23 | Conndition() (money int, kind Nation) 24 | } 25 | 26 | //Pioneer 一个创业者 27 | type Pioneer struct { 28 | AccountMoney int 29 | NationKind Nation 30 | } 31 | 32 | //RegisterCompany 创业者要注册公司 33 | func (p Pioneer) RegisterCompany(companyName string) (name, enterpriseNo string, err error) { 34 | 35 | return 36 | } 37 | 38 | //Conndition 注册条件 39 | func (p Pioneer) Conndition() (money int, kind Nation) { 40 | money = p.AccountMoney 41 | kind = p.NationKind 42 | return 43 | } 44 | 45 | //RegistryProxyCompany 代注公司,帮用户注册,对用户来讲,可以当成工商局来看待. 46 | type RegistryProxyCompany struct { 47 | p IPioneer 48 | } 49 | 50 | //RegisterCompany 代表用户注册公司 51 | func (r RegistryProxyCompany) RegisterCompany(companyName string) (name, enterpriseNo string, err error) { 52 | 53 | //检查注册人的,资金,姓名, 54 | money, nation := r.p.Conndition() 55 | 56 | if money < 10000 || nation != CN { 57 | return "", "", errors.New("Condition not OK") 58 | } 59 | //////////////////////////////// 60 | ///发送请求到工商局 61 | //////////////////////////////// 62 | 63 | name = companyName 64 | enterpriseNo = "abvdefe12450" 65 | 66 | return name, enterpriseNo, err 67 | 68 | } 69 | -------------------------------------------------------------------------------- /structure/07_proxy/proxy_test.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestProxy(t *testing.T) { 8 | 9 | proxyCompany := RegistryProxyCompany{p: Pioneer{10000, UK}} 10 | 11 | //想注册一个叫fire company 的公司 12 | name, no, err := proxyCompany.RegisterCompany("fire company") 13 | 14 | if err != nil { 15 | t.Log("something wrong:", err.Error()) 16 | } else { 17 | 18 | t.Log("got company, name:", name, "company no:", no) 19 | 20 | } 21 | 22 | proxyCompany = RegistryProxyCompany{p: Pioneer{109999, CN}} 23 | 24 | //想注册一个叫fire company 的公司 25 | name, no, err = proxyCompany.RegisterCompany("water company") 26 | 27 | if err != nil { 28 | t.Log("something wrong:", err.Error()) 29 | } else { 30 | 31 | t.Log("got company, name:", name, "company no:", no) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /structure/README.md: -------------------------------------------------------------------------------- 1 | # structure pattern 2 | 3 | 结构模式,就是多种对象之间的相互搭配/连接构成一个新的结构形态,从而满足一定目的,比如方便访问,扩展功能,对象复用,功能转换适配等 4 | 建筑结构是一个很生动鲜明的结构模式代表,同样是钢筋、混凝土,建筑的不同结构,标示着这些建筑的不同作用,做成桥的结构,就是支持行人过路的,做成房子就是住人的, 5 | 最好脑子里面形成不同建筑结构的画面,这个是最容易跟结构产生联系的一个形态对象,也是对结构模式的最佳描述之一。 6 | 7 | 所以,代码也一样,不同的对象构成的不同形态,就意味着这些对象是有不同的目的设计. 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------