面向对象原则
面向对象编程中有一些基本的设计原则,通常被称为 SOLID 原则,这些原则有助于创建可维护、灵活和可扩展的代码。SOLID 是由 Robert C. Martin 提出的一组五个原则,分别是:
单一职责原则(Single Responsibility Principle,SRP): 一个类应该只有一个引起它变化的原因。换句话说,一个类应该只有一个单一的职责或功能。这有助于保持类的简单性,提高可维护性。
开闭原则(Open/Closed Principle,OCP): 软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。这意味着在添加新功能时,应该通过扩展而不是修改现有代码。这有助于保持稳定性和可维护性。
里氏替换原则(Liskov Substitution Principle,LSP): 子类应该能够替换其基类,并且程序的行为不会发生变化。这要求继承关系中子类必须能够完全替代基类。
接口隔离原则(Interface Segregation Principle,ISP): 不应该强迫一个类实现它不需要的接口。客户端不应该依赖它不使用的方法。接口应该小而专注,符合类的需要。
依赖倒置原则(Dependency Inversion Principle,DIP): 高层模块不应该依赖低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现细节,具体实现细节应该依赖于抽象。
这些原则一起构成了面向对象编程的基本指导方针,旨在提高代码的可维护性、可读性和可扩展性。遵循这些原则可以帮助开发人员设计出更好的软件架构和设计。
单一职责原则
package main
import "fmt"
// 用户结构体,保存用户信息
type User struct {
Name string
Email string
Password string
}
// 保存用户信息到数据库
func SaveToDatabase(user User) {
fmt.Println("Saved to database:", user)
}
// 展示用户信息
func DisplayUserInfo(user User) {
fmt.Println("Name:", user.Name)
fmt.Println("Email:", user.Email)
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Password: "123456"}
SaveToDatabase(user)
DisplayUserInfo(user)
}
package main
import "fmt"
// 用户结构体,保存用户信息
type User struct {
Name string
Email string
Password string
}
// 用户管理器,负责保存用户信息到数据库
type UserManager struct{}
func (um *UserManager) SaveToDatabase(user User) {
fmt.Println("Saved to database:", user)
}
// 用户展示器,负责展示用户信息
type UserDisplay struct{}
func (ud *UserDisplay) DisplayUserInfo(user User) {
fmt.Println("Name:", user.Name)
fmt.Println("Email:", user.Email)
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Password: "123456"}
userManager := UserManager{}
userManager.SaveToDatabase(user)
userDisplay := UserDisplay{}
userDisplay.DisplayUserInfo(user)
}
开闭原则
//开闭原则
/*
开闭原则:在添加新功能的时候,应该是拓展而不是修改原先代码。
定义一个接口,让不同的类型实现这个接口,去拓展类型。比如设计一个功能:对接一个厂商一种发送和接收数据的方法。
*/
package main
import "fmt"
//开闭原则
/*
开闭原则:在添加新功能的时候,应该是拓展而不是修改原先代码。
定义一个接口,让不同的类型实现这个接口,去拓展类型。
比如设计一个功能:对接一个厂商一种发送和接收数据的方法。
*/
type Operation interface {
Send()
Receive()
}
type A struct {
}
func (c *A) Send() {
fmt.Println("发送给厂商A数据")
}
func (c *A) Receive() {
fmt.Println("接收厂商A数据")
}
type B struct {
}
func (c *B) Send() {
fmt.Println("发送给厂商B数据")
}
func (c *B) Receive() {
fmt.Println("接收厂商B数据")
}
func main() {
var o Operation
o = &A{}
o.Send()
}
package main
import "fmt"
// 原始的功能函数
func PerformOperation(operation string, a, b int) int {
switch operation {
case "add":
return a + b
case "subtract":
return a - b
default:
return 0
}
}
func main() {
result := PerformOperation("add", 5, 3)
fmt.Println("Result:", result)
}
package main
import "fmt"
// 操作接口
type Operation interface {
Operate(a, b int) int
}
// 加法操作
type AddOperation struct{}
func (op AddOperation) Operate(a, b int) int {
return a + b
}
// 减法操作
type SubtractOperation struct{}
func (op SubtractOperation) Operate(a, b int) int {
return a - b
}
// 新的乘法操作
type MultiplyOperation struct{}
func (op MultiplyOperation) Operate(a, b int) int {
return a * b
}
func PerformOperation(op Operation, a, b int) int {
return op.Operate(a, b)
}
func main() {
addOp := AddOperation{}
subtractOp := SubtractOperation{}
multiplyOp := MultiplyOperation{}
result := PerformOperation(addOp, 5, 3)
fmt.Println("Add Result:", result)
result = PerformOperation(subtractOp, 5, 3)
fmt.Println("Subtract Result:", result)
result = PerformOperation(multiplyOp, 5, 3)
fmt.Println("Multiply Result:", result)
}
package main
import "fmt"
/*
一种厂商一种数据推送方式,每新增一种厂商要添加一种方式。
*/
// DataImporter 定义数据导入接口
type DataImporter interface {
ImportData()
}
// VendorA 实现了 DataImporter 接口的厂商 A
type VendorA struct{}
// ImportData 实现了厂商 A 的数据导入方法
func (v VendorA) ImportData() {
fmt.Println("Vendor A: Data import method")
}
// VendorB 实现了 DataImporter 接口的厂商 B
type VendorB struct{}
// ImportData 实现了厂商 B 的数据导入方法
func (v VendorB) ImportData() {
fmt.Println("Vendor B: Data import method")
}
// ImportDataFromVendor 调用数据导入方法
func ImportDataFromVendor(importer DataImporter) {
importer.ImportData()
}
func main() {
vendorA := VendorA{}
vendorB := VendorB{}
// 导入厂商 A 的数据
ImportDataFromVendor(vendorA)
// 导入厂商 B 的数据
ImportDataFromVendor(vendorB)
}
依赖倒置原则
//依赖倒置原则
/*
依赖倒置原则:高层模块通常是更抽象的模块,它们定义了一些通用的逻辑。低层模块实现了具体的细节。
定义一个低模块接口,让不同的类型实现这个接口,去拓展类型。然后定于一个高级模块接口调用。设计一个数据库连接模块,高层模块连接数据库,底层模块实现连接某个数据库的具体实现。
*/
package main
import "fmt"
//依赖倒置原则
/*
依赖倒置原则:高层模块通常是更抽象的模块,它们定义了一些通用的逻辑。低层模块实现了具体的细节。
定义一个低模块接口,让不同的类型实现这个接口,去拓展类型。然后定于一个高级模块接口调用。
设计一个数据库连接模块,高层模块连接数据库,底层模块实现连接某个数据库的具体实现。
*/
type Database interface {
Connect() string
}
// 具体实现类1
type MySQLDatabase struct{}
func (db MySQLDatabase) Connect() string {
return "Data from MySQL Database"
}
// 具体实现类2
type PostgreSQLDatabase struct{}
func (db PostgreSQLDatabase) Connect() string {
return "Data from PostgreSQL Database"
}
type DataP struct {
db Database
}
func (c *DataP) P() {
c.db.Connect()
}
func main() {
my := MySQLDatabase{}
p := DataP{db: my}
fmt.Println(p.db.Connect())
}
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象编程中的一个设计原则,它强调高层模块不应该依赖于低层模块,而是应该依赖于抽象。同时,抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
依赖倒置原则的主要思想是通过抽象来降低模块之间的耦合性,从而提高代码的灵活性和可维护性。这个原则有助于实现开闭原则,使得在扩展和修改代码时,不需要影响到其他模块。
以下是依赖倒置原则的几个关键概念:
高层模块和低层模块:高层模块通常是更抽象的模块,它们定义了一些通用的逻辑。低层模块实现了具体的细节。
抽象和具体实现:抽象是接口、抽象类或基类,用于定义高层模块所需的方法和功能。具体实现是实现抽象的具体类。
倒置依赖:高层模块不应该直接依赖于低层模块,而是通过抽象来依赖。
package main
import "fmt"
// 抽象接口
type Database interface {
ReadData() string
}
// 具体实现类1
type MySQLDatabase struct{}
func (db MySQLDatabase) ReadData() string {
return "Data from MySQL Database"
}
// 具体实现类2
type PostgreSQLDatabase struct{}
func (db PostgreSQLDatabase) ReadData() string {
return "Data from PostgreSQL Database"
}
// 高层模块
type DataProcessor struct {
db Database
}
func (dp DataProcessor) Process() {
data := dp.db.ReadData()
fmt.Println("Processing data:", data)
}
func main() {
mySQLDB := MySQLDatabase{}
postgresDB := PostgreSQLDatabase{}
processor1 := DataProcessor{db: mySQLDB}
processor1.Process()
processor2 := DataProcessor{db: postgresDB}
processor2.Process()
}
在上面的示例中,DataProcessor 是一个高层模块,它依赖于抽象的 Database 接口,而不是直接依赖于具体的数据库实现。
这样,通过实现不同的数据库类,我们可以轻松地在不修改 DataProcessor 的情况下,切换不同的数据库实现。
里氏替换原则
里氏替换原则是面向对象设计中的一个重要原则,它指出程序中的对象应该可以被它们的子类所替换,而不影响程序的正确性。这意味着,只要父类能够出现的地方,子类就应该可以出现,并且可以在不改变程序正确性的前提下被替换掉。
这个原则由 Barbara Liskov 在 1987 年的一篇论文中提出,并以她的名字命名。它强调了继承关系的正确性和子类型的可替换性,帮助保持系统的一致性和稳定性。
在实际编程中,遵循里氏替换原则可以提高代码的灵活性和可维护性,减少出错的可能性。
要遵循里氏替换原则,子类应该:
- 保持父类的行为:子类应该能够替代父类并表现出相同的行为。
- 不应该重写父类的非抽象方法:子类可以通过重写抽象方法来实现自己的行为,但不应该修改父类的非抽象方法的行为。
以下是一个示例,说明了违反里氏替换原则可能导致的问题:
package main
import "fmt"
// Rectangle 矩形结构体
type Rectangle struct {
Width int
Height int
}
func (r *Rectangle) SetWidth(width int) {
r.Width = width
}
func (r *Rectangle) SetHeight(height int) {
r.Height = height
}
func (r *Rectangle) GetArea() int {
return r.Width * r.Height
}
// Square 正方形结构体,继承自矩形
type Square struct {
Rectangle
}
func (s *Square) SetWidth(width int) {
s.Width = width
s.Height = width
}
func (s *Square) SetHeight(height int) {
s.Width = height
s.Height = height
}
func main() {
rectangle := Rectangle{Width: 3, Height: 4}
fmt.Println("Rectangle Area:", rectangle.GetArea()) // 输出:Rectangle Area: 12
square := Square{Rectangle{Width: 3, Height: 4}}
square.SetWidth(5)
fmt.Println("Square Area:", square.GetArea()) // 输出:Square Area: 25,但实际上不是正方形的面积
}
接口隔离原则
接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计中的一个原则,它指出一个类对其他类的依赖应该建立在最小的接口上。该原则的核心思想是客户端不应该依赖于它不需要的接口,应将不同的功能分解成不同的接口,以便于客户端根据需求使用特定的接口。
这个原则的目标是降低类之间的耦合性,提高系统的灵活性、可维护性和可扩展性。
以下是一些遵循接口隔离原则的示例:
- 合理拆分接口:将大而臃肿的接口拆分成多个小的粒度接口,每个接口提供一组相关的方法,避免接口臃肿。
- 接口定制:根据客户端的需要创建定制的接口,使得客户端只依赖于它们真正需要的方法。
- 接口适配:在不同层次之间引入适配器接口,使得上层只需要与适配器接口进行交互,而不是直接依赖于底层实现的具体接口。
package main
import "fmt"
// Worker 接口
type Worker interface {
Work()
}
// Eater 接口
type Eater interface {
Eat()
}
// SuperWorker 实现了 Worker 和 Eater 接口
type SuperWorker struct{}
func (s *SuperWorker) Work() {
fmt.Println("SuperWorker is working")
}
func (s *SuperWorker) Eat() {
fmt.Println("SuperWorker is eating")
}
// NormalWorker 实现了 Worker 接口
type NormalWorker struct{}
func (n *NormalWorker) Work() {
fmt.Println("NormalWorker is working")
}
// Client 仅依赖于所需的接口
func Client(w Worker) {
w.Work()
}
func main() {
superWorker := &SuperWorker{}
normalWorker := &NormalWorker{}
// Client 仅依赖于 Worker 接口
Client(superWorker) // 输出:SuperWorker is working
Client(normalWorker) // 输出:NormalWorker is working
}
在这个示例中,Worker
和 Eater
接口被分别定义为两个不同的接口,SuperWorker
实现了这两个接口,而 NormalWorker
只实现了 Worker
接口。Client
函数只依赖于 Worker
接口,这样就遵循了接口隔离原则,客户端只需使用它所需的最小接口,而不需要依赖于不必要的接口。
设计模式
-
创建型模式:
- 工厂模式(Factory Pattern)
- 抽象工厂模式(Abstract Factory Pattern)
- 单例模式(Singleton Pattern)
- 建造者模式(Builder Pattern)
- 原型模式(Prototype Pattern)
-
结构型模式:
- 适配器模式(Adapter Pattern)
- 桥接模式(Bridge Pattern)
- 组合模式(Composite Pattern)
- 装饰器模式(Decorator Pattern)
- 外观模式(Facade Pattern)
- 享元模式(Flyweight Pattern)
- 代理模式(Proxy Pattern)
-
行为型模式:
- 责任链模式(Chain of Responsibility Pattern)
- 命令模式(Command Pattern)
- 迭代器模式(Iterator Pattern)
- 中介者模式(Mediator Pattern)
- 备忘录模式(Memento Pattern)
- 观察者模式(Observer Pattern)
- 状态模式(State Pattern)
- 策略模式(Strategy Pattern)
- 模板方法模式(Template Method Pattern)
- 访问者模式(Visitor Pattern)
设计模式和面向对象原则的关系
设计模式和面向对象原则是紧密相关的概念,它们在软件设计中共同发挥着重要作用。下面解释一下它们之间的关系:
设计模式是实现原则的具体实践: 面向对象编程有一些基本的设计原则,如开闭原则、单一职责原则、依赖倒置原则等。设计模式可以看作是这些原则的具体实践,它们是将原则抽象成实际可用的模式,以解决实际问题。
设计模式支持面向对象原则: 设计模式的目标之一是帮助开发人员遵循面向对象原则,从而编写出更好的、可维护的代码。通过应用适当的设计模式,可以更容易地遵循这些原则,从而达到更好的设计和架构。
设计模式强调灵活性和可扩展性: 面向对象原则提供了一些指导,告诉我们如何在代码中组织类和对象。设计模式则进一步强调如何通过模式来实现灵活性、可扩展性和可维护性,以满足不同的需求变化。
设计模式是实现面向对象的工具: 面向对象编程的目标是将现实世界的概念映射到代码中,提供更好的抽象和封装。设计模式则是一些通用的解决方案,可以帮助我们构建更好的面向对象的系统。
综上所述,设计模式和面向对象原则是相辅相成的概念,它们共同构建了良好的软件设计和架构基础。设计模式是原则的实际应用,帮助开发人员在实际项目中更好地应用面向对象的思想和原则。
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,将对象的实例化过程封装在一个工厂类中,从而降低了客户端代码与具体对象的耦合度。工厂模式有多种变体,其中包括简单工厂、工厂方法和抽象工厂等。
创建型模式
单例模式
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式主要用于控制类的实例化过程,以便在整个应用程序中只存在一个实例,从而避免不必要的资源浪费。
单例模式的特点包括
一个类只有一个实例: 单例模式确保一个类只有一个实例存在,无论在何处访问它。
全局访问点: 单例模式提供一个全局的访问点,允许其他类通过该访问点访问单例实例。
单例模式的经典实现通常包括两种方式:懒汉式和饿汉式。
懒汉式:先不急创建,在第一次使用时候创建使用sync.Once的Do方法,该方法在第一次调用时候执行
饿汉式:系统初始化之前把对象创建好,需要的时候直接获取
//在懒汉式中,实例在第一次使用时创建。
package main
import (
"fmt"
"sync"
)
type Singleton struct{}
var instance *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
func main() {
s1 := GetInstance()
s2 := GetInstance()
fmt.Println(s1 == s2) // 输出 true,因为 s1 和 s2 是同一个实例
}
package main
import (
"fmt"
"sync"
)
type Singleton struct {
Data int
}
var instance *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
// 在这里可以进行实例的初始化操作
instance.Data = 42
})
return instance
}
func main() {
s1 := GetInstance()
s2 := GetInstance()
fmt.Println(s1 == s2) // 输出 true,因为 s1 和 s2 是同一个实例
fmt.Println(s1.Data) // 输出 42,因为实例已经在 GetInstance 中初始化
fmt.Println(s2.Data) // 输出 42,因为实例已经在 GetInstance 中初始化
}
简单工厂模式
package main
import (
"fmt"
)
// DatabaseConnection 是数据库连接接口
type DatabaseConnection interface {
Connect() error
}
// MySQLConnection 是 MySQL 数据库连接对象
type MySQLConnection struct {
// 这里可以包含连接 MySQL 所需的属性
}
func (m MySQLConnection) Connect() error {
fmt.Println("Connected to MySQL database")
// 连接 MySQL 的逻辑
return nil
}
// PostgreSQLConnection 是 PostgreSQL 数据库连接对象
type PostgreSQLConnection struct {
// 这里可以包含连接 PostgreSQL 所需的属性
}
func (p PostgreSQLConnection) Connect() error {
fmt.Println("Connected to PostgreSQL database")
// 连接 PostgreSQL 的逻辑
return nil
}
func CreateDatabaseConnection(databaseType string) DatabaseConnection {
// 根据数据库类型选择对应的工厂
switch databaseType {
case "MySQL":
return MySQLConnection{}
case "PostgreSQL":
return PostgreSQLConnection{}
default:
// 处理不支持的数据库类型
}
return nil
}
func main() {
// 从配置文件或用户输入中获取数据库类型
databaseType := "MySQL" // 假设从配置文件获取了数据库类型
c := CreateDatabaseConnection(databaseType)
c.Connect()
}
工厂模式
package main
import (
"fmt"
)
// DatabaseConnection 是数据库连接接口
type DatabaseConnection interface {
Connect() error
}
// MySQLConnection 是 MySQL 数据库连接对象
type MySQLConnection struct {
// 这里可以包含连接 MySQL 所需的属性
}
func (m MySQLConnection) Connect() error {
fmt.Println("Connected to MySQL database")
// 连接 MySQL 的逻辑
return nil
}
// PostgreSQLConnection 是 PostgreSQL 数据库连接对象
type PostgreSQLConnection struct {
// 这里可以包含连接 PostgreSQL 所需的属性
}
func (p PostgreSQLConnection) Connect() error {
fmt.Println("Connected to PostgreSQL database")
// 连接 PostgreSQL 的逻辑
return nil
}
// DatabaseConnectionFactory 是数据库连接工厂接口
type DatabaseConnectionFactory interface {
CreateConnection() DatabaseConnection
}
// MySQLConnectionFactory 是创建 MySQL 数据库连接对象的工厂
type MySQLConnectionFactory struct{}
func (f MySQLConnectionFactory) CreateConnection() DatabaseConnection {
return MySQLConnection{}
}
// PostgreSQLConnectionFactory 是创建 PostgreSQL 数据库连接对象的工厂
type PostgreSQLConnectionFactory struct{}
func (f PostgreSQLConnectionFactory) CreateConnection() DatabaseConnection {
return PostgreSQLConnection{}
}
func main() {
// 从配置文件或用户输入中获取数据库类型
databaseType := "MySQL" // 假设从配置文件获取了数据库类型
var factory DatabaseConnectionFactory
// 根据数据库类型选择对应的工厂
switch databaseType {
case "MySQL":
factory = MySQLConnectionFactory{}
case "PostgreSQL":
factory = PostgreSQLConnectionFactory{}
default:
// 处理不支持的数据库类型
}
// 使用工厂创建数据库连接对象
connection := factory.CreateConnection()
connection.Connect()
}
main()
函数不需要直接关注对象的具体创建细节,而是委托给了工厂接口的实现类。
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式是一种创建型设计模式,旨在提供一个接口,用于创建相关或依赖对象的家族,而不需要指定它们具体的类。这种模式是工厂方法模式的升级版,在工厂方法模式中,每个工厂只负责创建一种产品,而在抽象工厂模式中,一个工厂可以创建多个相关的产品。
在抽象工厂模式中,通常有以下几个关键角色:
- 抽象工厂(Abstract Factory):定义了创建一组产品的接口。
- 具体工厂(Concrete Factory):实现了抽象工厂接口,负责创建一组相关的产品。
- 抽象产品(Abstract Product):定义了产品的接口,描述了产品的特性。
- 具体产品(Concrete Product):实现了抽象产品接口,是抽象工厂所创建的具体对象。
抽象工厂模式提供了一种方式来封装一组相关或依赖的产品对象的创建,使得客户端可以通过抽象的方式使用具体的产品,而不需要关注其具体实现。
package main
import "fmt"
// AbstractFactory 抽象工厂接口
type AbstractFactory interface {
CreateProductA() AbstractProductA
CreateProductB() AbstractProductB
}
// AbstractProductA 抽象产品A接口
type AbstractProductA interface {
Use()
}
// AbstractProductB 抽象产品B接口
type AbstractProductB interface {
Use()
}
// ConcreteFactory1 具体工厂1
type ConcreteFactory1 struct{}
func (c *ConcreteFactory1) CreateProductA() AbstractProductA {
return &ProductA1{}
}
func (c *ConcreteFactory1) CreateProductB() AbstractProductB {
return &ProductB1{}
}
// ConcreteFactory2 具体工厂2
type ConcreteFactory2 struct{}
func (c *ConcreteFactory2) CreateProductA() AbstractProductA {
return &ProductA2{}
}
func (c *ConcreteFactory2) CreateProductB() AbstractProductB {
return &ProductB2{}
}
// ProductA1 具体产品A1
type ProductA1 struct{}
func (p *ProductA1) Use() {
fmt.Println("ProductA1 used")
}
// ProductB1 具体产品B1
type ProductB1 struct{}
func (p *ProductB1) Use() {
fmt.Println("ProductB1 used")
}
// ProductA2 具体产品A2
type ProductA2 struct{}
func (p *ProductA2) Use() {
fmt.Println("ProductA2 used")
}
// ProductB2 具体产品B2
type ProductB2 struct{}
func (p *ProductB2) Use() {
fmt.Println("ProductB2 used")
}
func main() {
// 通过具体工厂1创建一组产品
factory1 := &ConcreteFactory1{}
productA1 := factory1.CreateProductA()
productB1 := factory1.CreateProductB()
productA1.Use()
productB1.Use()
// 通过具体工厂2创建一组产品
factory2 := &ConcreteFactory2{}
productA2 := factory2.CreateProductA()
productB2 := factory2.CreateProductB()
productA2.Use()
productB2.Use()
}
在这个示例中,AbstractFactory
是抽象工厂接口,AbstractProductA
和 AbstractProductB
是抽象产品接口。ConcreteFactory1
和 ConcreteFactory2
是具体工厂,分别实现了抽象工厂接口,并能创建一组相关的产品。ProductA1
、ProductB1
、ProductA2
和 ProductB2
是具体产品,它们分别实现了抽象产品接口。通过具体工厂可以创建一组相关的产品,而客户端可以通过抽象工厂接口使用这些产品,而无需关注具体的产品实现。
建造者模式(Builder Pattern)
建造者模式是一种创建型设计模式,旨在将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。它允许你按照步骤来创建对象,将对象的构建过程与其最终表示分离开来。
该模式通常包含以下几个关键角色:
- Product(产品):最终所要创建的复杂对象。
- Builder(建造者):抽象接口,规范了构建产品的步骤和方法。
- ConcreteBuilder(具体建造者):实现了 Builder 接口,负责具体的构建步骤和最终产品的创建。
- Director(指挥者):负责使用 Builder 接口来构建最终的对象。
建造者模式常用于构建复杂对象,例如创建对象时需要多个步骤、步骤顺序不固定或者对象内部构建过程较为复杂时。
package main
import "fmt"
// Product 产品
type Product struct {
PartA string
PartB string
PartC string
}
// Builder 建造者接口
type Builder interface {
BuildPartA()
BuildPartB()
BuildPartC()
GetResult() *Product
}
// ConcreteBuilder 具体建造者
type ConcreteBuilder struct {
product *Product
}
func NewConcreteBuilder() *ConcreteBuilder {
return &ConcreteBuilder{product: &Product{}}
}
func (c *ConcreteBuilder) BuildPartA() {
c.product.PartA = "PartA built"
}
func (c *ConcreteBuilder) BuildPartB() {
c.product.PartB = "PartB built"
}
func (c *ConcreteBuilder) BuildPartC() {
c.product.PartC = "PartC built"
}
func (c *ConcreteBuilder) GetResult() *Product {
return c.product
}
// Director 指挥者
type Director struct {
builder Builder
}
func NewDirector(builder Builder) *Director {
return &Director{builder: builder}
}
func (d *Director) Construct() *Product {
d.builder.BuildPartA()
d.builder.BuildPartB()
d.builder.BuildPartC()
return d.builder.GetResult()
}
func main() {
builder := NewConcreteBuilder()
director := NewDirector(builder)
product := director.Construct()
fmt.Println(product)
}
原型模式(Prototype Pattern)
原型模式是一种创建型设计模式,旨在通过克隆现有对象来创建新对象,而不是通过实例化一个类。这样可以在运行时动态创建对象,同时避免了使用构造函数。
关键角色包括:
- 原型接口(Prototype):定义了克隆自身的方法。
- 具体原型类(Concrete Prototype):实现了原型接口,实现了克隆方法以生成新的对象。
在大多数编程语言中,实现原型模式通常需要对对象进行深拷贝(deep copy)操作,确保新对象与原对象的数据完全独立,避免共享引用导致的问题。
以下是一个简单的原型模式示例:
package main
import "fmt"
// Prototype 原型接口
type Prototype interface {
Clone() Prototype
}
// ConcretePrototype 具体原型类
type ConcretePrototype struct {
Name string
}
func (c *ConcretePrototype) Clone() Prototype {
// 深拷贝对象
return &ConcretePrototype{Name: c.Name}
}
func main() {
// 创建原型对象
prototype := &ConcretePrototype{Name: "Original Object"}
// 克隆原型对象
clone := prototype.Clone().(*ConcretePrototype)
clone.Name = "Cloned Object"
fmt.Println(prototype.Name) // 输出:Original Object
fmt.Println(clone.Name) // 输出:Cloned Object
}
结构型模式
外观模式
//外观模式
/*
外观模式:封装一些列复杂的子系统,提供一个高层次的接口供客户端访问.
*/
package main
import "fmt"
//外观模式
/*
外观模式:封装一些列复杂的子系统,提供一个高层次的接口供客户端访问.
*/
// SubsystemA 子系统 A
type SubsystemA struct{}
func (a SubsystemA) OperationA() {
fmt.Println("SubsystemA: OperationA")
}
// SubsystemB 子系统 B
type SubsystemB struct{}
func (b SubsystemB) OperationB() {
fmt.Println("SubsystemB: OperationB")
}
// SubsystemC 子系统 C
type SubsystemC struct{}
func (c SubsystemC) OperationC() {
fmt.Println("SubsystemC: OperationC")
}
// Facade 外观类
type Facade struct {
subsystemA SubsystemA
subsystemB SubsystemB
subsystemC SubsystemC
}
func NewFacade() *Facade {
return &Facade{
subsystemA: SubsystemA{},
subsystemB: SubsystemB{},
subsystemC: SubsystemC{},
}
}
func (f *Facade) Operation() {
fmt.Println("Facade: Operation")
f.subsystemA.OperationA()
f.subsystemB.OperationB()
f.subsystemC.OperationC()
}
func main() {
facade := NewFacade()
facade.Operation()
}
适配器模式(Adapter Pattern)
适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一个接口。它允许不兼容的接口间进行合作,使得原本由于接口不匹配而无法在一起工作的类能够协同工作。
适配器模式通常涉及以下几个角色:
- 目标接口(Target):客户端期待的接口。
- 适配器(Adapter):实现目标接口,并持有适配的对象,负责将请求转发给适配对象。
- 适配者(Adaptee):已存在的接口,但与目标接口不兼容的类。
适配器模式通过适配器将不兼容的接口进行转换,让适配者能够被客户端使用,同时不改变原有的接口和实现。
举个例子,比如有一个圆孔(目标接口),但是我们有一个方钉(适配者),方钉不能直接放入圆孔中。这时候适配器就像一个圆形套筒,它可以将方钉放入套筒中,从而适配了圆孔的形状。
在Go语言中的实现可能如下所示:
package main
import "fmt"
// Target 目标接口
type Target interface {
Request() string
}
// Adaptee 适配者
type Adaptee struct{}
func (a *Adaptee) SpecificRequest() string {
return "Adaptee method"
}
// Adapter 适配器
type Adapter struct {
adaptee *Adaptee
}
func (adapter *Adapter) Request() string {
return adapter.adaptee.SpecificRequest()
}
func main() {
adaptee := &Adaptee{}
adapter := &Adapter{adaptee: adaptee}
client := func(target Target) {
fmt.Println(target.Request())
}
client(adapter)
}
在这个例子中,Adaptee
表示适配者,它有一个特定的方法 SpecificRequest
。Adapter
实现了目标接口 Target
,在其内部包含了一个Adaptee
对象。当客户端调用Request
方法时,实际上是调用了Adaptee
的SpecificRequest
方法。
适配器模式的关键是建立一个中间层(适配器)来实现接口的转换,使得原本不兼容的对象能够协同工作。
桥接模式(Bridge Pattern)
桥接模式是一种结构型设计模式,它旨在将抽象部分与其实现部分分离,使它们可以独立地变化。通常用于处理多维变化的情况,例如多种维度的分类,同时避免在这些维度之间产生笛卡尔积。
该模式通过创建一个桥接接口,将抽象部分和实现部分分开,并且它们可以独立地进行变化。这样就可以在不同的抽象层次上组合和扩展不同的实现,而无需修改现有的抽象层次和实现层次。
以下是桥接模式的几个重要角色:
- 抽象类(Abstraction):定义抽象接口,它维护一个指向实现对象的引用。
- 扩充抽象类(Refined Abstraction):对抽象类的扩展,通常用于定义更精细的抽象接口。
- 实现类接口(Implementor):定义实现类的接口,通常包括实现抽象类的具体方法。
- 具体实现类(Concrete Implementor):实现类接口的具体实现。
这个模式的核心思想是将抽象和实现分离,让它们可以独立地变化。这样,在不同的抽象层次上可以有不同的实现,从而使得系统更灵活、可扩展性更强。
下面是一个简单的桥接模式的示例:
package main
import "fmt"
// Implementor 实现类接口
type Implementor interface {
OperationImpl() string
}
// ConcreteImplementorA 具体实现类A
type ConcreteImplementorA struct{}
func (c *ConcreteImplementorA) OperationImpl() string {
return "Concrete Implementor A operation"
}
// ConcreteImplementorB 具体实现类B
type ConcreteImplementorB struct{}
func (c *ConcreteImplementorB) OperationImpl() string {
return "Concrete Implementor B operation"
}
// Abstraction 抽象类
type Abstraction struct {
impl Implementor
}
func (a *Abstraction) Operation() string {
return "Abstraction operation with " + a.impl.OperationImpl()
}
func main() {
implementorA := &ConcreteImplementorA{}
implementorB := &ConcreteImplementorB{}
abstraction1 := &Abstraction{impl: implementorA}
abstraction2 := &Abstraction{impl: implementorB}
fmt.Println(abstraction1.Operation())
fmt.Println(abstraction2.Operation())
}
在这个例子中,Component
是组件接口,Leaf
表示叶子节点,Composite
表示容器节点。Composite
可以包含多个Component
,无论是Leaf
还是Composite
本身,都可以被当作一个整体进行操作。
组合模式(Composite Pattern)
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构以表现“整体-部分”的层次结构。组合模式使得客户端能够统一对待单个对象和对象组合,而无需关心它们的具体类型。
组合模式主要涉及以下几个角色:
- 组件(Component):定义组合中所有对象的通用接口,它可以是叶子节点或者容器(即包含其他组件的容器节点)。
- 叶子(Leaf):在组合中表示叶子节点对象,它没有子节点。
- 容器(Container):在组合中表示容器节点对象,它可以包含其他组件。
- 客户端(Client):通过组件接口操作组合对象的客户端。
组合模式的核心思想是使得客户端能够统一地对待单个对象和对象的组合。无论是单个对象还是对象组合,它们都实现了相同的接口,这样客户端就可以一致性地对待它们,而不必关心具体是哪种对象。
下面是一个简单的组合模式的示例:
package main
import "fmt"
// Component 组件接口
type Component interface {
Operation() string
}
// Leaf 叶子节点
type Leaf struct {
name string
}
func (l *Leaf) Operation() string {
return "Leaf: " + l.name
}
// Composite 容器节点
type Composite struct {
name string
children []Component
}
func (c *Composite) Operation() string {
result := fmt.Sprintf("Composite: %s [", c.name)
for _, child := range c.children {
result += child.Operation() + " "
}
result += "]"
return result
}
func (c *Composite) Add(child Component) {
c.children = append(c.children, child)
}
func main() {
leaf1 := &Leaf{name: "Leaf 1"}
leaf2 := &Leaf{name: "Leaf 2"}
composite := &Composite{name: "Composite"}
composite.Add(leaf1)
composite.Add(leaf2)
fmt.Println(composite.Operation())
}
这个例子中,Adaptee
表示适配者,它有一个特定的方法 SpecificRequest
。Adapter
实现了目标接口 Target
,在其内部包含了一个Adaptee
对象。当客户端调用Request
方法时,实际上是调用了Adaptee
的SpecificRequest
方法。适配器模式的关键在于适配器对象包含了一个适配者对象,并通过适配器实现了目标接口,从而使得适配者能够被客户端调用。
装饰器模式(Decorator Pattern)
装饰器模式是一种结构型设计模式,它允许在不改变对象结构的情况下,动态地给对象添加新的功能。该模式通过创建一个装饰类来包裹原始类,并在装饰类中实现额外的功能。
在装饰器模式中,有几个核心角色:
- 组件接口(Component):定义原始对象和装饰对象共同的接口。
- 具体组件(ConcreteComponent):实现组件接口的具体对象,它是被装饰的原始对象。
- 装饰器(Decorator):实现了组件接口,并且持有一个指向组件对象的引用。这个类可以在原有对象的基础上添加新的功能。
装饰器模式的关键思想是通过递归组合来进行功能的添加,每个装饰器类都可以装饰另一个装饰器或者具体组件,从而实现功能的嵌套。
以下是一个简单的装饰器模式示例:
package main
import "fmt"
// Component 组件接口
type Component interface {
Operation() string
}
// ConcreteComponent 具体组件
type ConcreteComponent struct{}
func (c *ConcreteComponent) Operation() string {
return "ConcreteComponent operation"
}
// Decorator 装饰器
type Decorator struct {
component Component
}
func (d *Decorator) Operation() string {
if d.component != nil {
return "Decorator operation with " + d.component.Operation()
}
return "Decorator operation"
}
func main() {
component := &ConcreteComponent{}
decorator := &Decorator{component: component}
// 可以使用装饰器对原始对象进行功能的动态添加
result := decorator.Operation()
fmt.Println(result)
}
在这个示例中,Component
是组件接口,ConcreteComponent
是具体组件。Decorator
是装饰器,它实现了组件接口,并在 Operation
方法中添加了额外的功能。这个装饰器类可以对一个具体组件进行装饰,并在原有功能基础上添加新的功能。
享元模式(Flyweight Pattern)
享元模式是一种结构型设计模式,旨在有效地支持大量细粒度对象的共享。它通过共享对象来最小化内存使用和提高性能。该模式适用于需要创建大量相似对象,以节省内存和提高性能的场景。
在享元模式中,有几个关键角色:
- 享元接口(Flyweight):定义了需要共享的对象的接口。
- 具体享元(ConcreteFlyweight):实现了享元接口,包含了内部状态,并且能够进行共享。
- 非共享具体享元(UnsharedConcreteFlyweight):不可共享的具体享元对象,通常不会被共享,可能包含独特的内部状态。
- 享元工厂(FlyweightFactory):负责创建和管理享元对象,通常实现了对象的池化,用于存储和管理共享的享元对象。
以下是一个简单的享元模式示例:
package main
import "fmt"
// Flyweight 享元接口
type Flyweight interface {
Operation()
}
// ConcreteFlyweight 具体享元
type ConcreteFlyweight struct {
name string
}
func (c *ConcreteFlyweight) Operation() {
fmt.Println("ConcreteFlyweight operation with name:", c.name)
}
// FlyweightFactory 享元工厂
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
func (f *FlyweightFactory) GetFlyweight(name string) Flyweight {
if f.flyweights == nil {
f.flyweights = make(map[string]Flyweight)
}
if flyweight, ok := f.flyweights[name]; ok {
return flyweight
}
f.flyweights[name] = &ConcreteFlyweight{name: name}
return f.flyweights[name]
}
func main() {
factory := &FlyweightFactory{}
flyweight1 := factory.GetFlyweight("shared")
flyweight1.Operation()
flyweight2 := factory.GetFlyweight("shared")
flyweight2.Operation()
flyweight3 := factory.GetFlyweight("unshared")
flyweight3.Operation()
}
在这个示例中,Flyweight
是享元接口,ConcreteFlyweight
是具体享元。FlyweightFactory
是享元工厂,它负责创建和管理享元对象。在 main()
函数中,通过享元工厂获取了共享的享元对象和非共享的享元对象,这些共享的对象在工厂内部被重复利用,从而节省了内存空间。
代理模式(Proxy Pattern)
代理模式是一种结构型设计模式,它允许通过代理对象控制对原始对象的访问。代理模式通常用于在访问对象时添加额外的逻辑,例如控制对对象的访问权限、实现延迟加载或缓存、记录日志等。
在代理模式中,通常有以下几种角色:
- 抽象对象接口(Subject):定义了真实对象和代理对象的公共接口。
- 真实对象(RealSubject):实现了抽象对象接口,是被代理的真实对象。
- 代理对象(Proxy):实现了抽象对象接口,并包含一个指向真实对象的引用,在其内部对真实对象的操作进行控制和管理。
代理模式可以分为多种类型,如远程代理、虚拟代理、保护代理等,它们在解决不同问题上有着不同的应用场景。
以下是一个简单的代理模式示例
package main
import "fmt"
// Subject 抽象对象接口
type Subject interface {
Request()
}
// RealSubject 真实对象
type RealSubject struct{}
func (r *RealSubject) Request() {
fmt.Println("RealSubject: Handling request")
}
// Proxy 代理对象
type Proxy struct {
realSubject *RealSubject
}
func (p *Proxy) Request() {
// 在代理对象中可以进行额外的逻辑处理
if p.realSubject == nil {
p.realSubject = &RealSubject{}
}
p.realSubject.Request()
}
func main() {
proxy := &Proxy{}
proxy.Request()
}
在这个示例中,Subject
是抽象对象接口,RealSubject
是真实对象,它实现了 Subject
接口的方法。Proxy
是代理对象,它同样实现了 Subject
接口,并在 Request
方法中进行了对真实对象的访问控制。在 main()
函数中,通过代理对象访问了真实对象,代理对象在内部调用了真实对象的 Request
方法。代理模式允许代理对象控制对真实对象的访问,并可以在不改变真实对象接口的情况下增加额外的功能。
行为式模式
观察者模式
//观察者模式
/*
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
观察者模式主要涉及两个角色:主题(Subject)和观察者(Observer)。主题维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。观察者则定义了一个接口,用于接收主题的通知。
*/
package main
import "fmt"
//观察者模式
/*
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
观察者模式主要涉及两个角色:主题(Subject)和观察者(Observer)。主题维护了一个观察者列表,并提供了添加、删除和通知观察者的方法。观察者则定义了一个接口,用于接收主题的通知。
*/
type GCZI interface {
GCZF(s string)
}
type GCZ struct {
Name string
}
func (c *GCZ) GCZF(s string) {
fmt.Println(c.Name + "接收到通知" + s)
}
type ZT struct {
GCZ []GCZI
}
type ZTF interface {
Z(gcz GCZ)
S(gcz GCZ)
T(s string)
}
func (c *ZT) Z(gCZI GCZI) {
c.GCZ = append(c.GCZ, gCZI)
}
func (c *ZT) S(gCZI GCZI) {
for i, v := range c.GCZ {
if v == gCZI {
c.GCZ = append(c.GCZ[:i], c.GCZ[i+1:]...)
break
}
}
}
func (c *ZT) T(s string) {
for _, v := range c.GCZ {
v.GCZF(s)
}
}
func main() {
gcz1 := GCZ{Name: "张三"}
gcz2 := GCZ{Name: "王四"}
zt := ZT{}
zt.Z(&gcz1)
zt.Z(&gcz2)
zt.T("今天放假")
}
责任链模式
责任链模式是一种行为型设计模式,它允许你将请求沿着处理者链进行传递,直到有一个处理者处理该请求为止。在责任链模式中,请求由一个对象向另一个对象传递,直到有一个对象处理它为止。
关键要素:
-
处理者(Handler):
- 定义一个处理请求的接口。
- 持有对下一个处理者的引用(可以是另一个处理者对象或者是处理者链中的下一个节点)。
- 处理请求的方法通常包含了判断能否处理请求的逻辑,如果能则进行处理,否则将请求传递给下一个处理者。
-
具体处理者(Concrete Handler):
- 实现处理者接口。
- 对请求进行具体的处理。如果自身无法处理请求,则将请求传递给下一个处理者。
-
客户端(Client):
- 创建并组装责任链。
工作原理:
- 当客户端发送请求时,请求沿着责任链传递,直到有一个处理者能够处理该请求。
- 每个处理者都有机会对请求进行处理,如果它可以处理,则不会将请求传递给链中的下一个处理者;如果不能处理,则会将请求传递给下一个处理者,直到有一个处理者能够处理为止。
优势:
- 解耦请求发送者和接收者,让请求在不同的处理者之间自由传递,增加了灵活性。
- 可动态地组织和调整处理链,可以随时增加或删除处理者。
示例应用场景:
- Web 中的中间件机制。
- 日志记录系统,根据日志级别进行不同处理。
- 购买流程中的优惠券、折扣等逻辑处理。
责任链模式可以降低请求的发送者和接收者之间的耦合度,并使得处理链可配置、可扩展
在 Go 中实现责任链模式相对简单,你可以使用接口来定义处理者和具体处理者,然后在具体处理者中实现处理请求的逻辑,并设置下一个处理者。
package main
import "fmt"
//责任链模式
/*
责任链允许一个请求沿着处理者链进行一次处理.
*/
//定义处理请求的处理者
type 请求处理者接口 interface {
处理请求(p *请求)
设置下一个处理者(p *请求处理者)
}
//请求
type 请求 struct {
Name string
}
type 请求处理者 struct {
Name string
下个处理者 *请求处理者
}
func (c *请求处理者) 处理请求(p *请求) {
fmt.Println(c.Name + "处理请求了")
if c.下个处理者 != nil {
c.下个处理者.处理请求(p)
}
}
func (c *请求处理者) 设置下一个处理者(p *请求处理者) {
c.下个处理者 = p
}
func main() {
请求处理者A := 请求处理者{Name: "请求处理A"}
请求处理者B := 请求处理者{Name: "请求处理B"}
请求处理者A.设置下一个处理者(&请求处理者B)
请求A := &请求{Name: "我是原消息"}
请求处理者A.处理请求(请求A)
}
package main
import "fmt"
//责任链模式
/*
责任链允许一个请求沿着处理者链进行一次处理.
*/
//定义处理请求的处理者
type 请求处理者接口 interface {
处理请求(p *请求)
设置下一个处理者(p 请求处理者接口)
}
//请求
type 请求 struct {
Name string
}
type 请求处理者 struct {
Name string
下个处理者 请求处理者接口
}
func (c *请求处理者) 处理请求(p *请求) {
fmt.Println(c.Name + "处理请求了")
if c.下个处理者 != nil {
c.下个处理者.处理请求(p)
}
}
func (c *请求处理者) 设置下一个处理者(p 请求处理者接口) {
c.下个处理者 = p
}
type 请求处理者B struct {
Name string
下个处理者 请求处理者接口
}
func (c *请求处理者B) 处理请求(p *请求) {
fmt.Println(c.Name + "处理请求了")
if c.下个处理者 != nil {
c.下个处理者.处理请求(p)
}
}
func (c *请求处理者B) 设置下一个处理者(p 请求处理者接口) {
c.下个处理者 = p
}
func main() {
请求处理者A1 := &请求处理者{Name: "请求处理者A1"}
请求处理者B1 := &请求处理者B{Name: "请求处理者B1"}
请求处理者A1.设置下一个处理者(请求处理者B1)
消息A := 请求{Name: "请求A"}
请求处理者A1.处理请求(&消息A)
}
package main
import "fmt"
// Request 定义请求结构
type Request struct {
content string
}
// Handler 定义处理者接口
type Handler interface {
HandleRequest(req *Request)
SetNext(handler Handler)
}
// ConcreteHandlerA 具体处理者 A
type ConcreteHandlerA struct {
next Handler
}
func (c *ConcreteHandlerA) HandleRequest(req *Request) {
if req.content == "A" {
fmt.Println("ConcreteHandlerA handles the request:", req.content)
} else if c.next != nil {
c.next.HandleRequest(req)
}
}
func (c *ConcreteHandlerA) SetNext(handler Handler) {
c.next = handler
}
// ConcreteHandlerB 具体处理者 B
type ConcreteHandlerB struct {
next Handler
}
func (c *ConcreteHandlerB) HandleRequest(req *Request) {
if req.content == "B" {
fmt.Println("ConcreteHandlerB handles the request:", req.content)
} else if c.next != nil {
c.next.HandleRequest(req)
}
}
func (c *ConcreteHandlerB) SetNext(handler Handler) {
c.next = handler
}
func main() {
// 创建具体处理者
handlerA := &ConcreteHandlerA{}
handlerB := &ConcreteHandlerB{}
// 设置责任链
handlerA.SetNext(handlerB)
// 创建请求
requestA := &Request{"A"}
requestB := &Request{"B"}
// 发送请求
handlerA.HandleRequest(requestA)
handlerA.HandleRequest(requestB)
}
命令模式
命令模式是一种行为型设计模式,它将请求封装成对象,使得可以使用不同的请求来参数化其他对象(即将请求作为参数传递)。这种模式可以让请求的发送者和接收者解耦。
主要角色:
-
命令(Command):
- 定义了执行操作的接口。
- 通常包含一个执行操作的方法(例如
Execute()
)。
-
具体命令(Concrete Command):
- 实现了命令接口,定义了将要执行的操作。
- 持有一个执行操作所需的所有信息,包括执行的对象、方法和参数等。
-
命令发送者(Invoker):
- 用于发送命令的对象,它并不直接执行命令,而是将命令发送给接收者执行。
-
命令接收者(Receiver):
- 执行实际操作的对象。
- 知道如何实现命令的具体操作。
工作原理:
- 发送者创建一个命令对象,并将其发送给接收者,接收者执行命令。
- 命令对象包含了所有必要的信息,以便接收者可以执行相应的操作。
优势:
- 解耦命令的发送者和接收者,发送者只需知道如何发送命令即可。
- 支持撤销(Undo)和重做(Redo)操作。
- 可以很容易地扩展新的命令,无需修改现有代码。
示例应用场景:
- GUI 应用程序中的菜单和工具栏操作。
- 多级撤销和重做功能。
- 任务队列系统。
在 Go 中,命令模式可以使用接口和结构体来实现。通过创建命令接口和具体命令对象,并将命令发送给接收者来执行相应的操作,可以实现这种模式。
在 Go 中实现命令模式通常会涉及接口、结构体和函数等元素。以下是一个简单的示例:
package main
import "fmt"
// Command 定义命令接口
type Command interface {
Execute()
}
// ConcreteCommandA 具体命令 A
type ConcreteCommandA struct {
receiver *Receiver
}
func (c *ConcreteCommandA) Execute() {
c.receiver.ActionA()
}
// ConcreteCommandB 具体命令 B
type ConcreteCommandB struct {
receiver *Receiver
}
func (c *ConcreteCommandB) Execute() {
c.receiver.ActionB()
}
// Receiver 命令接收者
type Receiver struct{}
func (r *Receiver) ActionA() {
fmt.Println("Receiver is performing Action A")
}
func (r *Receiver) ActionB() {
fmt.Println("Receiver is performing Action B")
}
// Invoker 命令发送者
type Invoker struct {
command Command
}
func (i *Invoker) SetCommand(command Command) {
i.command = command
}
func (i *Invoker) ExecuteCommand() {
i.command.Execute()
}
func main() {
receiver := &Receiver{}
commandA := &ConcreteCommandA{receiver: receiver}
commandB := &ConcreteCommandB{receiver: receiver}
invoker := &Invoker{}
// 设置并执行命令 A
invoker.SetCommand(commandA)
invoker.ExecuteCommand()
// 设置并执行命令 B
invoker.SetCommand(commandB)
invoker.ExecuteCommand()
}
迭代器模式
迭代器模式是一种行为型设计模式,用于提供一种访问聚合对象(如列表、数组、树等)元素的方法,而不暴露其内部结构。它允许逐个访问聚合对象中的元素,而无需暴露聚合对象的底层表示。
主要角色:
-
迭代器(Iterator):
- 定义访问和遍历元素的接口。
-
具体迭代器(Concrete Iterator):
- 实现迭代器接口,提供对具体聚合对象的遍历方式。
-
聚合对象(Aggregate):
- 定义创建迭代器对象的接口。
-
具体聚合对象(Concrete Aggregate):
- 实现聚合对象接口,创建相应的具体迭代器。
工作原理:
- 迭代器模式的核心思想是将遍历集合的责任放在迭代器上,而不是集合本身。这使得可以在不暴露集合内部结构的情况下,对集合进行遍历。
优势:
- 将遍历操作与集合对象分离,提高了灵活性和可扩展性。
- 允许多个迭代器同时存在于同一个集合上。
示例应用场景:
- 需要遍历不同数据结构的集合。
- 需要提供统一的遍历接口以方便客户端操作。
在 Go 中,迭代器模式通常可以通过定义迭代器接口和具体迭代器实现,以及聚合对象和具体聚合对象来实现。
以下是一个简单的示例
package main
import "fmt"
// Iterator 迭代器接口
type Iterator interface {
HasNext() bool
Next() interface{}
}
// ConcreteIterator 具体迭代器
type ConcreteIterator struct {
index int
data []interface{}
}
func (c *ConcreteIterator) HasNext() bool {
return c.index < len(c.data)
}
func (c *ConcreteIterator) Next() interface{} {
item := c.data[c.index]
c.index++
return item
}
// Aggregate 聚合对象接口
type Aggregate interface {
GetIterator() Iterator
}
// ConcreteAggregate 具体聚合对象
type ConcreteAggregate struct {
data []interface{}
}
func (c *ConcreteAggregate) GetIterator() Iterator {
return &ConcreteIterator{data: c.data}
}
func main() {
aggregate := &ConcreteAggregate{
data: []interface{}{"one", "two", "three", "four", "five"},
}
iterator := aggregate.GetIterator()
for iterator.HasNext() {
fmt.Println(iterator.Next())
}
}
package main
import "fmt"
//迭代器模式
/*
用于提供一种访问聚合对象(如列表、数组、树等)元素的方法,而不暴露其内部结构。
*/
中介者模式
中介者模式是一种行为型设计模式,旨在减少对象之间的直接通信,并将其归纳为通过中介者对象进行的间接通信。这种模式通过将对象解耦并使其之间的通信更加松散,有助于提高系统的可维护性和可扩展性。
主要角色:
-
中介者(Mediator):
- 定义了对象之间通信的接口。
- 可以是一个接口或者具体类,负责协调不同对象之间的交互关系。
-
具体中介者(Concrete Mediator):
- 实现中介者接口,促进不同对象之间的通信。
-
同事对象(Colleague):
- 相互通信的对象。
- 每个同事对象都知道中介者对象,并通过中介者来进行通信。
-
具体同事对象(Concrete Colleague):
- 实现了同事对象接口。
- 当需要与其他同事对象交互时,将请求发送给中介者处理。
工作原理:
- 中介者模式使得对象之间的通信变得简单,所有通信都通过中介者进行。这使得对象之间的耦合性降低,而不需要每个对象都知道其他对象的具体细节。
优势:
- 减少对象之间的直接连接,降低耦合度。
- 可以集中控制和管理对象之间的通信。
- 支持易于维护和扩展的代码。
package main
import "fmt"
type S struct {
Name string
}
func (c S) 发送(s1 S1, s string) {
s1.接收(c.Name + s)
}
type S1 struct {
Name string
}
func (c S1) 接收(s string) {
fmt.Println(c.Name + "接收成功" + s)
}
func main() {
s := S{Name: "张三"}
s1 := S1{Name: "李四"}
s.发送(s1, "消息")
}
package main
import "fmt"
type 中介者 struct {
对象集 []同事对象
}
func (c *中介者) 转发(数据 string, 对象名字 string) {
for i, v := range c.对象集 {
if v.Name == 对象名字 {
c.对象集[i].接收(数据)
}
}
}
type 同事对象 struct {
Name string
}
func (c 同事对象) 发送(中介者 中介者, 数据 string, 对象名字 string) {
中介者.转发(c.Name+数据, 对象名字)
}
func (c 同事对象) 接收(s string) {
fmt.Println(c.Name + "接收到:" + s)
}
func main() {
同事对象实例 := 同事对象{Name: "张三"}
同事对象实例1 := 同事对象{Name: "李四"}
中介者实例 := 中介者{对象集: []同事对象{同事对象实例1, 同事对象实例}}
同事对象实例.发送(中介者实例, "数据", 同事对象实例1.Name)
}
备忘录模式
备忘录模式是一种行为设计模式,它允许在不暴露对象实现细节的情况下捕获并恢复对象之前的状态。
在备忘录模式中,有三个主要角色:
-
发起者(Originator):这是需要保存状态的对象。它创建备忘录以记录其当前状态,并且可以从备忘录中恢复其状态。
-
备忘录(Memento):备忘录是状态的快照。它存储发起者对象的内部状态。备忘录应该能够保护其状态,防止外部对象访问。
-
负责人(Caretaker):负责人负责保存和管理备忘录对象,但不修改或检查其内容。它可以存储多个备忘录,并在需要时将其还原到发起者对象。
备忘录模式的关键思想是允许对象在不暴露其内部实现的情况下捕获和恢复其状态。这对于需要实现撤销、恢复或历史记录功能的应用程序非常有用。
package main
import (
"fmt"
)
// Memento 是备忘录接口
type Memento interface{}
// Originator 是发起者接口
type Originator interface {
CreateMemento() Memento
RestoreMemento(memento Memento)
SetState(state string)
GetState() string
}
// Caretaker 是负责人
type Caretaker struct {
Memento Memento
}
// ConcreteOriginator 是具体的发起者
type ConcreteOriginator struct {
state string
}
func (co *ConcreteOriginator) CreateMemento() Memento {
return co.state
}
func (co *ConcreteOriginator) RestoreMemento(memento Memento) {
co.state = memento.(string)
}
func (co *ConcreteOriginator) SetState(state string) {
co.state = state
}
func (co *ConcreteOriginator) GetState() string {
return co.state
}
func main() {
originator := &ConcreteOriginator{}
// 设置状态并保存备忘录
originator.SetState("State 1")
caretaker := Caretaker{Memento: originator.CreateMemento()}
// 修改状态并恢复备忘录
originator.SetState("State 2")
fmt.Println("Current State:", originator.GetState()) // Output: Current State: State 2
originator.RestoreMemento(caretaker.Memento)
fmt.Println("Restored State:", originator.GetState()) // Output: Restored State: State 1
}
状态模式
状态模式是一种行为设计模式,用于实现状态机。它允许对象在内部状态改变时改变其行为,看起来像是改变了其类。
在状态模式中,有三个核心角色:
-
Context(上下文):它是拥有状态的对象。在状态模式中,它会持有当前状态对象的引用,并且在状态发生变化时,会委托给当前状态对象处理行为。
-
State(状态):状态是状态模式的关键部分。它定义了对象在特定状态下的行为。在状态模式中,状态对象通常会实现一个共同的接口,以便上下文可以切换和委托给不同的状态对象。
-
ConcreteState(具体状态):具体状态是状态的具体实现。每个具体状态都会实现状态接口,并定义了在该状态下对象应该做的事情。
状态模式的核心思想是将对象的状态抽象成一个独立的对象,并且让对象在状态发生变化时改变其行为。这种模式使得对象的状态转换变得更加清晰,并且可以避免使用大量的条件语句来控制对象行为的变化。
当在Go中实现状态模式时,可以使用接口来定义状态和上下文的行为,并使用具体的结构体来实现这些接口。以下是一个简单的状态模式的示例:
package main
import "fmt"
// State 是状态接口
type State interface {
Handle() // 处理状态的方法
}
// Context 是上下文,持有状态对象的引用
type Context struct {
state State
}
// SetState 设置状态
func (c *Context) SetState(s State) {
c.state = s
}
// Request 发起请求
func (c *Context) Request() {
c.state.Handle()
}
// ConcreteStateA 是具体的状态A
type ConcreteStateA struct{}
func (s *ConcreteStateA) Handle() {
fmt.Println("Handling request in State A")
}
// ConcreteStateB 是具体的状态B
type ConcreteStateB struct{}
func (s *ConcreteStateB) Handle() {
fmt.Println("Handling request in State B")
}
func main() {
context := &Context{}
stateA := &ConcreteStateA{}
stateB := &ConcreteStateB{}
// 设置初始状态为 StateA
context.SetState(stateA)
context.Request() // Output: Handling request in State A
// 切换状态到 StateB
context.SetState(stateB)
context.Request() // Output: Handling request in State B
}
策略模式
策略模式是一种行为设计模式,它定义了一系列算法,并使它们可以互相替换,从而使得算法的变化独立于使用它们的客户端。
在策略模式中,有三个主要角色:
-
Context(上下文):它是使用策略的类。上下文可以在运行时改变其策略。
-
Strategy(策略):策略是定义了一组算法的接口或抽象类。所有的具体策略都实现了该接口或继承自该抽象类。
-
ConcreteStrategy(具体策略):具体策略是策略模式的具体实现。每个具体策略都实现了策略接口,并提供了自己的算法实现。
在Go中,策略模式可以使用接口定义策略,然后通过具体的结构体来实现这些策略。以下是一个简单的策略模式示例:
package main
import "fmt"
// Strategy 是策略接口
type Strategy interface {
Execute()
}
// Context 是上下文,持有策略对象的引用
type Context struct {
strategy Strategy
}
// SetStrategy 设置策略
func (c *Context) SetStrategy(s Strategy) {
c.strategy = s
}
// ExecuteStrategy 执行策略
func (c *Context) ExecuteStrategy() {
c.strategy.Execute()
}
// ConcreteStrategyA 是具体的策略A
type ConcreteStrategyA struct{}
func (s *ConcreteStrategyA) Execute() {
fmt.Println("Executing strategy A")
}
// ConcreteStrategyB 是具体的策略B
type ConcreteStrategyB struct{}
func (s *ConcreteStrategyB) Execute() {
fmt.Println("Executing strategy B")
}
func main() {
context := &Context{}
strategyA := &ConcreteStrategyA{}
strategyB := &ConcreteStrategyB{}
// 设置初始策略为 StrategyA
context.SetStrategy(strategyA)
context.ExecuteStrategy() // Output: Executing strategy A
// 切换策略到 StrategyB
context.SetStrategy(strategyB)
context.ExecuteStrategy() // Output: Executing strategy B
}
模板方法模式
模板方法模式是一种行为设计模式,它定义了一个算法的框架,并允许子类重写某些步骤以提供特定的行为实现,同时保持算法的整体结构不变。
在模板方法模式中,有两个核心角色:
-
模板(Abstract Class):它是定义了算法框架的抽象类或接口。它包含了算法的骨架,其中一些步骤可能是抽象的,由子类来实现。
-
具体类(Concrete Class):具体类继承自模板并实现了模板中的抽象方法,以提供具体的实现。
在Go中,由于语言本身不支持继承,可以通过组合和接口的方式实现类似的效果。以下是一个简单的模板方法模式的示例:
package main
import "fmt"
// AbstractClass 是模板接口
type AbstractClass interface {
TemplateMethod()
}
// ConcreteClass 是具体实现类
type ConcreteClass struct{}
func (c *ConcreteClass) TemplateMethod() {
c.primitiveOperation1()
c.primitiveOperation2()
}
// primitiveOperation1 是模板方法中的具体步骤1
func (c *ConcreteClass) primitiveOperation1() {
fmt.Println("Primitive Operation 1")
}
// primitiveOperation2 是模板方法中的具体步骤2
func (c *ConcreteClass) primitiveOperation2() {
fmt.Println("Primitive Operation 2")
}
func main() {
concrete := &ConcreteClass{}
concrete.TemplateMethod()
}
访问者模式
访问者模式是一种行为设计模式,用于对一组对象的操作进行封装。它允许你为一个对象结构定义新的操作,而无需修改对象本身。
在访问者模式中,有一些核心角色:
-
Visitor(访问者):访问者是一个接口,定义了对不同类型元素的访问操作。
-
Element(元素):元素是一个接口,它定义了接受访问者的操作。
-
ConcreteVisitor(具体访问者):具体访问者是实现了访问者接口的具体对象。它包含了对不同元素的访问操作。
-
ConcreteElement(具体元素):具体元素是实现了元素接口的具体对象。
在Go中,由于缺少传统的继承方式,我们可以使用接口和组合来模拟访问者模式。以下是一个简单的访问者模式的示例:
package main
import "fmt"
// Element 是元素接口
type Element interface {
Accept(visitor Visitor)
}
// ConcreteElementA 是具体元素A
type ConcreteElementA struct{}
func (e *ConcreteElementA) Accept(visitor Visitor) {
visitor.VisitConcreteElementA(e)
}
// ConcreteElementB 是具体元素B
type ConcreteElementB struct{}
func (e *ConcreteElementB) Accept(visitor Visitor) {
visitor.VisitConcreteElementB(e)
}
// Visitor 是访问者接口
type Visitor interface {
VisitConcreteElementA(element *ConcreteElementA)
VisitConcreteElementB(element *ConcreteElementB)
}
// ConcreteVisitor 是具体访问者
type ConcreteVisitor struct{}
func (v *ConcreteVisitor) VisitConcreteElementA(element *ConcreteElementA) {
fmt.Println("Visitor visited ConcreteElementA")
}
func (v *ConcreteVisitor) VisitConcreteElementB(element *ConcreteElementB) {
fmt.Println("Visitor visited ConcreteElementB")
}
func main() {
visitor := &ConcreteVisitor{}
elementA := &ConcreteElementA{}
elementB := &ConcreteElementB{}
elementA.Accept(visitor) // Output: Visitor visited ConcreteElementA
elementB.Accept(visitor) // Output: Visitor visited ConcreteElementB
}
//在这个示例中,Element 接口定义了元素的 Accept 方法,ConcreteElementA 和 ConcreteElementB 结构体实现了这个接口。Visitor 接口定义了访问者的访问方法,ConcreteVisitor 结构体实现了这个接口,并提供了对不同元素的访问操作。在 main() 函数中,演示了如何创建元素并接受访问者访问的过程
//一组对象对不用的访问者访问不同的内容