设计模式-创造型模式

标签
类型
技术
创建时间
Jan 16, 2023 19:50
本文中代码案例简化自我的个人项目LightCD。LightCD是基于k8s的轻量级cicd系统,工作模式为master&slave模式,slave构建镜像引擎为img(buildkit)和docker。
模式名称
介绍
简单工厂模式
专门定义一个类来创建其他类的实例。
工厂模式
定义一个工厂接口,每个类有自己的类工厂,类工厂实现工厂接口。将创建实例类的功能下放到类工厂中。
抽象工厂模式
提供一个创建一系列相关或者相互依赖的接口,而无需指定它们具体的类。
单例模式
是保证一个类仅有一个实例,并提供一个访问它的全局访问点。

简单工厂模式

// -----抽象层----- // 构建引擎接口 type Engine interface { Build() Push() Clean() } // -----实现层----- // Docker引擎,实现Engine接口 type EDocker struct {} func (d *EDocker)Build() {} func (d *EDocker)Push() {} func (d *EDocker)Clean() {} // Img引擎,实现Engine接口 type EImg struct {} func (i *EImg)Build() {} func (i *EImg)Push() {} func (i *EImg)Clean() {} // -----工厂模块----- // 简单工厂 type SimpleFactory struct {} // 创造引擎 func (s *SimpleFactory) NewEngine(name string) Engine{ if name == "docker" { return &EDocker{} } else if name == "img" { return &EImg{} } return &EDocker{} } // -----业务逻辑层----- func main() { // 初始化一个工厂 f := SimpleFactory{} // 工厂造引擎 engine := f.NewEngine("img") // 拿到引擎 engine.build() }
简单工厂模式实现了对象创建和使用的分离,面向抽象接口编程,但是对工厂类职责过重,每新增一个产品实现,就要修改工厂类的代码,违反了开闭原则。随着产品越来越多,工厂类越来越复杂,所以工厂类适用于产品较少,不会造成工厂方法中的业务逻辑太复杂。

工厂方法模式

一个产品拥有一个工厂,如下图所示
水果(抽象接口) -> 苹果,香蕉等
工厂 (抽象接口)-> 苹果工厂,香蕉工厂等。
实际案例:
package main type Engine interface { Build() Push() Clean() } type AbstractFactory interface { CreateEngine() Engine } // -----实现层----- // Docker引擎,实现Engine接口 type EDocker struct{} func (d *EDocker) Build() {} func (d *EDocker) Push() {} func (d *EDocker) Clean() {} // Img引擎,实现Engine接口 type EImg struct{} func (i *EImg) Build() {} func (i *EImg) Push() {} func (i *EImg) Clean() {} // -----工厂模块----- // Docker引擎工厂 type EDockerFactory struct {} // 实现抽象工厂接口 func (s *EDockerFactory) CreateEngine() Engine { return &EDocker{} } // Img引擎工厂 type EImgFactory struct{} // 实现抽象工厂接口 func (s *EImgFactory) CreateEngine() Engine { return &EImg{} } // -----业务逻辑层----- func main() { // 初始化抽象工厂 var DockerFac AbstractFactory // 选择一个工厂 DockerFac = &EDockerFactory{} d := DockerFac.CreateEngine() d.Build() var ImgFac AbstractFactory // 选择一个工厂 ImgFac = &EImgFactory{} i := ImgFac.CreateEngine() i.Build() }
工厂方法模式,在编写代码中,不需要记住具体类名,只需要面对抽象接口编程。实现了对象创建和使用的分离,系统的可拓展性好,无需修改接口和原类。符合开闭原则。
但是增加了系统中类的个数,增加了系统的抽象性和理解难度。

抽象工厂方法模式

抽象工厂模式在工厂模式的基础上,对工厂进一步抽象。
首先了解一个概念,产品等级结构和产品族。
产品族:水平区分,同类产品
产品等级结构:垂直区分,具有不同特点的同类产品。
工厂 -> 中国工厂,美国工厂等。
苹果 -> 中国苹果,美国苹果等。
梨 -> 中国梨,美国梨等。
实际案例:
这里产品族是Img引擎,Docker引擎。产品等级结构是测试版本,公开版本
package main // -----抽象层----- type AbstractEDocker interface { Build() Push() Clean() } type AbstractEImg interface { Build() Push() Clean() } type AbstractFactory interface { CreateDockerEngine() AbstractEDocker CreateImgEngine() AbstractEImg } // -----实现层----- // test版本Docker引擎,实现Engine接口 type TestEDocker struct{} func (d *TestEDocker) Build() {} func (d *TestEDocker) Push() {} func (d *TestEDocker) Clean() {} // test版本Img引擎,实现Engine接口 type TestEImg struct{} func (i *TestEImg) Build() {} func (i *TestEImg) Push() {} func (i *TestEImg) Clean() {} // -----工厂模块----- // test版本工厂 type TestVersionFactory struct{} func (f *TestVersionFactory) CreateDockerEngine() AbstractEDocker { return &TestEDocker{} } func (f *TestVersionFactory) CreateImgEngine() AbstractEImg { return &TestEImg{} } // release版本Docker引擎,实现Engine接口 type ReleaseEDocker struct{} func (d *ReleaseEDocker) Build() {} func (d *ReleaseEDocker) Push() {} func (d *ReleaseEDocker) Clean() {} // test版本Img引擎,实现Engine接口 type ReleaseEImg struct{} func (i *ReleaseEImg) Build() {} func (i *ReleaseEImg) Push() {} func (i *ReleaseEImg) Clean() {} // release版本工厂 type ReleaseVersionFactory struct{} func (f *ReleaseVersionFactory) CreateDockerEngine() AbstractEDocker { return &ReleaseEDocker{} } func (f *ReleaseVersionFactory) CreateImgEngine() AbstractEImg { return &ReleaseEImg{} } // -----业务逻辑层----- func main() { // 初始化抽象工厂 var Fac AbstractFactory // 创建测试版本的工厂 Fac = &TestVersionFactory{} // 创建一个测试版本的Img引擎 ti := Fac.CreateImgEngine() ti.Build() // 创建一个测试版本的docker引擎 td := Fac.CreateDockerEngine() td.Build() // 创建公开版本的工厂 Fac = &ReleaseVersionFactory{} ri := Fac.CreateImgEngine() ri.Build() rd := Fac.CreateDockerEngine() rd.Build() }
抽象工厂模式拥有工厂模式的优点,增加产品族(水平拓展)很方便,无需修改已有代码。
但是也有缺点。首先是系统复杂度增加,由“一纬“到”二维“。增加产品等级结构时,需要对原有系统进行较大改动,比如上述例子中,当我再增加一个Stage版本时,需要再增加两个实现类StageImg和StageDocker,这些实现类需要实现各自的接口,除此外,还需要增加一个Stage版本工厂实现类,实现自己的接口。显然带来较大的不便,违背了“开闭原则”。

单例模式

单例模式较为简单,这里直接贴代码例子
package main import "fmt" /* 三个要点: 一是某个类只能有一个实例; 二是它必须自行创建这个实例; 三是它必须自行向整个系统提供这个实例。 *//* 保证一个类永远只能有一个对象 *///1、保证这个类非公有化,外界不能通过这个类直接创建一个对象 // 那么这个类就应该变得非公有访问 类名称首字母要小写 type singelton struct {} //2、但是还要有一个指针可以指向这个唯一对象,但是这个指针永远不能改变方向 // Golang中没有常指针概念,所以只能通过将这个指针私有化不让外部模块访问 var instance *singelton = new(singelton) //3、如果全部为私有化,那么外部模块将永远无法访问到这个类和对象, // 所以需要对外提供一个方法来获取这个唯一实例对象 // 注意:这个方法是否可以定义为singelton的一个成员方法呢? // 答案是不能,因为如果为成员方法就必须要先访问对象、再访问函数 // 但是类和对象目前都已经私有化,外界无法访问,所以这个方法一定是一个全局普通函数 func GetInstance() *singelton { return instance } func (s *singelton) SomeThing() { fmt.Println("单例对象的某方法") } func main() { s := GetInstance() s.SomeThing() }
单例模式提供了对唯一实例的受控访问,节约系统资源,结构清晰明了,简单易懂。但是因为单例模式没有抽象依赖,所以拓展性较差,除此外也有并发安全的问题。

总结

单例模式,简单工厂,工厂,抽象工厂四个模式中,抽象程度和系统复杂度越来越高,耦合度越来越低,开发者应该根据实际情况选择满足当下系统需求的创造者模式,不能一味追求低耦合度而使得系统庞大复杂,过度设计,不利于系统的维护。