Repository: qiualiang/gof Branch: master Commit: 99c1c6f07fb4 Files: 55 Total size: 66.1 KB Directory structure: gitextract_zsi6xebf/ ├── .gitignore ├── README.md ├── go.mod └── src/ ├── behaviour/ │ ├── command/ │ │ ├── command.go │ │ └── 命令模式/ │ │ └── 命令模式.md │ ├── interpreter/ │ │ ├── interpreter.go │ │ └── 解释器模式/ │ │ └── 解释器模式.md │ ├── iterator/ │ │ ├── iterator.go │ │ └── 迭代器模式/ │ │ └── 迭代器模式.md │ ├── mediator/ │ │ ├── mediator.go │ │ └── 中介者模式/ │ │ └── 中介者模式.md │ ├── memento/ │ │ ├── memento.go │ │ └── 备忘录模式/ │ │ └── 备忘录模式.md │ ├── observer/ │ │ ├── observer.go │ │ └── 观察者模式/ │ │ └── 观察者模式.md │ ├── responsibility/ │ │ ├── responsibility.go │ │ └── 责任链模式/ │ │ └── 责任链模式.md │ ├── state/ │ │ ├── state.go │ │ └── 状态模式/ │ │ └── 状态模式.md │ ├── strategy/ │ │ ├── strategy.go │ │ └── 策略模式/ │ │ └── 策略模式.md │ ├── template/ │ │ ├── temlpate.go │ │ └── 模板模式/ │ │ └── 模板模式.md │ └── visitor/ │ ├── visitor.go │ └── 访问者模式/ │ └── 访问者模式.md ├── creator/ │ ├── builder/ │ │ ├── builder.go │ │ └── 建造者模式/ │ │ └── 建造者模式 Builder Pattern.md │ ├── factory/ │ │ ├── abstract/ │ │ │ ├── abstract.go │ │ │ └── 抽象工厂方法/ │ │ │ └── 抽象工厂方法.md │ │ ├── method/ │ │ │ ├── method.go │ │ │ └── 工厂方法/ │ │ │ └── 工厂方法.md │ │ └── simple/ │ │ ├── simple.go │ │ └── 简单工厂模式/ │ │ └── 简单工厂模式.md │ ├── prototype/ │ │ ├── prototype.go │ │ └── 原型模式/ │ │ └── 原型模式.md │ └── singleton/ │ ├── hungry/ │ │ └── hungry.go │ ├── lazy/ │ │ └── lazy.go │ ├── once/ │ │ └── once.go │ └── 单例模式/ │ └── 单例模式.md └── structure/ ├── adapter/ │ ├── demo1/ │ │ └── adapter.go │ ├── demo2/ │ │ └── adapter.go │ └── 适配器模式/ │ └── 适配器模式.md ├── bridge/ │ ├── bridge.go │ └── 桥接模式/ │ └── 桥接模式.md ├── composite/ │ ├── safe/ │ │ └── safe.go │ ├── transparent/ │ │ └── transparent.go │ └── 组合模式/ │ └── 组合模式.md ├── decorator/ │ ├── decorator.go │ └── 装饰器模式/ │ └── 装饰器模式.md ├── facade/ │ ├── facade.go │ └── 外观模式/ │ └── 外观模式.md ├── flyweight/ │ ├── flyweight.go │ └── 享元模式/ │ └── 享元模式.md └── proxy/ ├── proxy.go └── 代理模式/ └── 代理模式.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .vscode ================================================ FILE: README.md ================================================ # GoF 设计模式 GoF所提出的23种设计模式主要基于以下面向对象设计原则: 1. 对接口编程而不是对实现编程 2. 优先使用对象组合而不是继承 23种设计模式分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns) 创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。 创建型模式分为以下几种。 * 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。 * 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。 * 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。 * 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。 * 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。 以上 5 种创建型模式,除了工厂方法模式属于类创建型模式,其他的全部属于对象创建型模式。 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: 1. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。 2. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。 3. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。 4. 装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。 5. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。 6. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。 7. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。 以上 7 种结构型模式,除了适配器模式分为类结构型模式和对象结构型模式两种,其他的全部属于对象结构型模式。 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式是 GoF 设计模式中最为庞大的一类,它包含以下 11 种模式。 1. 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 2. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。 3. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。 4. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。 5. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。 6. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。 7. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。 8. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。 9. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。 10. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。 11. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。 以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。 ================================================ FILE: go.mod ================================================ module gof go 1.15 ================================================ FILE: src/behaviour/command/command.go ================================================ package main import ( "fmt" ) type Stock struct { name string quantity int } func (stock *Stock) Set(name string, quantity int) { stock.name = name stock.quantity = quantity } func (stock *Stock) Buy(s Stock) { stock.name = s.name stock.quantity = s.quantity fmt.Printf("Buy stock %s, quantity:%d \n", s.name, s.quantity) } func (stock Stock) Sell(s Stock) { stock.name = s.name stock.quantity = s.quantity fmt.Printf("Sell stock %s, quantity:%d \n", s.name, s.quantity) } type Order interface { Execute() } type BuyStock struct { stock Stock } func (buy BuyStock) Execute() { fmt.Println("execute in buy command") buy.stock.Buy(buy.stock) } type SellStock struct { stock Stock } func (sell SellStock) Execute() { fmt.Println("execute in sell command") sell.stock.Sell(sell.stock) } type Broker struct { order Order } func (broker *Broker) SetOrder(order Order) { fmt.Printf("set order:%v\n", order) broker.order = order } func (broker Broker) Call() { fmt.Println("call in broker") broker.order.Execute() } func main() { stock := new(Stock) stock.Set("icbc", 200) var order Order order = BuyStock{*stock} broker := new(Broker) broker.SetOrder(order) broker.Call() order = SellStock{*stock} broker.SetOrder(order) broker.Call() } ================================================ FILE: src/behaviour/command/命令模式/命令模式.md ================================================ # 命令模式 命令模式的定义与特点 ---------- 命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。 命令模式的主要优点如下。 1. 降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。 2. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。 3. 可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。 4. 方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录结合,实现命令的撤销与恢复。 其缺点是:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。 命令模式的结构与实现 ---------- 可以将系统中的相关操作抽象成命令,使调用者与实现者相关分离,其结构如下。 #### 1\. 模式的结构 命令模式包含以下主要角色。 1. 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。 2. 具体命令角色(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。 3. 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。 4. 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。 其结构图如图 1 所示。 ![命令模式的结构图](resources/284C0E86A16A5B751FF72C75C8A5175E.gif) 图1 命令模式的结构图 命令模式的应用实例 --------- 【例】用“命令模式”买卖股票。 我们首先创建作为命令的接口 *Order*,然后创建作为请求的 *Stock* 类。实体命令类 *BuyStock* 和 *SellStock*,实现了 *Order* 接口,将执行实际的命令处理。创建作为调用对象的类 *Broker*,它接受订单并能下订单。 *Broker* 对象使用命令模式,基于命令的类型确定哪个对象执行哪个命令。*CommandPatternDemo*,我们的演示类使用 *Broker* 类来演示命令模式。 结构图如图2 所示。示例代码如command.go所示。 ![](resources/F945FDE0672E6B0FD3ED939D36406E3D.jpg) 图2 买卖股票命令模式结构图 ================================================ FILE: src/behaviour/interpreter/interpreter.go ================================================ package main import ( "fmt" "strings" ) type Expression interface { // SetData(data []string) Interpret(info string) bool } type TerminalExpression struct { nodes map[string]string } func (te *TerminalExpression) SetData(data []string) { te.nodes = make(map[string]string) for _, v := range data { te.nodes[v] = v } } func (te *TerminalExpression) Interpret(ifo string) bool { if _, ok := te.nodes[ifo]; ok { return true } return false } type AndExpression struct { city Expression people Expression } func (and *AndExpression) Interpret(info string) bool { ss := strings.Split(info, "的") return and.city.Interpret(ss[0]) && and.people.Interpret(ss[1]) } type Context struct { cities []string people []string cityPerson Expression } func (cx *Context) Init() { cx.cities = []string{"广州", "深圳"} cx.people = []string{"老人", "妇女", "儿童"} // var city, people Expression city := new(TerminalExpression) people := new(TerminalExpression) city.SetData(cx.cities) people.SetData(cx.people) cx.cityPerson = &AndExpression{city, people} } func (cx Context) FreeRide(info string) { if ok := cx.cityPerson.Interpret(info); ok { fmt.Println("您是" + info + ",您本次乘车免费") } else { fmt.Println(info + ",乘车扣费2元.") } } func main() { bus := new(Context) bus.Init() bus.FreeRide("广州的老人") bus.FreeRide("广州的妇女") bus.FreeRide("广州的年轻人") bus.FreeRide("广州的儿童") bus.FreeRide("深圳的老人") bus.FreeRide("河北的老人") } ================================================ FILE: src/behaviour/interpreter/解释器模式/解释器模式.md ================================================ # 解释器模式 模式的定义与特点 -------- 解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。 这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。 解释器模式是一种类行为型模式,其主要优点如下。 1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。 2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。 解释器模式的主要缺点如下。 1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。 2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。 3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。 模式的结构与实现 -------- 解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。 #### 1) 文法 文法是用于描述语言的语法结构的形式规则。没有规矩不成方圆,例如,有些人认为完美爱情的准则是“相互吸引、感情专一、任何一方都没有恋爱经历”,虽然最后一条准则较苛刻,但任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。例如,中文中的“句子”的文法如下。 〈句子〉::=〈主语〉〈谓语〉〈宾语〉 〈主语〉::=〈代词〉|〈名词〉 〈谓语〉::=〈动词〉 〈宾语〉::=〈代词〉|〈名词〉 〈代词〉你|我|他 〈名词〉7大学生I筱霞I英语 〈动词〉::=是|学习 注:这里的符号“::=”表示“定义为”的意思,用“〈”和“〉”括住的是非终结符,没有括住的是终结符。 #### 2) 句子 句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。例如,上述文法可以推出“我是大学生”,所以它是句子。 #### 3) 语法树 语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。图 1 所示是“我是大学生”的语法树。 ![句子“我是大学生”的语法树](resources/F1E6EE3AF31E730072CF28F28110A99E.gif) 图1 句子“我是大学生”的语法树 有了以上基础知识,现在来介绍解释器模式的结构就简单了。解释器模式的结构与[组合模式](http://c.biancheng.net/view/1373.html)相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。 #### 1\. 模式的结构 解释器模式包含以下主要角色。 1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。 2. 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。 3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。 4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。 5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。 解释器模式的结构图如图 2 所示。 ![解释器模式的结构图](resources/EF3294C080E4147CF771C49827C289E8.gif) 图2 解释器模式的结构图 #### 2\. 模式的实现 解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树。 原型模式的应用实例 --------- 【例】用解释器模式设计一个“韶粵通”公交车卡的读卡器程序。 说明:假如“粤通卡”公交车读卡器可以判断乘客的身份,如果是“深圳”或者“广州”的“老人” “妇女”“儿童”就可以免费乘车,其他人员乘车一次扣 2 元。 分析:本实例用“解释器模式”设计比较适合,首先设计其文法规则如下。 ::= ::= 深圳|广州 ::= 老人|妇女|儿童 然后,根据文法规则按以下步骤设计公交车卡的读卡器程序的类图。 * 定义一个抽象表达式(Expression)接口,它包含了解释方法 interpret(String info)。 * 定义一个终结符表达式(Terminal Expression)类,它用集合(Set)类来保存满足条件的城市或人,并实现抽象表达式接口中的解释方法 interpret(Stringinfo),用来判断被分析的字符串是否是集合中的终结符。 * 定义一个非终结符表达式(AndExpressicm)类,它也是抽象表达式的子类,它包含满足条件的城市的终结符表达式对象和满足条件的人员的终结符表达式对象,并实现 interpret(String info) 方法,用来判断被分析的字符串是否是满足条件的城市中的满足条件的人员。 * 最后,定义一个环境(Context)类,它包含解释器需要的数据,完成对终结符表达式的初始化,并定义一个方法 freeRide(String info) 调用表达式对象的解释方法来对被分析的字符串进行解释。其结构图如图 3 所示。 ![“韶粵通”公交车读卡器程序的结构图](resources/AAC83BEBEDC20138A8253A6FBE75A150.gif) 图3 “韶粵通”公交车读卡器程序的结构图 程序代码如interpreter.go所示。 ================================================ FILE: src/behaviour/iterator/iterator.go ================================================ package main import ( "fmt" ) type Iterator interface { First() interface{} Next() interface{} HasNext() bool } type Aggregate interface { Add(obj interface{}) Remove(obj interface{}) GetIterator() Iterator } type ConcreteIterator struct { objs []interface{} index int } func (it *ConcreteIterator) Init(objs []interface{}) { it.index = -1 it.objs = make([]interface{}, len(objs)) copy(it.objs, objs) } func (it *ConcreteIterator) First() interface{} { it.index = 0 obj := it.objs[it.index] return obj } func (it *ConcreteIterator) HasNext() bool { if it.index < len(it.objs)-1 { return true } return false } func (it *ConcreteIterator) Next() interface{} { if it.HasNext() { it.index += 1 return it.objs[it.index] } return nil } type ConcreteAggregate struct { objs []interface{} } func (a *ConcreteAggregate) Add(obj interface{}) { if a.objs == nil { a.objs = make([]interface{}, 0) } a.objs = append(a.objs, obj) } func (a *ConcreteAggregate) Remove(obj interface{}) { if a.objs == nil { return } for i, v := range a.objs { if v == obj { a.objs = append(a.objs[0:i], a.objs[i+1:]...) } } } func (a *ConcreteAggregate) GetIterator() Iterator { it := new(ConcreteIterator) it.Init(a.objs) return it } func main() { var ag Aggregate = new(ConcreteAggregate) ag.Add("first") ag.Add("second") ag.Add("third") var it Iterator = ag.GetIterator() for it.HasNext() { obj := it.Next() fmt.Println(obj.(string)) } obj := it.First() fmt.Println("The firs one:", obj.(string)) } ================================================ FILE: src/behaviour/iterator/迭代器模式/迭代器模式.md ================================================ # 迭代器模式 模式的定义与特点 -------- 迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式,其主要优点如下。 1. 访问一个聚合对象的内容而无须暴露它的内部表示。 2. 遍历任务交由迭代器完成,这简化了聚合类。 3. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。 4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。 5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。 其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。 模式的结构与实现 -------- 迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。现在我们来分析其基本结构与实现方法。 #### 1\. 模式的结构 迭代器模式主要包含以下角色。 1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。 2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。 3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。 4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。 其结构图如图 1 所示。 ![迭代器模式的结构图](resources/56808E8226689515EF81BAA78F6543E6.gif) 图1 迭代器模式的结构图 #### 2\. 模式的实现 示例代码如iterator.go所示。 ================================================ FILE: src/behaviour/mediator/mediator.go ================================================ package main import ( "fmt" ) type Mediator interface { Register(cl Colleague) Reply(cl Colleague) } type ConcreteMediator struct { colleagues []Colleague } func (mediator *ConcreteMediator) Register(cl Colleague) { if !mediator.Exist(cl) { mediator.colleagues = append(mediator.colleagues, cl) cl.SetMediator(mediator) } } func (mediator *ConcreteMediator) Reply(cl Colleague) { for _, v := range mediator.colleagues { if v != cl { v.Receive() } } } func (mediator *ConcreteMediator) Exist(cl Colleague) bool { for _, v := range mediator.colleagues { if v == cl { return true } } return false } type Colleague interface { SetMediator(m Mediator) Receive() Send() } type ConcreteColleague1 struct { mediator Mediator } func (cl *ConcreteColleague1) SetMediator(m Mediator) { cl.mediator = m } func (cl *ConcreteColleague1) Receive() { fmt.Println("concrete colleague 1 receive message") } func (cl *ConcreteColleague1) Send() { fmt.Println("concrete colleague 1 sendmessage") cl.mediator.Reply(cl) } type ConcreteColleague2 struct { mediator Mediator } func (cl *ConcreteColleague2) SetMediator(m Mediator) { cl.mediator = m } func (cl *ConcreteColleague2) Receive() { fmt.Println("concrete colleague 2 receive message") } func (cl *ConcreteColleague2) Send() { fmt.Println("concrete colleague 2 sendmessage") cl.mediator.Reply(cl) } func main() { var md Mediator = &ConcreteMediator{colleagues: make([]Colleague, 0)} var c1, c2 Colleague c1 = new(ConcreteColleague1) c2 = new(ConcreteColleague2) md.Register(c1) md.Register(c2) c1.Send() c2.Send() } ================================================ FILE: src/behaviour/mediator/中介者模式/中介者模式.md ================================================ # 中介者模式 模式的定义与特点 -------- 中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。 中介者模式是一种对象行为型模式,其主要优点如下。 1. 降低了对象之间的耦合性,使得对象易于独立地被复用。 2. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。 其主要缺点是:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。 模式的结构与实现 -------- 中介者模式实现的关键是找出“中介者”,下面对它的结构和实现进行分析。 #### 1\. 模式的结构 中介者模式包含以下主要角色。 1. 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。 2. 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。 3. 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。 4. 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。 中介者模式的结构图如图 1 所示。 ![中介者模式的结构图](resources/2757530B097845DD5D611DDDFB5B5697.gif) 图1 中介者模式的结构图 #### 2\. 模式的实现 中介者模式的实现代码如mediator.go 所示。 ================================================ FILE: src/behaviour/memento/memento.go ================================================ package main import ( "fmt" ) type Originator struct { state string } type Memento struct { state string } func (mem *Memento) SetState(state string) { mem.state = state } func (mem Memento) GetState() string { return mem.state } func (originator *Originator) SetState(state string) { originator.state = state } func (originator Originator) GetState() string { return originator.state } func (originator *Originator) CreateMemento() Memento { return Memento{state: originator.state} } func (originator *Originator) RestoreMemento(mem Memento) { originator.SetState(mem.GetState()) } // The manager type Caretaker struct { memento Memento } func (caretaker *Caretaker) SetMemento(mem Memento) { caretaker.memento = mem } func (caretaker Caretaker) GetMemento() Memento { return caretaker.memento } func main() { or := new(Originator) cr := new(Caretaker) or.SetState("S0") fmt.Println("The original state:", or.GetState()) cr.SetMemento(or.CreateMemento()) // save state or.SetState("s1") fmt.Println("The new state:", or.GetState()) or.RestoreMemento(cr.GetMemento()) // restore original state fmt.Println("After restore,the state is:", or.GetState()) } ================================================ FILE: src/behaviour/memento/备忘录模式/备忘录模式.md ================================================ # 备忘录模式 模式的定义与特点 -------- 备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。 备忘录模式是一种对象行为型模式,其主要优点如下。 * 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。 * 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。 * 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。 其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。 模式的结构与实现 -------- 备忘录模式的核心是设计备忘录类以及用于管理备忘录的管理者类,现在我们来学习其结构与实现。 #### 1\. 模式的结构 备忘录模式的主要角色如下。 1. 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。 2. 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。 3. 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。 备忘录模式的结构图如图 1 所示。 ![备忘录模式的结构图](resources/4A51A7F52DEAE1D6F18AF105855A4499.gif) 图1 备忘录模式的结构图 #### 2\. 模式的实现 备忘录模式的实现代码如memento.go 所示。 ================================================ FILE: src/behaviour/observer/observer.go ================================================ package main import ( "fmt" ) type Subject interface { Add(ob Observer) Remove(ob Observer) NotifyObserver() } type Observer interface { Response() } type ConcreteSubject struct { observers []Observer } func (cs *ConcreteSubject) Add(ob Observer) { if cs.observers == nil { cs.observers = make([]Observer, 0) } cs.observers = append(cs.observers, ob) } func (cs *ConcreteSubject) Remove(ob Observer) { for i, v := range cs.observers { if ob == v { cs.observers = append(cs.observers[:i], cs.observers[i+1:]...) } } } func (cs *ConcreteSubject) NotifyObserver() { fmt.Println("Subject change,notify the observers!") for _, v := range cs.observers { v.Response() } } type ConcreteObserver1 struct { } func (ob *ConcreteObserver1) Response() { fmt.Println("concrete observer 1 response.") } type ConcreteObserver2 struct { } func (ob *ConcreteObserver2) Response() { fmt.Println("concrete observer 2 response.") } func main() { var subject Subject = new(ConcreteSubject) obs1 := new(ConcreteObserver1) obs2 := new(ConcreteObserver2) subject.Add(obs1) subject.Add(obs2) // subject.Remove(obs2) subject.NotifyObserver() } ================================================ FILE: src/behaviour/observer/观察者模式/观察者模式.md ================================================ # 观察者模式 模式的结构与实现 -------- 实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。 #### 1\. 模式的结构 观察者模式的主要角色如下。 1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。 2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。 3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。 4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。 观察者模式的结构图如图 1 所示。 ![观察者模式的结构图](resources/4AD3E0B3C15D542518AA433ED9A8C640.gif) 图1 观察者模式的结构图 #### 2\. 模式的实现 观察者模式的实现代码如observer.go所示。 ================================================ FILE: src/behaviour/responsibility/responsibility.go ================================================ package main import ( "fmt" ) type Handle interface { SetNext(next Handle) GetNext() Handle HandleRequest(request string) } type ConcreteHandle1 struct { next Handle } func (handle *ConcreteHandle1) SetNext(next Handle) { handle.next = next } func (handle *ConcreteHandle1) GetNext() Handle { return handle.next } func (handle *ConcreteHandle1) HandleRequest(request string) { if request == "one" { fmt.Println("concrete handler 1 response for the request") } else { if handle.GetNext() != nil { handle.GetNext().HandleRequest(request) } else { fmt.Println("No one handle the request.") } } } type ConcreteHandle2 struct { next Handle } func (handle *ConcreteHandle2) SetNext(next Handle) { handle.next = next } func (handle *ConcreteHandle2) GetNext() Handle { return handle.next } func (handle *ConcreteHandle2) HandleRequest(request string) { if request == "two" { fmt.Println("concrete handler 2 response for the request") } else { if handle.GetNext() != nil { handle.GetNext().HandleRequest(request) } else { fmt.Println("No one handle the request.") } } } func main() { var handle1, handle2 Handle handle1 = new(ConcreteHandle1) handle2 = new(ConcreteHandle2) handle1.SetNext(handle2) handle1.HandleRequest("one") handle1.HandleRequest("two") handle1.HandleRequest("three") } ================================================ FILE: src/behaviour/responsibility/责任链模式/责任链模式.md ================================================ # 责任链模式 模式的定义与特点 -------- 责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。 注意:责任链模式也叫职责链模式。 在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。 责任链模式是一种对象行为型模式,其主要优点如下。 1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。 2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。 3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。 4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。 5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。 其主要缺点如下。 1. 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。 2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。 3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。 模式的结构与实现 -------- 通常情况下,可以通过数据链表来实现职责链模式的数据结构。 #### 1\. 模式的结构 职责链模式主要包含以下角色。 1. 抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。 2. 具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。 3. 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。 其结构图如图 1 所示。客户端可按图 2 所示设置责任链。 ![责任链模式的结构图](resources/9D0F32754FB04700B1607C31D668A086.gif) 图1 责任链模式的结构图 ![责任链](resources/EEB3163883409208FB292A0497D110EC.gif) 图2 责任链 #### 2\. 模式的实现 职责链模式的实现代码如oberver.go所示。 ================================================ FILE: src/behaviour/state/state.go ================================================ package main import ( "fmt" ) type State interface { Handle(context *Context) } type ConcreteStateA struct { } func (state *ConcreteStateA) Handle(context *Context) { fmt.Println("current state is A") context.SetState(new(ConcreteStateA)) } type ConcreteStateB struct { } func (state *ConcreteStateB) Handle(context *Context) { fmt.Println("current state is B") context.SetState(new(ConcreteStateB)) } type Context struct { state State } func (context *Context) Init() { context.state = new(ConcreteStateA) } func (context *Context) SetState(state State) { context.state = state } func (context *Context) GetState() State { return context.state } func (context *Context) Handle() { context.state.Handle(context) } func main() { var context *Context = new(Context) context.Init() context.Handle() context.SetState(new(ConcreteStateB)) context.Handle() context.Init() context.Handle() } ================================================ FILE: src/behaviour/state/状态模式/状态模式.md ================================================ # 状态模式 状态模式的结构与实现 ---------- 状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。现在我们来分析其基本结构和实现方法。 #### 1\. 模式的结构 状态模式包含以下主要角色。 1. 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。 2. 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。 3. 具体状态(Concrete State)角色:实现抽象状态所对应的行为。 其结构图如图 1 所示。 ![状态模式的结构图](resources/9B1DD329D342BA4DD429602B9216A2AC.gif) 图1 状态模式的结构图 #### 2\. 模式的实现 状态模式的实现代码如state.go所示。 ================================================ FILE: src/behaviour/strategy/strategy.go ================================================ package main import ( "fmt" ) type Strategy interface { StrategyMethod() } type ConcreteStrategyA struct { } func (s ConcreteStrategyA) StrategyMethod() { fmt.Println("concrte strategy A's method is visited.") } type ConcreteStrategyB struct { } func (s ConcreteStrategyB) StrategyMethod() { fmt.Println("concrte strategy B's method is visited.") } type Context struct { strategy Strategy } func (cx *Context) SetSrategy(strategy Strategy) { cx.strategy = strategy } func (cx *Context) GetSrategy() Strategy { return cx.strategy } func (cx *Context) StrategyMethod() { cx.strategy.StrategyMethod() } func main() { context := new(Context) var s Strategy = new(ConcreteStrategyA) context.SetSrategy(s) context.StrategyMethod() fmt.Println("================================") s = new(ConcreteStrategyB) context.SetSrategy(s) context.StrategyMethod() } ================================================ FILE: src/behaviour/strategy/策略模式/策略模式.md ================================================ # 策略模式 策略模式的定义与特点 ---------- 策略(Strategy)模式的定义:该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。 策略模式的主要优点如下。 1. 多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句。 2. 策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码。 3. 策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的。 4. 策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。 5. 策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。 其主要缺点如下。 1. 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。 2. 策略模式造成很多的策略类。 策略模式的结构与实现 ---------- 策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性,现在我们来分析其基本结构和实现方法。 #### 1\. 模式的结构 策略模式的主要角色如下。 1. 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。 2. 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。 3. 环境(Context)类:持有一个策略类的引用,最终给客户端调用。 其结构图如图 1 所示。 ![策略模式的结构图](resources/75C91DE2FAB6A2E39E9EE7CE3C79CCD4.gif) 图1 策略模式的结构图 #### 2\. 模式的实现 策略模式的实现代码如strategy.go所示。 ================================================ FILE: src/behaviour/template/temlpate.go ================================================ package main import "fmt" type AbstractClass interface { TemplateMethod() SpecificMethod() AbstractMethod1() AbstractMethod2() } type ConcreteClass struct { Specific AbstractClass } func (cc *ConcreteClass) AbstractMethod1() { if cc.Specific == nil { return } cc.Specific.AbstractMethod1() } func (cc *ConcreteClass) AbstractMethod2() { if cc.Specific == nil { return } cc.Specific.AbstractMethod2() } func (cc *ConcreteClass) SpecificMethod() { fmt.Println("running specificMethod") } func (cc *ConcreteClass) TemplateMethod() { cc.SpecificMethod() cc.AbstractMethod1() cc.AbstractMethod2() } type ConcreteA struct { ConcreteClass } func (_ *ConcreteA) AbstractMethod1() { fmt.Println("running abstract method 1 of concretA..") } func (_ *ConcreteA) AbstractMethod2() { fmt.Println("running abstract method 2 of concretA..") } type ConcreteB struct { ConcreteClass } func (_ *ConcreteB) AbstractMethod1() { fmt.Println("running abstract method 1 of concretB..") } func (_ *ConcreteB) AbstractMethod2() { fmt.Println("running abstract method 2 of concretB..") } func main() { var p *ConcreteClass = &ConcreteClass{} fmt.Println("the object is A") p.Specific = &ConcreteA{} p.TemplateMethod() fmt.Println("=========================") fmt.Println("the object is B") p.Specific = &ConcreteB{} p.TemplateMethod() } ================================================ FILE: src/behaviour/template/模板模式/模板模式.md ================================================ # 模板模式 模式的定义与特点 -------- 模板方法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。 该模式的主要优点如下。 1. 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。 2. 它在父类中提取了公共的部分代码,便于代码复用。 3. 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。 该模式的主要缺点如下。 1. 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。 2. 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。 模式的结构与实现 -------- 模板方法模式需要注意抽象类与具体子类之间的协作。它用到了虚函数的多态性技术以及“不用调用我,让我来调用你”的反向控制技术。现在来介绍它们的基本结构。 #### 1\. 模式的结构 模板方法模式包含以下主要角色。 (1) 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。 ① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。 ② 基本方法:是整个算法中的一个步骤,包含以下几种类型。 * 抽象方法:在抽象类中申明,由具体子类实现。 * 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。 * 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。 (2) 具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。 模板方法模式的结构图如图 1 所示。 ![模板方法模式的结构图](resources/C07B44F8D0FA11EFAF4A07430DAE2613.gif) 图1 模板方法模式的结构图 模板模式的实现代码如template.go所示。 ================================================ FILE: src/behaviour/visitor/visitor.go ================================================ package main import ( "fmt" ) type Visitor interface { VisitA(a *ConcreteElementA) VisitB(b *ConcreteElementB) } type VisitorA struct { } type VisitorB struct { } type Element interface { Accept(visitor Visitor) } func (visotor VisitorA) VisitA(a *ConcreteElementA) { fmt.Println("Visitor A visit element a") a.OperationA() } func (visotor VisitorA) VisitB(b *ConcreteElementB) { fmt.Println("Visitor A visit element b") b.OperationB() } func (visotor VisitorB) VisitA(a *ConcreteElementA) { fmt.Println("Visitor B visit element a") a.OperationA() } func (visotor VisitorB) VisitB(b *ConcreteElementB) { fmt.Println("Visitor B visit element b") b.OperationB() } type ConcreteElementA struct { } func (e *ConcreteElementA) Accept(visitor Visitor) { visitor.VisitA(e) } func (e *ConcreteElementA) OperationA() { fmt.Println(" operation on A") } type ConcreteElementB struct { } func (e *ConcreteElementB) Accept(visitor Visitor) { visitor.VisitB(e) } func (e *ConcreteElementB) OperationB() { fmt.Println(" operation on B") } type ObjectStructure struct { elements []Element } func (o *ObjectStructure) Accept(visitor Visitor) { for _, v := range o.elements { v.Accept(visitor) } } func (o *ObjectStructure) Add(e Element) { if o.elements == nil { o.elements = []Element{} } o.elements = append(o.elements, e) } func (o *ObjectStructure) Remove(e Element) { if o.elements == nil { return } for i, v := range o.elements { if v == e { o.elements = append(o.elements[0:i], o.elements[i+1:]...) } } } func main() { obj := new(ObjectStructure) obj.Add(new(ConcreteElementA)) obj.Add(new(ConcreteElementB)) var visitor Visitor = VisitorA{} obj.Accept(visitor) fmt.Println("===========================") visitor = VisitorB{} obj.Accept(visitor) } ================================================ FILE: src/behaviour/visitor/访问者模式/访问者模式.md ================================================ # 访问者模式 模式的定义与特点 -------- 访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。 访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。 1. 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。 2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。 3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。 4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。 访问者(Visitor)模式的主要缺点如下。 1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。 2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。 3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。 模式的结构与实现 -------- 访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类,其基本结构与实现方法如下。 #### 1\. 模式的结构 访问者模式包含以下主要角色。 1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。 2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。 3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。 4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。 5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。 其结构图如图 1 所示。 [![访问者(Visitor)模式的结构图](resources/4419D3126B0FFF5E21F1A5A3233F4F9E.gif) 图1 访问者(Visitor)模式的结构图 #### 2\. 模式的实现 访问者模式的实现代码如visitor.go所示。 ================================================ FILE: src/creator/builder/builder.go ================================================ package main import ( "fmt" ) type Parlour struct { wall string tv string sofa string } func (p *Parlour) SetTV(tv string) { p.tv = tv } func (p *Parlour) SetWall(wall string) { p.wall = wall } func (p *Parlour) SetSofa(sofa string) { p.sofa = sofa } func (p *Parlour) Show() { fmt.Printf("wall:%s,tv:%s,sofa:%s\n", p.wall, p.tv, p.sofa) } type Decorator interface { BuildWall() BuildTV() BuildSofa() GetResult() *Parlour } type ConcreteDecorator1 struct { parlour *Parlour } func (cd1 *ConcreteDecorator1) SetParlour(parlour *Parlour) { cd1.parlour = parlour fmt.Println("set the parlour") } func (cd1 ConcreteDecorator1) BuildWall() { cd1.parlour.SetWall("build by builder1") fmt.Println("Wall built by cd1!") } func (cd1 ConcreteDecorator1) BuildTV() { cd1.parlour.SetTV("build by builder1") fmt.Println("TV built by cd1!") } func (cd1 ConcreteDecorator1) BuildSofa() { cd1.parlour.SetSofa("build by builder1") fmt.Println("Sofa built by cd1!") } func (cd1 ConcreteDecorator1) GetResult() *Parlour { return cd1.parlour } type ConcreteDecorator2 struct { parlour *Parlour } func (cd2 *ConcreteDecorator2) SetParlour(parlour *Parlour) { cd2.parlour = parlour fmt.Println("Set the parlour") } func (cd2 ConcreteDecorator2) BuildWall() { cd2.parlour.SetWall("build by builder2") fmt.Println("Wall built by cd2!") } func (cd2 ConcreteDecorator2) BuildTV() { cd2.parlour.SetTV("build by builder2") fmt.Println("TV built by cd2!") } func (cd2 ConcreteDecorator2) BuildSofa() { cd2.parlour.SetSofa("build by builder2") fmt.Println("Sofa built by cd2!") } func (cd2 ConcreteDecorator2) GetResult() *Parlour { return cd2.parlour } type ProjectManager struct { builder Decorator } func (manager *ProjectManager) SetDecorator(builder Decorator) { manager.builder = builder } func (manager *ProjectManager) Decorate() *Parlour { builder := manager.builder builder.BuildSofa() builder.BuildTV() builder.BuildWall() return builder.GetResult() } func main() { fmt.Println("Manager ask builder1 to decorate the parlour:") builder1 := ConcreteDecorator1{parlour: new(Parlour)} manager := new(ProjectManager) manager.SetDecorator(builder1) p := manager.Decorate() p.Show() fmt.Println("Manager ask builder2 to decorate the parlour:") builder2 := ConcreteDecorator2{parlour: new(Parlour)} // manager := new(ProjectManager) manager.SetDecorator(builder2) p2 := manager.Decorate() p2.Show() } ================================================ FILE: src/creator/builder/建造者模式/建造者模式 Builder Pattern.md ================================================ # 建造者模式 Builder Pattern 模式的定义与特点 建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。 该模式的主要优点如下: 1. 封装性好,构建和表示分离。 2. 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。 3. 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。 其缺点如下: 1. 产品的组成部分必须相同,这限制了其使用范围。 2. 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。 建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。 模式的结构与实现 -------- 建造者(Builder)模式由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成,现在我们来分析其基本结构和实现方法。 #### 1\. 模式的结构 建造者(Builder)模式的主要角色如下。 1. 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。 2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。 3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。 4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。 其结构图如图 1 所示。 ![建造者模式的结构图](./resources/5F189C42F9C74E9D11777571F9DA5ADA.gif) #### 模式的应用实例 【例】用建造者(Builder)模式描述客厅装修。 分析:客厅装修是一个复杂的过程,它包含墙体的装修、电视机的选择、沙发的购买与布局等。客户把装修要求告诉项目经理,项目经理指挥装修工人一步步装修,最后完成整个客厅的装修与布局,所以本实例用建造者模式实现比较适合。 这里客厅是产品,包括墙、电视和沙发等组成部分。具体装修工人是具体建造者,他们负责装修与墙、电视和沙发的布局。项目经理是指挥者,他负责指挥装修工人进行装修。 另外,客厅类中提供了 show() 方法,可以将装修效果图显示出来。其类图如图 2 所示。实现代码详见builder.go ![客厅装修的结构图](./resources/A503AEEBA2ECA9FB30DB16F1DBB6040D.gif) 模式的应用场景 ------- 建造者模式唯一区别于工厂模式的是针对复杂对象的创建。也就是说,如果创建简单对象,通常都是使用工厂模式进行创建,而如果创建复杂对象,就可以考虑使用建造者模式。 当需要创建的产品具备复杂创建过程时,可以抽取出共性创建过程,然后交由具体实现类自定义创建流程,使得同样的创建行为可以生产出不同的产品,分离了创建与表示,使创建产品的灵活性大大增加。 建造者模式主要适用于以下应用场景: * 相同的方法,不同的执行顺序,产生不同的结果。 * 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。 * 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。 * 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。 建造者模式和工厂模式的区别 ------------- 通过前面的学习,我们已经了解了建造者模式,那么它和工厂模式有什么区别呢? * 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。 * 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样 * 关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。 * 建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。 模式的扩展 ----- 建造者(Builder)模式在应用过程中可以根据需要改变,如果创建的产品种类只有一种,只需要一个具体建造者,这时可以省略掉抽象建造者,甚至可以省略掉指挥者角色。 ================================================ FILE: src/creator/factory/abstract/abstract.go ================================================ package main import ( "flag" "fmt" "os" ) //抽象产品 type Product1 interface { Show1() } type Product2 interface { Show2() } //具体产品1-1 type ConcreteProduct11 struct { name string } func (p ConcreteProduct11) Show1() { fmt.Println("show product:", p.name) } //具体产品1-2 type ConcreteProduct12 struct { name string } func (p ConcreteProduct12) Show1() { fmt.Println("show product:", p.name) } //具体产品2-1 type ConcreteProduct21 struct { name string } func (p ConcreteProduct21) Show2() { fmt.Println("show product:", p.name) } //具体产品2-2 type ConcreteProduct22 struct { name string } func (p ConcreteProduct22) Show2() { fmt.Println("show product:", p.name) } //抽象工厂:提供产品的生产方法 type AbstractFactory interface { NewProduct1() Product1 NewProduct2() Product2 } //具体工厂1:实现产品1和2的生成方法 type ConcreteFactory1 struct { name string } func (fc ConcreteFactory1) NewProduct1() Product1 { fmt.Println("factory1 produces product1") return ConcreteProduct11{name: "product1"} } func (fc ConcreteFactory1) NewProduct2() Product2 { fmt.Println("factory1 produces product2") return ConcreteProduct21{name: "product2"} } //具体工厂2:实现产品1和2的生成方法 type ConcreteFactory2 struct { name string } func (fc ConcreteFactory2) NewProduct1() Product1 { fmt.Println("factory2 produces product1") return ConcreteProduct12{name: "product1"} } func (fc ConcreteFactory2) NewProduct2() Product2 { fmt.Println("factory2 produces product2") return ConcreteProduct22{name: "product2"} } func NewFactory(name string) AbstractFactory { switch name { case "ConcreteFactory1": return ConcreteFactory1{name: "factory1"} case "ConcreteFactory2": return ConcreteFactory2{name: "factory2"} default: fmt.Fprintf(os.Stderr, "Unsupported factory:%s\n", factory) } return nil } var factory string func init() { flag.StringVar(&factory, "factory", "ConcreteFactory1", "The concrete factory name.") // flag.StringVar(&product, "product", "Product1", "The concrete product name of factory") } func main() { flag.Parse() var p1 Product1 var p2 Product2 var fc AbstractFactory if fc = NewFactory(factory); fc != nil { p1 = fc.NewProduct1() p1.Show1() p2 = fc.NewProduct2() p2.Show2() } } ================================================ FILE: src/creator/factory/abstract/抽象工厂方法/抽象工厂方法.md ================================================ # 抽象工厂方法 模式的定义与特点 -------- 抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。 抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。 使用抽象工厂模式一般要满足以下条件。 * 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。 * 系统一次只可能消费其中某一族产品,即同族的产品一起使用。 抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。 * 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。 * 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。 * 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。 其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。 模式的结构与实现 -------- 抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。 #### 1\. 模式的结构 抽象工厂模式的主要角色如下。 1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。 2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。 3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。 4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。 抽象工厂模式的结构图如图 2 所示。 ![抽象工厂模式的结构图](resources/8D50BEDA77A158BCAFE349BF0FBC6833.gif) #### 2\. 模式的实现 abstract.go ================================================ FILE: src/creator/factory/method/method.go ================================================ package main import ( "flag" "fmt" "os" ) //抽象产品 type Product interface { Show() } //具体产品1 type ConcreteProduct1 struct { name string } func (p1 ConcreteProduct1) Show() { fmt.Println("show product1:", p1.name) } //具体产品2 type ConcreteProduct2 struct { name string } func (p2 ConcreteProduct2) Show() { fmt.Println("show product2:", p2.name) } //抽象工厂:提供产品的生产方法 type AbstractFactory interface { NewProduct() Product } //具体工厂1:实现产品1的生成方法 type ConcreteFactory1 struct { name string } func (fc ConcreteFactory1) NewProduct() Product { fmt.Println("factory1 produces product1") return ConcreteProduct1{name: "product1"} } //具体工厂2:实现产品2的生成方法 type ConcreteFactory2 struct { name string } func (fc ConcreteFactory2) NewProduct() Product { fmt.Println("factory2 produces product2") return ConcreteProduct2{name: "product2"} } func NewFactory(name string) AbstractFactory { switch name { case "ConcreteFactory1": return ConcreteFactory1{name: "factory1"} case "ConcreteFactory2": return ConcreteFactory2{name: "factory2"} default: fmt.Fprintf(os.Stderr, "Unsupported factory:%s\n", factory) } return nil } var factory string func init() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: input the concrete factory name to produce product.\n") fmt.Fprintf(os.Stderr, "Product 1:%s\n", "ConcreteFactory1") fmt.Fprintf(os.Stderr, "Product 2:%s\n", "ConcreteFactory2") flag.PrintDefaults() } flag.StringVar(&factory, "factory", "ConcreteFactory1", "The concrete factory name.") } func main() { // flag.Usage() flag.Parse() var p Product var fc AbstractFactory if fc = NewFactory(factory); fc != nil { p = fc.NewProduct() p.Show() } } ================================================ FILE: src/creator/factory/method/工厂方法/工厂方法.md ================================================ # 工厂方法 在现实生活中社会分工越来越细,越来越专业化。各种产品有专门的工厂生产,彻底告别了自给自足的小农经济时代,这大大缩短了产品的生产周期,提高了生产效率。同样,在软件开发中能否做到软件对象的生产和使用相分离呢?能否在满足“开闭原则”的前提下,客户随意增删或改变对软件相关对象的使用呢?这就是本节要讨论的问题。 简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。 #### 优点: * 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。 * 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。 * 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。 #### 缺点: * 类的个数容易过多,增加复杂度 * 增加了系统的抽象性和理解难度 * 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。 #### 应用场景: * 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。 * 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。 * 客户不关心创建产品的细节,只关心产品的品牌 模式的结构与实现 -------- 工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。本节来分析其基本结构和实现方法。 #### 1\. 模式的结构 工厂方法模式的主要角色如下。 1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。 2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。 3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。 4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。 其结构图如图 1 所示。 ![](resources/7BB483D7E75C5D6569F4B42080B69FCF.jpg) #### 2\. 模式的实现 method.go ================================================ FILE: src/creator/factory/simple/simple.go ================================================ package main import ( "fmt" ) const ( SQUARE = "square" CIRCLE = "circle" RECTANGLE = "rectangle" ) type Shape interface { draw() } type Square struct{} type Circle struct{} type Rectangle struct{} type ShapeFactory struct{} func (s Square) draw() { fmt.Println("Draw a square") } func (c Circle) draw() { fmt.Println("Draw a circle") } func (r Rectangle) draw() { fmt.Println("Draw a rectangle") } func (factory ShapeFactory) getShape(shape string) Shape { switch shape { case RECTANGLE: return new(Rectangle) case SQUARE: return new(Square) case CIRCLE: return new(Circle) default: fmt.Println("Unsupported shape:", shape) return nil } } func main() { var s Shape factory := new(ShapeFactory) if s = factory.getShape("circle"); s != nil { s.draw() } if s = factory.getShape("square"); s != nil { s.draw() } if s = factory.getShape("rectangle"); s != nil { s.draw() } if s = factory.getShape("triangle"); s != nil { s.draw() } } ================================================ FILE: src/creator/factory/simple/简单工厂模式/简单工厂模式.md ================================================ # 简单工厂模式 现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康)。我们的项目代码同样是由简到繁一步一步迭代而来的,但对于调用者来说,却越来越简单。 在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。 > 注意:上述复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。 工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。 按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。 我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。 在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。 简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。 简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。 > “工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。 优点和缺点 ----- #### 优点: 1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。 2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。 3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。 #### 缺点: 1. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。 2. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度 3. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂 4. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。 #### 应用场景 对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。 模式的结构与实现 -------- 简单工厂模式的主要角色如下: * 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。 * 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。 * 具体产品(ConcreteProduct):是简单工厂模式的创建目标。 其结构图如下图所示。 ![](resources/54387C4360058F61AAB82FDBA0F791CE.jpg) simple.go 代码中实现的实例图如下: ![](resources/4F859962F557C8D3393C4D23D741C764.jpg) ================================================ FILE: src/creator/prototype/prototype.go ================================================ package main import ( "fmt" ) type Cloneable interface { Clone() *SunWuKong } type SunWuKong struct { real bool kind string skill []string } func (wukong SunWuKong) Clone() *SunWuKong { return &SunWuKong{ real: false, kind: wukong.kind, skill: wukong.skill, } } func main() { wukong := SunWuKong{real: true, kind: "monkey", skill: []string{"jump", "fly", "invisible"}} fake := wukong.Clone() fmt.Printf("wukong is real:%v\n", wukong.real) fmt.Printf("fake is real:%v\n", fake.real) _, ok := interface{}(wukong).(Cloneable) fmt.Println(ok) } ================================================ FILE: src/creator/prototype/原型模式/原型模式.md ================================================ # 原型模式 原型模式的定义与特点 ---------- 原型(Prototype)模式的定义如下:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。在生活中复制的例子非常多,这里不一一列举了。 #### 原型模式的优点: * Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。 * 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。 #### 原型模式的缺点: * 需要为每一个类都配置一个 clone 方法 * clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。 * 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。 原型模式的结构与实现 ---------- 由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。 #### 1\. 模式的结构 原型模式包含以下主要角色。 1. 抽象原型类:规定了具体原型对象必须实现的接口。 2. 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。 3. 访问类:使用具体原型类中的 clone() 方法来复制新的对象。 其结构图如图 1 所示。 ![](resources/F0E0B5C656C0270ED88D453C12024321.jpg) 原型模式的应用实例 --------- 【例】用原型模式模拟“孙悟空”复制自己。 分析:孙悟空拔下猴毛轻轻一吹就变出很多孙悟空,这实际上是用到了原型模式。这里的孙悟空类 SunWukong 是具体原型类,而 Cloneable 接口是抽象原型类。代码示例:prototype.go ![](resources/727D33EF99381C1F4AF8DE8D6BA3ED23.jpg) ================================================ FILE: src/creator/singleton/hungry/hungry.go ================================================ package main import ( "fmt" ) type HungrySingleton struct { name string } var instance *HungrySingleton func (singleton *HungrySingleton) String() string { return singleton.name } func (singleton *HungrySingleton) Name() string { return singleton.name } func (singleton *HungrySingleton) SetName(name string) { singleton.name = name } func (singleton *HungrySingleton) GetInstance() *HungrySingleton { return instance } func init() { instance = &HungrySingleton{name: "gina"} } func main() { fmt.Printf("original instance:%s\n", instance) instance1 := new(HungrySingleton).GetInstance() instance2 := new(HungrySingleton).GetInstance() if instance1 == instance2 { fmt.Println("instance1 is equal to instance2") } else { fmt.Println("instance1 isn't equal to instance2") } instance1.SetName("haha") fmt.Printf("after change name :instance1:%s,instance2:%s\n", instance1, instance2) } ================================================ FILE: src/creator/singleton/lazy/lazy.go ================================================ package main import ( "fmt" "sync" ) type HungrySingleton struct { name string } var instance *HungrySingleton func (singleton *HungrySingleton) String() string { return singleton.name } func (singleton *HungrySingleton) Name() string { return singleton.name } func (singleton *HungrySingleton) SetName(name string) { singleton.name = name } func (singleton *HungrySingleton) GetInstance() *HungrySingleton { var mu sync.Mutex mu.Lock() if instance == nil { fmt.Println("Creating instance!") instance = &HungrySingleton{name: "Gina"} } else { fmt.Println("Instance has been created!") } mu.Unlock() return instance } func main() { fmt.Printf("original instance:%s\n", instance) instance1 := new(HungrySingleton).GetInstance() instance2 := new(HungrySingleton).GetInstance() if instance1 == instance2 { fmt.Println("instance1 is equal to instance2") } else { fmt.Println("instance1 isn't equal to instance2") } fmt.Printf("instance1:%s,instance2:%s\n", instance1, instance2) } ================================================ FILE: src/creator/singleton/once/once.go ================================================ package main import ( "fmt" "sync" ) type HungrySingleton struct { name string } var instance *HungrySingleton var once sync.Once func (singleton *HungrySingleton) String() string { return singleton.name } func (singleton *HungrySingleton) Name() string { return singleton.name } func (singleton *HungrySingleton) SetName(name string) { singleton.name = name } func (singleton *HungrySingleton) GetInstance() *HungrySingleton { once.Do(func() { fmt.Println("Creating instance!") instance = &HungrySingleton{name: "Gina"} }) return instance } func main() { fmt.Printf("original instance:%s\n", instance) instance1 := new(HungrySingleton).GetInstance() instance2 := new(HungrySingleton).GetInstance() if instance1 == instance2 { fmt.Println("instance1 is equal to instance2") } else { fmt.Println("instance1 isn't equal to instance2") } fmt.Printf("instance1:%s,instance2:%s\n", instance1, instance2) } ================================================ FILE: src/creator/singleton/单例模式/单例模式.md ================================================ # 单例模式 在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。 单例模式的定义与特点 ---------- 单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。 在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。 单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型。J2EE 标准中的 ServletContext 和 ServletContextConfig、Spring框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。 单例模式有 3 个特点: 1. 单例类只有一个实例对象; 2. 该单例对象必须由单例类自行创建; 3. 单例类对外提供一个访问该单例的全局访问点。 单例模式的优点和缺点 ---------- 单例模式的优点: * 单例模式可以保证内存里只有一个实例,减少了内存的开销。 * 可以避免对资源的多重占用。 * 单例模式设置全局访问点,可以优化和共享资源的访问。 单例模式的缺点: * 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。 * 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。 * 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。 单例模式的应用场景 --------- 单例模式的应用场景主要有以下几个方面: * 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。 * 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。 * 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。 * 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。 * 频繁访问数据库或文件的对象。 * 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。 * 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。 单例模式的结构与实现 ---------- 单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。 下面来分析其基本结构和实现方法。 ### 1\. 单例模式的结构 单例模式的主要角色如下。 * 单例类:包含一个实例且能自行创建这个实例的类。 * 访问类:使用单例的类。 ### 2\. 单例模式的实现 Singleton 模式通常有两种实现形式。 #### 第 1 种:懒汉式单例 该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。详见代码lazy.go 第 2 种:饿汉式单例 该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。详见代码hungry.go 饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。 第 3 种:once.go 中则利用go的sync.Once 保证只执行一次创建对象的过程。 ================================================ FILE: src/structure/adapter/demo1/adapter.go ================================================ package main import ( "fmt" ) type Target interface { Request() } type Adaptee interface { SepcificRequest() } type AdapteeImpl struct { name string } func (adaptee AdapteeImpl) SepcificRequest() { fmt.Printf("适配者%s中的业务代码被调用!\n", adaptee.name) } type Adapter struct { name string adaptee Adaptee } func (adapter *Adapter) New() { adapter.adaptee = AdapteeImpl{name: "adaptee"} } func (adapter Adapter) Request() { fmt.Printf("适配器%s中的业务代码被调用!\n", adapter.name) adapter.adaptee.SepcificRequest() } func main() { fmt.Println("适配器模式测试:") var adapter Adapter = Adapter{name: "adapter"} adapter.New() var target Target = adapter target.Request() } ================================================ FILE: src/structure/adapter/demo2/adapter.go ================================================ package main import "fmt" type Motor interface { Drive() } type ElectricMotor struct { } func (em ElectricMotor) ElectricDrive() { fmt.Println("电能发动机驱动汽车!") } type OpticalMotor struct { } func (om OpticalMotor) opticalDrive() { fmt.Println("光能发动机驱动汽车!") } type ElectricAdapter struct { emotor ElectricMotor } func (ea ElectricAdapter) New() { ea.emotor = ElectricMotor{} } func (ea ElectricAdapter) Drive() { ea.emotor.ElectricDrive() } type OpticalAdapter struct { omotor OpticalMotor } func (op OpticalAdapter) New() { op.omotor = OpticalMotor{} } func (op OpticalAdapter) Drive() { op.omotor.opticalDrive() } func main() { fmt.Println("使用电能适配器") emotor := ElectricAdapter{} emotor.New() var motor Motor = emotor motor.Drive() fmt.Println("使用光能适配器") omotor := OpticalAdapter{} omotor.New() motor = omotor motor.Drive() } ================================================ FILE: src/structure/adapter/适配器模式/适配器模式.md ================================================ # 适配器模式 模式的定义与特点 适配器模式(Adapter)的定义如下:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。 该模式的主要优点如下。 * 客户端通过适配器可以透明地调用目标接口。 * 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。 * 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。 * 在很多业务场景中符合开闭原则。 其缺点是: * 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。 * 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。 模式的结构与实现 -------- 类适配器模式可采用多重继承方式实现,如 C++ 可定义一个适配器类来同时继承当前系统的业务接口和现有组件库中已经存在的组件接口;Go 不支持多继承,但可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。同时将现有组件库中的组件引入到适配器类中。 #### 1\. 模式的结构 适配器模式(Adapter)包含以下主要角色。 1. 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。 2. 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。 3. 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。 类适配器模式的结构图如图 1 所示。 ![类适配器模式的结构图](resources/E03C768B5ED2989D4AFA2F420D918D63.gif) 图1 类适配器模式的结构图 对象适配器模式的结构图如图 2 所示。 ![对象适配器模式的结构图](resources/37569A91BAD78CA8AEE4B982318D6962.gif) 图2 对象适配器模式的结构图 #### 2\. 模式的实现 示例代码如demo1/adapter.go 所示。 模式的应用实例 ------- 【例】用适配器模式(Adapter)模拟新能源汽车的发动机。 分析:新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法 electricDrive() 是用电能驱动,而光能发动机的驱动方法 opticalDrive() 是用光能驱动,它们是适配器模式中被访问的适配者。 客户端希望用统一的发动机驱动方法 drive() 访问这两种发动机,所以必须定义一个统一的目标接口 Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。 图 3 所示是其结构图。示例代码如demo2/adapter.go所示。 ![发动机适配器的结构图](resources/BCCCB5B1CC9D658E5846DCF8BDF27E2E.gif) 图3 发动机适配器的结构图 ================================================ FILE: src/structure/bridge/bridge.go ================================================ package main import ( "fmt" ) type Color interface { Color() string } type Yellow struct { } func (color Yellow) Color() string { return "yellow" } type Red struct { } func (color Red) Color() string { return "red" } type Kind interface { Kind() string } type Wallet struct { } func (wallet Wallet) Kind() string { return "wallet" } type HandBag struct { } func (handBag HandBag) Kind() string { return "hand bag" } type Bag interface { SetColor(color Color) SetKind(kind Kind) GetName() } type ChooseBag struct { color Color kind Kind } func (bag *ChooseBag) SetColor(color Color) { bag.color = color } func (bag *ChooseBag) SetKind(kind Kind) { bag.kind = kind } func (bag *ChooseBag) GetName() { fmt.Println("color:", bag.color.Color(), "kind:", bag.kind.Kind()) } func main() { var color Color = Yellow{} var kind Kind = HandBag{} var bag Bag bag = new(ChooseBag) bag.SetColor(color) bag.SetKind(kind) bag.GetName() } ================================================ FILE: src/structure/bridge/桥接模式/桥接模式.md ================================================ # 桥接模式 桥接模式的结构与实现 ---------- 可以将抽象化部分与实现化部分分开,取消二者的继承关系,改用组合关系。 #### 1\. 模式的结构 桥接(Bridge)模式包含以下主要角色。 1. 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。 2. 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。 3. 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。 4. 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。 其结构图如图 1 所示。 ![桥接模式的结构图](resources/24531B52E3B22DDF1240D923D130938A.gif) 图1 桥接模式的结构图 桥接模式的应用实例 --------- 【例】用桥接(Bridge)模式模拟女士皮包的选购。 分析:女士皮包有很多种,可以按用途分、按皮质分、按品牌分、按颜色分、按大小分等,存在多个维度的变化,所以采用桥接模式来实现女士皮包的选购比较合适。 本实例按用途分可选钱包(Wallet)和挎包(HandBag),按颜色分可选黄色(Yellow)和红色(Red)。可以按两个维度定义为颜色类和包类。 颜色类(Color)是一个维度,定义为实现化角色,它有两个具体实现化角色:黄色和红色,通过 getColor() 方法可以选择颜色;包类(Bag)是另一个维度,定义为抽象化角色,它有两个扩展抽象化角色:挎包和钱包,它包含了颜色类对象,通过 getName() 方法可以选择相关颜色的挎包和钱包。 图 2 所示是其结构图。示例代码如brideg.go所示。 ![女士皮包选购的结构图](resources/F93C59762561F53EC8C10CE3D070DB78.gif) 图2 女士皮包选购的结构图 ================================================ FILE: src/structure/composite/safe/safe.go ================================================ package main import "fmt" type Component interface { Operation() } type Leaf struct { name string } func (l *Leaf) SetName(name string) { l.name = name } func (l *Leaf) Operation() { fmt.Println("Leaf " + l.name + " visited!") } type Composite struct { children []Component } func (coms *Composite) NewChildren() { coms.children = make([]Component, 0) } func (coms *Composite) Add(c Component) { coms.children = append(coms.children, c) } func (coms *Composite) Remove(c Component) { switch c.(type) { case *Leaf: // fmt.Println("c is a leaf") children := coms.children for i, v := range children { if value, ok := v.(*Leaf); ok { if value == c { fmt.Println("c is a leaf,found!") // fmt.Println("i:", i) switch i { case 0: //at the first // fmt.Println("case first") coms.children = children[1:] case len(children) - 1: // fmt.Println("case last") coms.children = children[0 : len(children)-1] default: // fmt.Println("case middle") coms.children = append(children[:i], children[i+1:]...) } break } } else { if value, ok := v.(*Composite); ok { value.Remove(c) } } } case *Composite: // fmt.Println("c is a composite") children := coms.children for i, v := range children { if value, ok := v.(*Composite); ok { if value == c { fmt.Println("c is a composite,found!") // fmt.Println("i:", i) switch i { case 0: //at the first // fmt.Println("case first") coms.children = children[1:] case len(children) - 1: // fmt.Println("case last") coms.children = children[0 : len(children)-1] default: // fmt.Println("case middle") coms.children = append(children[:i], children[i+1:]...) } break } else { value.Remove(c) } } } default: fmt.Println("c is a component") } } func (coms *Composite) GetChild(i int) Component { return coms.children[i] } func (coms *Composite) Operation() { for _, child := range coms.children { child.Operation() } } func main() { fmt.Println("safe pattern") c0 := new(Composite) c0.NewChildren() c1 := new(Composite) c1.NewChildren() c2 := new(Composite) c2.NewChildren() l1 := Leaf{name: "1"} l2 := Leaf{name: "2"} l3 := Leaf{name: "3"} l4 := Leaf{name: "4"} l5 := Leaf{name: "5"} l6 := Leaf{name: "6"} c0.Add(&l1) c0.Add(c1) c0.Add(&l5) c0.Add(&l6) c1.Add(&l2) c1.Add(c2) c2.Add(&l3) c2.Add(&l4) c0.Operation() fmt.Println("remove.......") // c0.Remove(&l1) // c0.Remove(&l6) // c0.Remove(&l5) // c0.Remove(&l2) c0.Remove(c1) c0.Remove(&Leaf{name: "8"}) c3 := new(Composite) c3.NewChildren() c0.Remove(c3) fmt.Println("after removed:") c0.Operation() } ================================================ FILE: src/structure/composite/transparent/transparent.go ================================================ package main import "fmt" type Component interface { Add(c Component) Remove(c Component) GetChild(i int) Component Operation() } type Leaf struct { name string } func (l *Leaf) SetName(name string) { l.name = name } func (l *Leaf) Add(c Component) { } func (l *Leaf) Remove(c Component) { } func (l *Leaf) GetChild(i int) Component { return nil } func (l *Leaf) Operation() { fmt.Println("Leaf " + l.name + " visited!") } type Composite struct { children []Component } func (coms *Composite) NewChildren() { coms.children = make([]Component, 0) } func (coms *Composite) Add(c Component) { coms.children = append(coms.children, c) } func (coms *Composite) Remove(c Component) { flag := false switch c.(type) { case *Leaf: // fmt.Println("c is a leaf") children := coms.children for i, v := range children { if value, ok := v.(*Leaf); ok { if value == c { flag = true fmt.Println("c is a leaf,found!") // fmt.Println("i:", i) switch i { case 0: //at the first // fmt.Println("case first") coms.children = children[1:] case len(children) - 1: // fmt.Println("case last") coms.children = children[0 : len(children)-1] default: // fmt.Println("case middle") coms.children = append(children[:i], children[i+1:]...) } break } } else { v.Remove(c) } } case *Composite: // fmt.Println("c is a composite") children := coms.children for i, v := range children { if value, ok := v.(*Composite); ok { if value == c { fmt.Println("c is a composite,found!") flag = true // fmt.Println("i:", i) switch i { case 0: //at the first // fmt.Println("case first") coms.children = children[1:] case len(children) - 1: // fmt.Println("case last") coms.children = children[0 : len(children)-1] default: // fmt.Println("case middle") coms.children = append(children[:i], children[i+1:]...) } break } else { value.Remove(c) } } } default: fmt.Println("c is a component") } if !flag { fmt.Println("c is not found!") } } func (coms *Composite) GetChild(i int) Component { return coms.children[i] } func (coms *Composite) Operation() { for _, child := range coms.children { child.Operation() } } func main() { fmt.Println("transparent pattern") c0 := new(Composite) c0.NewChildren() c1 := new(Composite) c1.NewChildren() c2 := new(Composite) c2.NewChildren() l1 := Leaf{name: "1"} l2 := Leaf{name: "2"} l3 := Leaf{name: "3"} l4 := Leaf{name: "4"} l5 := Leaf{name: "5"} l6 := Leaf{name: "6"} c0.Add(&l1) c0.Add(c1) c0.Add(&l5) c0.Add(&l6) c1.Add(&l2) c1.Add(c2) c2.Add(&l3) c2.Add(&l4) c0.Operation() fmt.Println("remove.......") c0.Remove(&l1) // c0.Remove(&l6) // c0.Remove(&l5) // c0.Remove(&l2) // c0.Remove(c2) c0.Remove(c1) // c0.Remove(&Leaf{name: "8"}) // c3 := new(Composite) // c3.NewChildren() // c0.Remove(c3) fmt.Println("after removed:") c0.Operation() } ================================================ FILE: src/structure/composite/组合模式/组合模式.md ================================================ # 组合模式 组合模式的定义与特点 ---------- 组合(Composite)模式的定义:有时又叫作部分-整体模式,它是一种将对象组合成树状的层次结构的模式,用来表示“部分-整体”的关系,使用户对单个对象和组合对象具有一致的访问性。 组合模式的主要优点有: 1. 组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码; 2. 更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”; 其主要缺点是: 1. 设计较复杂,客户端需要花更多时间理清类之间的层次关系; 2. 不容易限制容器中的构件; 3. 不容易用继承的方法来增加构件的新功能; 组合模式的结构与实现 ---------- 组合模式的结构不是很复杂,下面对它的结构和实现进行分析。 #### 1\. 模式的结构 组合模式包含以下主要角色。 1. 抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。 2. 树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于实现抽象构件角色中 声明的公共接口。 3. 树枝构件(Composite)角色:是组合中的分支节点对象,它有子节点。它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。 组合模式分为透明式的组合模式和安全式的组合模式。 (1) 透明方式:在该方式中,由于抽象构件声明了所有子类中的全部方法,所以客户端无须区别树叶对象和树枝对象,对客户端来说是透明的。但其缺点是:树叶构件本来没有 Add()、Remove() 及 GetChild() 方法,却要实现它们(空实现或抛异常),这样会带来一些安全性问题。其结构图如图 1 所示。 ![透明式的组合模式的结构图](resources/44C5F1708C950EA16871B8D91315D0EA.gif) 图1 透明式的组合模式的结构图 (2) 安全方式:在该方式中,将管理子构件的方法移到树枝构件中,抽象构件和树叶构件没有对子对象的管理方法,这样就避免了上一种方式的安全性问题,但由于叶子和分支有不同的接口,客户端在调用时要知道树叶对象和树枝对象的存在,所以失去了透明性。其结构图如图 2 所示。 ![安全式的组合模式的结构图](resources/410F9FCB2EA38FA4ACCFEE7B55C0DA85.gif) 图2 安全式的组合模式的结构图 #### 2\. 模式的实现 假如要访问集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其对应的树状图如图 3 所示。 ![集合c0的树状图](resources/D81057B65CF20B6A251776F65CBDB8D1.gif) 图3 集合c0的树状图 透明式的组合模式的实现代码如transparent/transparent.go所示。 安全式的组合模式的实现代码如safe/safe.go所示。 ================================================ FILE: src/structure/decorator/decorator.go ================================================ package main import ( "fmt" ) type Shape interface { Draw() } type Circle struct { radius float64 } func (c Circle) Draw() { fmt.Println("Draw a circle with radius:", c.radius) } type Rectangle struct { length float64 width float64 } func (r Rectangle) Draw() { fmt.Printf("Draw a rectangle with length:%f,width:%f\n", r.length, r.width) } type ShapeDecorator interface { Shape } type RedShapeDecorator struct { S Shape } func (d RedShapeDecorator) Draw() { d.S.Draw() d.setRedBorder() } func (d RedShapeDecorator) setRedBorder() { fmt.Println("Decorator:set red border!") } func main() { var s Shape = Circle{radius: 12.11} s.Draw() fmt.Println("===========================") fmt.Println("Draw shap with decorator:") var d Shape = RedShapeDecorator{S: s} d.Draw() } ================================================ FILE: src/structure/decorator/装饰器模式/装饰器模式.md ================================================ # 装饰器模式 装饰模式的定义与特点 ---------- 装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。 装饰(Decorator)模式的主要优点有: * 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用 * 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果 * 装饰器模式完全遵守开闭原则 其主要缺点是:装饰模式会增加许多子类,过度使用会增加程序得复杂性。 装饰模式的结构与实现 ---------- 通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰模式的目标。下面来分析其基本结构和实现方法。 #### 1\. 模式的结构 装饰模式主要包含以下角色。 1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。 2. 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。 3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。 4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。 装饰模式的结构图如图 1 所示。 ![装饰模式的结构图](resources/E522782EDD431352B249572452EC36F6.gif) 图1 装饰模式的结构图 装饰模式的应用实例 --------- 【例】给绘制的图形装饰外边框颜色。 我们将创建一个 *Shape* 接口和实现了 *Shape* 接口的实体类。然后我们创建一个实现了 *Shape* 接口的抽象装饰类 *ShapeDecorator*,并把 *Shape* 对象作为它的实例变量。 *RedShapeDecorator* 是实现了 *ShapeDecorator* 的实体类。 *DecoratorPatternDemo*,我们的演示类使用 *RedShapeDecorator* 来装饰 *Shape* 对象。 结构图如图2 所示。示例代码如decorator.go所示。 ![装饰器模式的 UML 图](resources/9386920BD0CF0B6AB67EE7A286187ECE.jpg) 图2 图形装饰器模式的结构图 ================================================ FILE: src/structure/facade/facade.go ================================================ package main import "fmt" type Shape interface { Draw() } type Circle struct { radius float64 } func (c Circle) Draw() { fmt.Println("Draw a circle with radius:", c.radius) } type Rectangle struct { length float64 width float64 } func (r Rectangle) Draw() { fmt.Printf("Draw a rectangle with length:%f,width:%f\n", r.length, r.width) } type Square struct { width float64 } func (s Square) Draw() { fmt.Printf("Draw a square with width:%f\n", s.width) } type ShapeMaker struct { circle Circle rectangle Rectangle square Square } func (maker ShapeMaker) DrawCircle() { maker.circle.Draw() } func (maker ShapeMaker) DrawRectangle() { maker.rectangle.Draw() } func (maker ShapeMaker) DrawSquare() { maker.square.Draw() } func main() { c := Circle{radius: 5} r := Rectangle{width: 10, length: 5} s := Square{width: 10} maker := ShapeMaker{circle: c, rectangle: r, square: s} maker.DrawCircle() maker.DrawRectangle() maker.DrawSquare() } ================================================ FILE: src/structure/facade/外观模式/外观模式.md ================================================ # 外观模式 外观模式的定义与特点 ---------- 外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。 在日常编码工作中,我们都在有意无意的大量使用外观模式。只要是高层模块需要调度多个子系统(2个以上的类对象),我们都会自觉地创建一个新的类封装这些子系统,提供精简的接口,让高层模块可以更加容易地间接调用这些子系统的功能。尤其是现阶段各种第三方SDK、开源类库,很大概率都会使用外观模式。 外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。 1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。 2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。 3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。 外观(Facade)模式的主要缺点如下。 1. 不能很好地限制客户使用子系统类,很容易带来未知风险。 2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。 外观模式的结构与实现 ---------- 外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。现在来分析其基本结构和实现方法。 #### 1\. 模式的结构 外观(Facade)模式包含以下主要角色。 1. 外观(Facade)角色:为多个子系统对外提供一个共同的接口。 2. 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。 3. 客户(Client)角色:通过一个外观角色访问各个子系统的功能。 其结构图如图 1 所示。 ![外观模式的结构图](resources/2B18B79A43BF9C80007D9430C28E5960.gif) 图1 外观(Facade)模式的结构图 外观模式的应用实例 --------- 【例】用“外观模式”绘制不同的图形。 我们将创建一个 *Shape* 接口和实现了 *Shape* 接口的实体类。下一步是定义一个外观类 *ShapeMaker*。 *ShapeMaker* 类使用实体类来代表用户对这些类的调用。*FacadePatternDemo*,我们的演示类使用 *ShapeMaker* 类来显示结果。 结构图如图2所示。示例代码如facade.go所示。 ![外观模式的 UML 图](resources/1DEBB9E9B92697ECE0CB53E34AD14EFF.jpg) 图2 绘制图形的外观(Facade)模式的结构图 ================================================ FILE: src/structure/flyweight/flyweight.go ================================================ package main import ( "fmt" ) type UnsharedConcreteFlyweight struct { info string } func (unshared *UnsharedConcreteFlyweight) SetInfo(info string) { unshared.info = info } func (unshared UnsharedConcreteFlyweight) Info() string { return unshared.info } type Flyweight interface { Operation(unshared UnsharedConcreteFlyweight) } type ConcreteFlyweight struct { Key string } func (concrete ConcreteFlyweight) Operation(unshared UnsharedConcreteFlyweight) { fmt.Println("concrete flyweight " + concrete.Key + " get!") fmt.Println("unshared info:" + unshared.Info()) } type FlyweightFactory struct { flyweights map[string]Flyweight } func (f FlyweightFactory) GetFlyweight(key string) Flyweight { if flyweight, ok := f.flyweights[key]; !ok { fmt.Println("create a new flyweight:", key) flyweight = ConcreteFlyweight{Key: key} f.flyweights[key] = flyweight return flyweight } else { fmt.Println("flyweight " + key + " has existed!") return flyweight } } func main() { factory := FlyweightFactory{flyweights: map[string]Flyweight{}} f1 := factory.GetFlyweight("A") f2 := factory.GetFlyweight("A") f3 := factory.GetFlyweight("A") f4 := factory.GetFlyweight("B") f5 := factory.GetFlyweight("B") f1.Operation(UnsharedConcreteFlyweight{info: "first allocate A"}) f2.Operation(UnsharedConcreteFlyweight{info: "second allocate A"}) f3.Operation(UnsharedConcreteFlyweight{info: "third allocate A"}) f4.Operation(UnsharedConcreteFlyweight{info: "first allocate B"}) f5.Operation(UnsharedConcreteFlyweight{info: "second allocate B"}) } ================================================ FILE: src/structure/flyweight/享元模式/享元模式.md ================================================ # 享元模式 享元模式的定义与特点 ---------- 享元(Flyweight)模式的定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。 享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。 其主要缺点是: 1. 为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。 2. 读取享元模式的外部状态会使得运行时间稍微变长。 享元模式的结构与实现 ---------- 享元模式中存在以下两种状态: 1. 内部状态,即不会随着环境的改变而改变的可共享部分; 2. 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。下面来分析其基本结构和实现方法。 #### 1\. 模式的结构 享元模式的主要角色有如下。 1. 抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。 2. 具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。 3. 非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。 4. 享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。 图 1 是享元模式的结构图。图中的 UnsharedConcreteFlyweight 是非享元角色,里面包含了非共享的外部状态信息 info;而 Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight state),非享元的外部状态以参数的形式通过该方法传入;ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;FlyweightFactory 是享元工厂角色,它逝关键字 key 来管理具体享元;客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。 ![享元模式的结构图](resources/E94028AA175446D6AF25D8FB1287D74E.gif) 图1 享元模式的结构图 #### 2\. 模式的实现 享元模式的实现代码如flyweight.go所示。 ================================================ FILE: src/structure/proxy/proxy.go ================================================ package main import "fmt" type Image interface { Display() } type RealImage struct { FileName string } func (real RealImage) Display() { fmt.Println("Display the real image") } func (real *RealImage) LoadFromDisk(fileName string) { fmt.Println("Load image from disk.") real.FileName = fileName } type ProxyImage struct { Real *RealImage FileName string } func (proxy *ProxyImage) Display() { fmt.Println("Display the proxy image") if proxy.Real == nil { proxy.Real = new(RealImage) proxy.Real.LoadFromDisk(proxy.FileName) } proxy.Real.Display() } func main() { proxy := ProxyImage{FileName: "/images/1.png"} proxy.Display() fmt.Println("=======================") fmt.Println("Display the second time") proxy.Display() } ================================================ FILE: src/structure/proxy/代理模式/代理模式.md ================================================ # 代理模式 代理模式的结构与实现 ---------- 代理模式的结构比较简单,主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问,下面来分析其基本结构和实现方法。 #### 1\. 模式的结构 代理模式的主要角色如下。 1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。 2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。 3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。![](resources/688CBCCA80A92D6302B1467DC50B8BEA.jpg) 其结构图如图 1 所示。 代理模式的应用实例 --------- 【例】加载大尺寸图片 我们将创建一个 *Image* 接口和实现了 *Image* 接口的实体类。*ProxyImage* 是一个代理类,减少 *RealImage* 对象加载的内存占用。 *ProxyPatternDemo*,我们的演示类使用 *ProxyImage* 来获取要加载的 *Image* 对象,并按照需求进行显示。 ![代理模式的 UML 图](resources/D8E7C5147C37DD50324683D413E2C02A.jpg)