简介
设计模式是面向对象软件的设计经验,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。每一种设计模式系统的命名、解释和评价了面向对象中一个重要的和重复出现的设计。
创建模式是用来帮助我们创建对象的,具体有如下几种:
工厂模式 (Factory Pattern)
抽象工厂模式 (Abstract Factory Pattern)
单例模式 (Singleton Pattern)
建造者模式 (Builder Pattern)
原型模式 (Prototype Pattern)
工厂模式
通俗解释
追 MM 少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是 MM 爱吃的东西,虽然口味有所不同,但不管你带 MM 去麦当劳或肯德基,只管向服务员说「来四个鸡翅」就行了。麦当劳和肯德基就是生产鸡翅的 Factory 工厂模式:客户类和工厂类分开。
消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。
简单工厂模式
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。
优点
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点
由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。 使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
代码编写
由于 Go 本身是没有构造函数的,一般而言我们采用 NewName 的方式创建对象/接口,当它返回的是接口的时候,其实就是简单工厂模式
package factory
// IRuleConfigParser IRuleConfigParser
type IRuleConfigParser interface {
Parse(data []byte)
}
// jsonRuleConfigParser jsonRuleConfigParser
type jsonRuleConfigParser struct {
}
// Parse Parse
func (J jsonRuleConfigParser) Parse(data []byte) {
panic("implement me")
}
// yamlRuleConfigParser yamlRuleConfigParser
type yamlRuleConfigParser struct {
}
// Parse Parse
func (Y yamlRuleConfigParser) Parse(data []byte) {
panic("implement me")
}
// NewIRuleConfigParser NewIRuleConfigParser
func NewIRuleConfigParser(t string) IRuleConfigParser {
switch t {
case "json":
return jsonRuleConfigParser{}
case "yaml":
return yamlRuleConfigParser{}
}
return nil
}
测试
package factory
import (
"reflect"
"testing"
)
func TestNewIRuleConfigParser(t *testing.T) {
type args struct {
t string
}
tests := []struct {
name string
args args
want IRuleConfigParser
}{
{
name: "json",
args: args{t: "json"},
want: jsonRuleConfigParser{},
},
{
name: "yaml",
args: args{t: "yaml"},
want: yamlRuleConfigParser{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewIRuleConfigParser(tt.args.t); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewIRuleConfigParser() = %v, want %v", got, tt.want)
}
})
}
}
工厂模式
在工厂方法模式中,我们执行单个函数,传入一个参数(提供信息表明我们想要什么),但 并不要求知道任何关于对象如何实现以及对象来自哪里的细节
适用场景
创建对象需要大量重复的代码 客户端(应用层)不依赖于产品实例,如何被创建、实现等细节 一个类通过其子类来指定创建哪个对象
优点
用户只需要关心所需产品对应的工厂,无须关心创建细节 加入新产品符合开闭原则,提高可扩展性
缺点
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
代码编写
当对象的创建逻辑比较复杂,不只是简单的 new 一下就可以,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂
// IRuleConfigParserFactory 工厂方法接口
type IRuleConfigParserFactory interface {
CreateParser() IRuleConfigParser
}
// yamlRuleConfigParserFactory yamlRuleConfigParser 的工厂类
type yamlRuleConfigParserFactory struct {
}
// CreateParser CreateParser
func (y yamlRuleConfigParserFactory) CreateParser() IRuleConfigParser {
return yamlRuleConfigParser{}
}
// jsonRuleConfigParserFactory jsonRuleConfigParser 的工厂类
type jsonRuleConfigParserFactory struct {
}
// CreateParser CreateParser
func (j jsonRuleConfigParserFactory) CreateParser() IRuleConfigParser {
return jsonRuleConfigParser{}
}
// NewIRuleConfigParserFactory 用一个简单工厂封装工厂方法
func NewIRuleConfigParserFactory(t string) IRuleConfigParserFactory {
switch t {
case "json":
return jsonRuleConfigParserFactory{}
case "yaml":
return yamlRuleConfigParserFactory{}
}
return nil
}
测试
package factory
import (
"reflect"
"testing"
)
func TestNewIRuleConfigParserFactory(t *testing.T) {
type args struct {
t string
}
tests := []struct {
name string
args args
want IRuleConfigParserFactory
}{
{
name: "json",
args: args{t: "json"},
want: jsonRuleConfigParserFactory{},
},
{
name: "yaml",
args: args{t: "yaml"},
want: yamlRuleConfigParserFactory{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := NewIRuleConfigParserFactory(tt.args.t); !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewIRuleConfigParserFactory() = %v, want %v", got, tt.want)
}
})
}
}
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。一个抽象工厂是一些工厂方法的(逻辑)集合,其中每一个工厂方法负责生成不同的对象。
通俗解释
请 MM 去麦当劳吃汉堡,不同的 MM 有不同的口味,要每个都记住是一件烦人的事情,我一般采用 Factory Method 模式,带着 MM 到服务员那儿,说「要一个汉堡」,具体要什么样的汉堡呢,让 MM 直接跟服务员说就行了。
工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
应用环境
使用工厂模式还是抽象工厂模式?
通常先从简单的工厂模式开始,后面发现应用程序需要许多的工厂方法,且将这些工厂方法组合起来创建一系列的对象是有意义的,这样的话就应该使用抽象工厂。
优点
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。 增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
缺点
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。 开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
代码实现
该示例来自《Mastering Python Design Patterns,Second Edition》,模拟一个游戏。
游戏的实现需要三个接口:Hero、Obstacle、World
Hero 的实现:青蛙、巫师
Obstacle 的实现:虫子、邪恶兽人
World 的实现:青蛙世界、巫师世界
package abstractfactory
import (
"fmt"
)
// Hero 英雄
type Hero interface {
// InteractWith 和 xx 交互
InteractWith(Obstacle)
String() string
}
var _ Hero = (*Frog)(nil)
// Frog 青蛙🐸
type Frog struct {
Name string
}
func (f *Frog) InteractWith(o Obstacle) {
act := o.Action()
fmt.Printf("%s the Frog encounters %s and %s!\n", f, o, act)
}
func (f *Frog) String() string {
return f.Name
}
var _ Hero = (*Wizard)(nil)
// Wizard 巫师💂
type Wizard struct {
Name string
}
func (w *Wizard) InteractWith(o Obstacle) {
act := o.Action()
fmt.Printf("%s the Wizard battles against %s and %s!\n", w, o, act)
}
func (w *Wizard) String() string {
return w.Name
}
// Obstacle 障碍物
type Obstacle interface {
// Action 动作
Action() string
String() string
}
var _ Obstacle = (*Bug)(nil)
// Bug 虫子🐛
type Bug struct{}
func (b *Bug) Action() string {
return "eat it"
}
func (b *Bug) String() string {
return "a bug"
}
var _ Obstacle = (*Ork)(nil)
// Ork 兽人👹
type Ork struct{}
func (o *Ork) Action() string {
return "kills it"
}
func (o *Ork) String() string {
return "an evil ork"
}
// World 世界
type World interface {
// MakeCharacter 初始化角色
MakeCharacter() Hero
// MakeObstacle 初始化障碍物
MakeObstacle() Obstacle
String() string
}
var _ World = (*FrogWorld)(nil)
// FrogWorld 青蛙的世界
type FrogWorld struct {
PlayerName string
}
func (fw *FrogWorld) MakeCharacter() Hero {
return &Frog{
Name: fw.PlayerName,
}
}
func (fw *FrogWorld) MakeObstacle() Obstacle {
return &Bug{}
}
func (fw *FrogWorld) String() string {
return "\n\n\t------ Frog World ------"
}
var _ World = (*WizardWorld)(nil)
// WizardWorld 巫师世界
type WizardWorld struct {
PlayerName string
}
func (ww *WizardWorld) MakeCharacter() Hero {
return &Wizard{
Name: ww.PlayerName,
}
}
func (ww *WizardWorld) MakeObstacle() Obstacle {
return &Ork{}
}
func (ww *WizardWorld) String() string {
return "\n\n\t------ Wizard World ------"
}
// GameEnvironment 游戏入口 🕹
type GameEnvironment struct {
hero Hero
obstacle Obstacle
}
func NewGame(world World) GameEnvironment {
fmt.Println(world)
return GameEnvironment{
hero: world.MakeCharacter(),
obstacle: world.MakeObstacle(),
}
}
// Play 开始游戏
func (g GameEnvironment) Play() {
g.hero.InteractWith(g.obstacle)
}
测试
package abstractfactory
import "testing"
func TestFrogWorld(t *testing.T) {
var world World = &FrogWorld{PlayerName: "Billy"}
var game GameEnvironment = NewGame(world)
game.Play()
}
func TestWizardWorld(t *testing.T) {
var world World = &WizardWorld{PlayerName: "Charles"}
var game GameEnvironment = NewGame(world)
game.Play()
}