}
* 输出结果
代理访问控制器:用户 user1 可以访问资源 resource1
代理访问控制器:用户 user2 无权访问资源 resource1
代理访问控制器:用户 user2 可以访问资源 resource2
* 动态代理示例
package main
import (
“fmt”
“reflect”
)
// Subject 定义了主题接口
type Subject interface {
Request() string
}
// RealSubject 实现了主题接口
type RealSubject struct{}
func (rs *RealSubject) Request() string {
return “RealSubject: 处理请求”
}
// Proxy 代理对象,使用反射动态代理 RealSubject
type Proxy struct {
realSubject *RealSubject // 代理对象内部包含一个真实对象的引用
}
// Request 是 Proxy 结构体的方法,使用反射动态代理 RealSubject 的 Request 方法
func (p *Proxy) Request() string {
// 在调用真实对象方法之前执行一些额外的操作,例如记录日志
fmt.Println(“代理对象:记录日志”)
// 通过反射获取 RealSubject 对象的方法集合
realSubjectValue := reflect.ValueOf(p.realSubject).Elem()
// 获取 RealSubject 的 Request 方法
requestMethod := realSubjectValue.MethodByName("Request")
// 如果 RealSubject 的 Request 方法存在,则调用该方法
if requestMethod.IsValid() && requestMethod.Type().NumIn() == 0 && requestMethod.Type().NumOut() == 1 && requestMethod.Type().Out(0).Kind() == reflect.String {
result := requestMethod.Call([]reflect.Value{})
// 在调用真实对象方法之后执行一些额外的操作,例如记录日志
fmt.Println("代理对象:记录日志")
return result[0].String()
}
// 如果 RealSubject 的 Request 方法不存在,则返回错误信息
return "代理对象:无法调用方法"
}
func main() {
proxy := &Proxy{realSubject: &RealSubject{}}
result := proxy.Request()
fmt.Println(result)
}
* 输出结果
理对象:记录日志
代理对象:无法调用方法
## 门面模式
门面模式是一种结构型设计模式,旨在为复杂系统提供一个简单统一的接口,以隐藏系统内部的复杂性,使客户端只需要与一个门面对象交互,而不需要了解系统内部的具体实现细节。
在门面模式中,门面(Facade)充当了客户端与子系统之间的中介,客户端通过门面对象间接地与子系统进行交互,而不必直接与子系统的多个对象打交道。门面对象封装了对子系统的调用,向客户端提供了一个简单的接口,客户端可以通过调用门面提供的方法来完成复杂的操作,而无需了解子系统内部的复杂逻辑和结构。
1. 门面(Facade):门面是客户端访问子系统的入口,它封装了对子系统的调用,提供了一个简单的接口供客户端使用。门面对象通常负责协调和委派客户端的请求给子系统中的具体对象。
2. 子系统(Subsystem):子系统是构成整个系统的各个组成部分,它们负责完成系统的各种具体任务。子系统中的对象可能包括多个不同的类和接口。
* 场景
1. API设计:在构建Web服务或RESTful API时,可以使用门面模式将多个服务封装成一个简单的接口。这样客户端只需要与门面对象交互,而不需要了解后台服务的复杂性和细节。
2. 配置管理:当系统中涉及到多个配置文件、环境变量或远程配置服务时,可以使用门面模式封装配置管理逻辑,提供一个统一的接口给其他模块使用。
3. 数据存储:在使用多种数据存储方案(如数据库、缓存、文件存储等)时,可以使用门面模式将这些存储方案封装成一个统一的接口,使得客户端可以通过同一接口访问不同的数据存储。
4. 认证和授权:在构建身份验证和授权系统时,可以使用门面模式封装认证和授权逻辑,提供一个简单的接口给其他模块调用,同时隐藏具体的认证和授权细节。
5. 日志记录:在系统中加入日志记录功能时,可以使用门面模式封装日志记录器,提供一个简单的接口给其他模块使用,同时支持灵活的日志级别设置和日志输出格式。
6. 缓存管理:当系统中需要使用缓存时,可以使用门面模式封装缓存管理逻辑,提供一个统一的接口给其他模块使用,同时支持不同类型的缓存存储和缓存策略。
7. 消息队列:在系统中使用消息队列进行异步通信时,可以使用门面模式封装消息队列客户端,提供一个简单的接口给其他模块使用,同时隐藏底层消息队列的细节。
* 优点
1. 简化接口:门面模式提供了一个简单的接口,隐藏了系统内部的复杂性,使客户端可以更轻松地使用系统。这样可以降低学习成本,提高系统的易用性。
2. 解耦客户端和子系统:门面模式将客户端和子系统解耦,客户端只需要与门面对象交互,而不需要了解子系统的内部结构和实现细节。这样可以降低系统的耦合度,提高系统的灵活性和可维护性。
3. 隐藏实现细节:门面模式可以隐藏系统内部的实现细节,只暴露必要的接口给客户端使用。这样可以降低系统的复杂度,提高系统的安全性,防止客户端直接访问系统内部的敏感信息。
4. 统一入口:门面模式提供了一个统一的入口,可以将多个复杂的子系统封装成一个简单的接口,从而简化了系统的调用方式,提高了系统的一致性和可维护性。
* 缺点
1. 违反开闭原则:门面模式可能会导致门面对象变得过于庞大,如果系统内部的实现发生变化,可能需要修改门面对象的代码,这可能会违反开闭原则,导致需要修改客户端的代码。
2. 隐藏系统内部细节:门面模式虽然隐藏了系统内部的复杂性,但也可能导致客户端无法直接访问系统内部的详细信息,这可能会限制客户端对系统的灵活性和定制能力。
3. 增加了系统复杂性:引入门面模式会增加系统的复杂性,需要额外的门面对象来封装子系统的接口,并且可能需要处理门面对象和子系统之间的关系,这可能会增加系统的理解和维护难度。
4. 性能损耗:由于门面对象通常会涉及多个子系统对象的调用,可能会引入一定的性能损耗,特别是在需要频繁调用的情况下。因此,在性能要求高的场景中,需要谨慎使用门面模式。
* 示例
package main
import “fmt”
// 子系统1
type SubSystem1 struct{}
func (s *SubSystem1) Operation1() string {
return “子系统1:执行操作1”
}
func (s *SubSystem1) Operation2() string {
return “子系统1:执行操作2”
}
// 子系统2
type SubSystem2 struct{}
func (s *SubSystem2) Operation1() string {
return “子系统2:执行操作1”
}
func (s *SubSystem2) Operation2() string {
return “子系统2:执行操作2”
}
// 门面
type Facade struct {
subSystem1 *SubSystem1
subSystem2 *SubSystem2
}
func NewFacade() *Facade {
return &Facade{
subSystem1: &SubSystem1{},
subSystem2: &SubSystem2{},
}
}
// 封装了对子系统的访问
func (f *Facade) Operation() string {
result := “门面:开始操作\n”
result += f.subSystem1.Operation1() + “\n”
result += f.subSystem1.Operation2() + “\n”
result += f.subSystem2.Operation1() + “\n”
result += f.subSystem2.Operation2() + “\n”
result += “门面:操作结束”
return result
}
func main() {
facade := NewFacade()
result := facade.Operation()
fmt.Println(result)
}
* 输出结果
门面:开始操作
子系统1:执行操作1
子系统1:执行操作2
子系统2:执行操作1
子系统2:执行操作2
门面:操作结束
## 桥接模式
桥接模式是一种结构型设计模式,旨在将抽象部分与实现部分分离,使它们可以独立变化。桥接模式通过创建一个桥接接口,将抽象和实现分离,使得它们可以独立地变化,而不会相互影响。
1. 抽象部分(Abstraction):定义了抽象部分的接口,并维护一个指向实现部分的引用。抽象部分将客户端与实现部分分离,使客户端可以独立地访问抽象部分,而不需要了解其具体实现。
2. 扩展抽象部分(RefinedAbstraction):对抽象部分的扩展,可以添加新的功能或修改现有功能,但仍然依赖于实现部分。
3. 实现部分(Implementor):定义了实现部分的接口,并提供了具体的实现。实现部分通常是一个接口或抽象类,定义了实现部分的操作。
4. 具体实现部分(ConcreteImplementor):对实现部分的具体实现,实现了实现部分接口定义的具体操作。
* 场景
1. 多平台支持:当需要在多个平台上运行相似但又有差异的代码时,可以使用桥接模式。通过将抽象部分和实现部分分离,可以针对不同的平台实现不同的具体实现部分,从而实现跨平台支持。
2. 数据库驱动程序:在编写数据库驱动程序时,桥接模式可以用于将数据库操作和底层数据库引擎分离。抽象部分可以定义通用的数据库操作接口,而具体实现部分可以针对不同的数据库引擎实现具体的数据库操作。
3. 操作系统的适配器:在编写与操作系统相关的代码时,可以使用桥接模式将操作系统相关的部分和业务逻辑分离。这样可以在不同的操作系统上运行相同的业务逻辑,只需要针对不同的操作系统实现不同的具体实现部分。
4. 图形界面库:在编写图形界面库时,桥接模式可以用于将界面元素和界面样式分离。抽象部分可以定义通用的界面元素接口,而具体实现部分可以针对不同的界面样式实现具体的界面元素。
5. 远程服务调用:在编写远程服务调用的客户端时,可以使用桥接模式将远程服务调用和底层网络通信分离。抽象部分可以定义通用的远程服务接口,而具体实现部分可以针对不同的网络协议实现具体的远程服务调用。
* 优点
1. 解耦抽象和实现:桥接模式可以将抽象部分和实现部分分离,使它们可以独立变化。这样可以降低系统的耦合度,提高系统的灵活性和可维护性。
2. 可扩展性:桥接模式可以通过添加新的抽象部分或具体实现部分来扩展系统,而不会影响现有的代码。这样可以提高系统的可扩展性,使得系统更易于扩展和维护。
3. 隐藏实现细节:桥接模式可以隐藏系统内部的实现细节,只暴露必要的接口给客户端使用。这样可以降低系统的复杂度,提高系统的安全性,防止客户端直接访问系统内部的敏感信息。
4. 提高复用性:桥接模式可以将抽象部分和实现部分分离,使它们可以在不同的组合下进行重用。这样可以提高代码的复用性,减少重复代码的编写。
* 缺点
1. 增加代码复杂度:引入桥接模式会增加系统的复杂度,需要额外的抽象部分和具体实现部分来进行分离,可能会增加系统的理解和维护难度。
2. 可能导致过度设计:如果系统中只有一个抽象部分和一个具体实现部分,引入桥接模式可能会导致过度设计,增加不必要的复杂性。
3. 需要额外的开发工作:实现桥接模式需要额外的开发工作,包括定义抽象部分和具体实现部分的接口,以及编写桥接类来将它们连接起来。这可能会增加项目的开发成本和时间。
4. 可能引入性能损耗:由于桥接模式通常涉及多个类之间的组合和交互,可能会引入一定的性能损耗。特别是在需要频繁调用的情况下,可能会影响系统的性能表现。
* 示例
package main
import “fmt”
// 实现部分接口
type Drawer interface {
Draw()
}
// 具体实现部分1:SVG
type SVG struct{}
func (s *SVG) Draw() {
fmt.Println(“使用SVG绘制图形”)
}
// 具体实现部分2:Canvas
type Canvas struct{}
func (c *Canvas) Draw() {
fmt.Println(“使用Canvas绘制图形”)
}
// 抽象部分
type Shape interface {
DrawShape()
}
// 扩展抽象部分1:矩形
type Rectangle struct {
drawer Drawer
}
func (r *Rectangle) DrawShape() {
fmt.Println(“绘制矩形:”)
r.drawer.Draw()
}
// 扩展抽象部分2:圆形
type Circle struct {
drawer Drawer
}
func (c *Circle) DrawShape() {
fmt.Println(“绘制圆形:”)
c.drawer.Draw()
}
func main() {
// 使用SVG绘制矩形
svgDrawer := &SVG{}
rectangle := &Rectangle{drawer: svgDrawer}
rectangle.DrawShape()
// 使用Canvas绘制圆形
canvasDrawer := &Canvas{}
circle := &Circle{drawer: canvasDrawer}
circle.DrawShape()
}
* 输出结果
绘制矩形:
使用SVG绘制图形
绘制圆形:
使用Canvas绘制图形
## 适配器模式
适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端所期望的另一个接口。适配器模式通常用于系统需要使用已有的类,但是这些类的接口与系统的需求不匹配的情况下。适配器模式允许这些类可以协同工作,而无需修改其源代码。
* 场景
1. 集成第三方库:当系统需要使用第三方库提供的功能,但是第三方库的接口与系统的需求不匹配时,可以使用适配器模式将第三方库适配到系统所需的接口。
2. 兼容不同版本的接口:当系统需要与不同版本的接口进行交互时,可以使用适配器模式来将不同版本的接口适配到统一的接口,从而使系统可以适配不同版本的接口。
3. 兼容不同的数据格式:当系统需要与不同的数据格式进行交互时,可以使用适配器模式来将不同的数据格式适配到统一的数据格式,从而使系统可以处理不同的数据格式。
4. 适配旧接口到新接口:当系统需要升级到新的接口,但是现有的代码使用的是旧的接口时,可以使用适配器模式将旧的接口适配到新的接口,从而实现平滑过渡。
5. 适配异步接口到同步接口:当系统需要与异步接口进行交互,但是现有的代码使用的是同步接口时,可以使用适配器模式将异步接口适配到同步接口,从而简化系统的调用方式。
6. 适配不同的通信协议:当系统需要与不同的通信协议进行交互时,可以使用适配器模式将不同的通信协议适配到统一的通信协议,从而使系统可以适配不同的通信协议。
* 优点
1. 解耦性强:适配器模式可以将客户端与被适配者对象解耦,客户端只需要与适配器对象交互,而无需直接与被适配者对象交互。这样可以降低系统的耦合度,提高系统的灵活性和可维护性。
2. 复用性高:适配器模式可以重用现有的类或对象,而无需修改其源代码。通过创建不同的适配器对象,可以适配不同的被适配者对象,从而提高代码的复用性。
3. 扩展性好:适配器模式可以通过创建新的适配器对象来适配新的被适配者对象,而无需修改现有的代码。这样可以提高系统的扩展性,使得系统更易于扩展和维护。
4. 灵活性高:适配器模式可以在不修改现有代码的情况下集成新的功能。通过创建不同的适配器对象,可以适配不同的接口或实现不同的功能,从而使系统更加灵活。
* 缺点
1. 过多的适配器对象:如果系统中存在大量的适配器对象,可能会导致系统的复杂度增加。特别是在适配器对象数量较多或者适配器对象功能较复杂时,可能会增加系统的理解和维护难度。
2. 增加代码量:适配器模式引入了额外的适配器对象,可能会增加系统的代码量。特别是在适配器对象较多或者适配器对象功能较复杂时,可能会增加项目的开发成本和时间。
3. 性能损耗:适配器模式通常涉及将客户端的请求转发给被适配者对象,可能会引入一定的性能损耗。特别是在需要频繁调用的情况下,可能会影响系统的性能表现。
* 示例
package main
import “fmt”
// 旧的日志库接口
type OldLogger interface {
Log(message string)
}
// 旧的日志库实现
type SimpleOldLogger struct{}
func (s *SimpleOldLogger) Log(message string) {
fmt.Println(“旧的日志库:”, message)
}
// 新的日志库接口
type NewLogger interface {
PrintLog(msg string)
}
// 新的日志库实现
type FancyNewLogger struct{}
func (f *FancyNewLogger) PrintLog(msg string) {
fmt.Println(“新的日志库:”, msg)
}
// 适配器:将新的日志库接口适配到旧的日志库接口
type Adapter struct {
newLogger NewLogger
}
func (a *Adapter) Log(message string) {
// 在适配器中调用新的日志库接口来记录日志
a.newLogger.PrintLog(message)
}
func main() {
// 使用新的日志库创建适配器
newLogger := &FancyNewLogger{}
adapter := &Adapter{newLogger: newLogger}
// 使用旧的日志库接口来记录日志
adapter.Log("这是一条新的日志消息")
}
* 输出结果
新的日志库: 这是一条新的日志消息
## 外观模式
外观模式是一种结构型设计模式,旨在提供一个统一的接口,用于访问系统的一组接口或子系统。外观模式隐藏了系统的复杂性,使客户端可以通过一个简单的接口来访问系统的功能。这种模式类似于现实生活中的一个外观(门面),客户端只需要通过门面来与系统交互,而无需了解系统的内部实现。
* 场景
1. 简化复杂系统的调用:当系统中存在多个复杂的子系统,并且客户端需要频繁调用这些子系统的功能时,可以使用外观模式来提供一个简单的接口,将所有复杂的子系统功能封装起来,使客户端可以通过一个简单的接口来访问系统的功能。
2. 隐藏系统的复杂性:当系统的内部实现过于复杂,或者系统的接口过于繁琐时,可以使用外观模式来隐藏系统的实现细节,只暴露必要的接口给客户端使用,从而降低客户端对系统的认知和学习成本。
3. 统一系统的接口:当系统中存在多个不同的子系统,并且这些子系统具有不同的接口时,可以使用外观模式来提供一个统一的接口,使客户端可以通过一个统一的接口来访问所有子系统的功能,从而简化系统的调用方式。
4. 适配现有接口:当系统需要与外部的类、库或服务进行交互时,但是外部类、库或服务的接口与系统的需求不匹配时,可以使用外观模式来提供一个适配器,将外部接口适配到系统所需的接口。
5. 封装系统的复杂性:当系统中存在多个复杂的子系统,并且这些子系统之间存在依赖关系时,可以使用外观模式来封装系统的复杂性,将子系统之间的关系隐藏起来,使客户端无需了解系统的内部结构和实现细节。
* 优点
1. 简化客户端调用:外观模式提供了一个简单的接口,隐藏了系统的复杂性,使客户端可以更轻松地使用系统。客户端只需要通过外观对象来访问系统的功能,而无需了解系统的内部实现。
2. 解耦客户端和子系统:外观模式将客户端和子系统解耦,客户端只需要与外观对象交互,而无需了解子系统的内部结构和实现细节。这样可以降低系统的耦合度,提高系统的灵活性和可维护性。
3. 隐藏实现细节:外观模式可以隐藏系统的实现细节,只暴露必要的接口给客户端使用。这样可以降低系统的复杂度,提高系统的安全性,防止客户端直接访问系统内部的敏感信息。
4. 统一入口:外观模式提供了一个统一的入口,客户端可以通过外观对象来访问系统的功能。这样可以简化系统的调用方式,提高系统的一致性和可维护性。
* 缺点
1. 违反开闭原则:外观模式可能会导致外观对象变得过于庞大,如果系统的功能发生变化,可能需要修改外观对象的代码,这可能会违反开闭原则,导致需要修改客户端的代码。
2. 隐藏系统内部细节:外观模式虽然隐藏了系统的复杂性,但也可能导致客户端无法直接访问系统的详细信息,这可能会限制客户端对系统的灵活性和定制能力。
3. 增加系统复杂性:引入外观模式会增加系统的复杂性,需要额外的外观对象来封装子系统的接口,并且可能需要处理外观对象和子系统之间的关系,这可能会增加系统的理解和维护难度。
4. 性能损耗:外观模式通常涉及将客户端的请求转发给子系统,可能会引入一定的性能损耗。特别是在需要频繁调用的情况下,可能会影响系统的性能表现。
* 示例
package main
import “fmt”
// 子系统1:音频播放器
type AudioPlayer struct{}
func (a *AudioPlayer) PlayAudio(file string) {
fmt.Printf(“播放音频文件:%s\n”, file)
}
// 子系统2:视频播放器
type VideoPlayer struct{}
func (v *VideoPlayer) PlayVideo(file string) {
fmt.Printf(“播放视频文件:%s\n”, file)
}
// 子系统3:其他功能
type ExtraFeatures struct{}
func (e *ExtraFeatures) FastForward() {
fmt.Println(“快进”)
}
func (e *ExtraFeatures) Rewind() {
fmt.Println(“快退”)
}
func (e *ExtraFeatures) Pause() {
fmt.Println(“暂停”)
}
// 外观对象:多媒体播放器
type MultimediaPlayer struct {
audioPlayer *AudioPlayer
videoPlayer *VideoPlayer
extra *ExtraFeatures
}
func NewMultimediaPlayer() *MultimediaPlayer {
return &MultimediaPlayer{
audioPlayer: &AudioPlayer{},
videoPlayer: &VideoPlayer{},
extra: &ExtraFeatures{},
}
}
func (m *MultimediaPlayer) PlayAudio(file string) {
m.audioPlayer.PlayAudio(file)
}
func (m *MultimediaPlayer) PlayVideo(file string) {
m.videoPlayer.PlayVideo(file)
}
func (m *MultimediaPlayer) FastForward() {
m.extra.FastForward()
}
func (m *MultimediaPlayer) Rewind() {
m.extra.Rewind()
}
func (m *MultimediaPlayer) Pause() {
m.extra.Pause()
}
func main() {
// 创建多媒体播放器
player := NewMultimediaPlayer()
// 使用外观对象播放音频和视频,并执行其他功能
player.PlayAudio("music.mp3")
player.PlayVideo("movie.mp4")
player.FastForward()
player.Pause()
}
* 输出结果
播放音频文件:music.mp3
播放视频文件:movie.mp4
快进
暂停
## 享元模式
享元模式是一种结构型设计模式,旨在通过共享相似对象之间的公共部分来最大程度地减少内存使用或计算开销。在该模式中,将对象的部分状态(内部状态)和外部状态(外部环境)进行区分,内部状态可以被多个对象共享,而外部状态则由对象单独管理。
* 场景
1. 大量相似对象的重复创建:当系统中存在大量相似对象,且这些对象之间的区别仅在于部分内部状态或外部状态时,可以使用享元模式来共享相同的状态,从而减少内存消耗。
2. 对象的创建和销毁开销较大:当对象的创建和销毁需要消耗大量资源或时间时,可以使用享元模式来减少对象的创建和销毁次数,提高系统的性能。
3. 细粒度对象的内存优化:当需要对大量细粒度对象进行内存优化时,可以使用享元模式来共享相同的对象,减少内存消耗。
4. 缓存对象的复用:当需要缓存对象以便重复使用时,可以使用享元模式来管理对象的缓存池,从而提高系统的性能和资源利用率。
5. 文本编辑器、图像编辑器等工具软件:在文本编辑器、图像编辑器等工具软件中,存在大量相似的文本字符、图形对象等,可以使用享元模式来共享相同的对象,减少内存消耗。
* 优点
1. 减少内存消耗: 享元模式通过共享相似对象之间的公共部分,可以大幅减少内存使用。相同的对象可以被多个地方共享使用,而不是每次都创建新的对象。
2. 提高性能: 由于减少了对象的创建和销毁次数,以及减少了内存消耗,因此可以提高系统的性能。共享的对象可以重复使用,不需要频繁地创建和销毁。
3. 提高系统灵活性: 外部状态由客户端管理,可以灵活地进行配置和修改。享元模式将对象的内部状态和外部状态进行分离,使得系统更加灵活和可扩展。
* 缺点
1. 增加复杂性: 需要额外的工厂类来管理享元对象,可能会增加系统的复杂性。享元模式引入了共享对象和非共享对象的概念,需要客户端来管理外部状态,可能会增加系统的理解和维护难度。
2. 共享状态的限制: 对于外部状态的处理比较麻烦,可能会限制对象的共享状态的范围。外部状态由客户端管理,可能会导致对象的共享状态的范围受到限制,无法满足所有的需求。
3. 线程安全性: 如果享元对象是可变的,并且被多个线程共享访问,可能会存在线程安全性问题。需要考虑在享元对象的内部状态和外部状态上加锁来保证线程安全性。
* 示例
package main
import “fmt”
// 图形类型常量
const (
RectangleType = “矩形”
CircleType = “圆形”
LineType = “线条”
)
// 图形接口
type Shape interface {
Draw()
}
// 具体图形对象:矩形
type Rectangle struct {
color string // 颜色
}
func NewRectangle(color string) *Rectangle {
return &Rectangle{
color: color,
}
}
func (r *Rectangle) Draw() {
fmt.Printf(“绘制%s,颜色:%s\n”, RectangleType, r.color)
}
// 具体图形对象:圆形
type Circle struct {
color string // 颜色
}
func NewCircle(color string) *Circle {
return &Circle{
color: color,
}
}
func (c *Circle) Draw() {
fmt.Printf(“绘制%s,颜色:%s\n”, CircleType, c.color)
}
// 具体图形对象:线条
type Line struct {
color string // 颜色
}
func NewLine(color string) *Line {
return &Line{
color: color,
}
}
func (l *Line) Draw() {
fmt.Printf(“绘制%s,颜色:%s\n”, LineType, l.color)
}
// 图形工厂
type ShapeFactory struct {
shapes map[string]Shape // 缓存已创建的图形对象
}
func NewShapeFactory() *ShapeFactory {
return &ShapeFactory{
shapes: make(map[string]Shape),
}
}
func (f *ShapeFactory) GetShape(shapeType, color string) Shape {
key := shapeType + “_” + color
if _, ok := f.shapes[key]; !ok {
switch shapeType {
case RectangleType:
f.shapes[key] = NewRectangle(color)
case CircleType:
f.shapes[key] = NewCircle(color)
case LineType:
f.shapes[key] = NewLine(color)
}
}
return f.shapes[key]
}
func main() {
// 创建图形工厂
shapeFactory := NewShapeFactory()
// 获取并绘制不同类型的图形对象
shape1 := shapeFactory.GetShape(RectangleType, "红色")
shape1.Draw()
shape2 := shapeFactory.GetShape(CircleType, "绿色")
shape2.Draw()
shape3 := shapeFactory.GetShape(LineType, "蓝色")
shape3.Draw()
shape4 := shapeFactory.GetShape(RectangleType, "红色")
shape4.Draw()
shape5 := shapeFactory.GetShape(CircleType, "绿色")
shape5.Draw()
}
* 输出结果
绘制矩形,颜色:红色
绘制圆形,颜色:绿色
绘制线条,颜色:蓝色
绘制矩形,颜色:红色
绘制圆形,颜色:绿色
## 装饰器模式
装饰器模式是一种结构型设计模式,它允许动态地向对象添加额外的功能,而无需修改对象的代码。装饰器模式通过创建一个包装对象来实现,该包装对象包含了原始对象,并在其上附加了额外的功能。
装饰器模式的核心思想是将对象的功能划分为核心功能和附加功能,并将附加功能封装在装饰器对象中。这种方式使得我们可以动态地向对象添加新的功能,而不需要修改原始对象的代码。装饰器模式可以大大减少类的数量,使得代码更加灵活和可维护。
1. 组件接口(Component): 定义了被装饰对象和装饰器对象的公共接口。该接口可以是抽象类或接口,其中包含了原始对象的方法。
2. 具体组件(ConcreteComponent): 实现了组件接口的具体对象,即被装饰对象。具体组件是装饰器模式中最基本的对象,它定义了最基本的行为。
3. 装饰器(Decorator): 实现了组件接口的装饰器对象。装饰器持有一个指向组件对象的引用,并在其上附加额外的功能。装饰器与组件接口相同,因此可以使用装饰器替换原始对象。
4. 具体装饰器(ConcreteDecorator): 扩展了装饰器接口的具体对象。具体装饰器可以添加额外的功能,例如对原始对象的行为进行扩展或修改。
* 场景
1. 动态地扩展功能: 当需要动态地向对象添加新的功能,而不希望修改现有对象的代码时,可以使用装饰器模式。例如,在不修改原始文件读取器代码的情况下,可以使用装饰器模式向文件读取器添加缓冲区、加密、解压等功能。
2. 多个功能的组合: 当需要组合多个功能时,可以使用装饰器模式。通过创建多个装饰器对象,并按照需要组合它们,可以灵活地实现不同功能的组合。例如,在图形编辑器中,可以使用装饰器模式组合图形对象的绘制、填充、边框等功能。
3. 避免子类的爆炸: 当存在大量相似但略有不同的对象时,可以使用装饰器模式来避免子类的爆炸。通过将相似的功能封装在装饰器对象中,并动态地组合它们,可以减少类的数量,使得代码更加清晰和简洁。
4. 动态配置对象: 当需要根据不同的配置动态地创建对象时,可以使用装饰器模式。通过创建不同的装饰器对象,并按照需要组合它们,可以动态地配置对象的功能。例如,在网络请求中,可以使用装饰器模式动态地配置请求的超时时间、重试策略、日志记录等功能。
* 优点
1. 动态地扩展功能: 装饰器模式允许在不修改现有对象代码的情况下,动态地向对象添加新的功能。通过创建一个包装对象并在其上附加额外的功能,可以动态地扩展对象的功能。
2. 遵循开闭原则: 装饰器模式遵循开闭原则,即对扩展开放,对修改关闭。通过将功能划分为核心功能和附加功能,并将附加功能封装在装饰器对象中,可以避免修改原始对象的代码。
3. 灵活性和可复用性: 装饰器模式可以大大提高代码的灵活性和可复用性。通过将装饰器对象与原始对象解耦,可以方便地组合不同的装饰器对象,从而实现不同的功能组合。
4. 简化类的设计: 装饰器模式可以大大减少类的数量,使得代码更加简洁和清晰。通过将相似的功能封装在装饰器对象中,可以避免创建大量的子类和接口。
* 缺点
1. 增加复杂性: 装饰器模式会增加系统的复杂性,特别是在存在多个装饰器对象的情况下。由于装饰器对象与原始对象具有相同的接口,因此在使用装饰器模式时需要仔细考虑对象之间的交互关系。
2. 可能产生过多的对象: 装饰器模式可能会产生大量的装饰器对象,特别是在存在多层装饰器的情况下。这可能会导致对象数量的急剧增加,从而增加系统的内存消耗。
3. 不易理解和调试: 装饰器模式可能会使代码变得更加复杂,从而增加理解和调试的难度。由于装饰器对象与原始对象具有相同的接口,因此在查找和调试问题时可能会比较困难。
* 示例
package main
import “fmt”
// 咖啡接口
type Coffee interface {
Description() string
Cost() float64
}
// 具体咖啡:浓缩咖啡
type Espresso struct{}
func (e *Espresso) Description() string {
return “浓缩咖啡”
}
func (e *Espresso) Cost() float64 {
return 1.5
}
// 装饰器基类:咖啡配料
type CoffeeDecorator struct {
coffee Coffee
}
func NewCoffeeDecorator(coffee Coffee) *CoffeeDecorator {
return &CoffeeDecorator{coffee: coffee}
}
func (cd *CoffeeDecorator) Description() string {
return cd.coffee.Description()
}
func (cd *CoffeeDecorator) Cost() float64 {
return cd.coffee.Cost()
}
// 具体装饰器:牛奶
type Milk struct {
*CoffeeDecorator
}
func NewMilk(coffee Coffee) *Milk {
return &Milk{CoffeeDecorator: NewCoffeeDecorator(coffee)}
}
func (m *Milk) Description() string {
return m.coffee.Description() + “,牛奶”
}
func (m *Milk) Cost() float64 {
return m.coffee.Cost() + 0.5
}
// 具体装饰器:糖
type Sugar struct {
*CoffeeDecorator
}
func NewSugar(coffee Coffee) *Sugar {
return &Sugar{CoffeeDecorator: NewCoffeeDecorator(coffee)}
}
func (s *Sugar) Description() string {
return s.coffee.Description() + “,糖”
}
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点!真正的体系化!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
return &Sugar{CoffeeDecorator: NewCoffeeDecorator(coffee)}
}
func (s *Sugar) Description() string {
return s.coffee.Description() + “,糖”
}
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-0U4wFiNd-1715488836164)]
[外链图片转存中…(img-FUhAhZVF-1715488836164)]
[外链图片转存中…(img-R5nzVifP-1715488836164)]
[外链图片转存中…(img-BJwqng2R-1715488836165)]
[外链图片转存中…(img-S7NgVzRo-1715488836165)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点!真正的体系化!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!