本文最后更新于240 天前,其中的信息可能已经过时,如有错误请发送邮件到lvlvko233@qq.com
外观模式 (Facade Pattern)
介绍
外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
特点
- 简化复杂系统的接口
- 对客户屏蔽子系统组件
- 降低客户端与子系统的耦合
应用场景
让我们以一个智能家居系统为例,展示外观模式如何简化复杂系统的使用。
package main
import (
"fmt"
"time"
)
// 子系统:灯光控制
type LightingSystem struct{}
func (ls *LightingSystem) TurnOn() {
fmt.Println("Lighting system: Turning on lights")
}
func (ls *LightingSystem) TurnOff() {
fmt.Println("Lighting system: Turning off lights")
}
func (ls *LightingSystem) Dim(level int) {
fmt.Printf("Lighting system: Dimming lights to %d%%\n", level)
}
// 子系统:温度控制
type TemperatureSystem struct{}
func (ts *TemperatureSystem) SetTemperature(temp float64) {
fmt.Printf("Temperature system: Setting temperature to %.1f°C\n", temp)
}
// 子系统:安全系统
type SecuritySystem struct{}
func (ss *SecuritySystem) Arm() {
fmt.Println("Security system: Arming")
}
func (ss *SecuritySystem) Disarm() {
fmt.Println("Security system: Disarming")
}
// 子系统:音乐系统
type MusicSystem struct{}
func (ms *MusicSystem) PlayMusic(genre string) {
fmt.Printf("Music system: Playing %s music\n", genre)
}
func (ms *MusicSystem) StopMusic() {
fmt.Println("Music system: Stopping music")
}
// 外观:智能家居控制器
type SmartHomeFacade struct {
lighting *LightingSystem
temperature *TemperatureSystem
security *SecuritySystem
music *MusicSystem
}
func NewSmartHomeFacade() *SmartHomeFacade {
return &SmartHomeFacade{
lighting: &LightingSystem{},
temperature: &TemperatureSystem{},
security: &SecuritySystem{},
music: &MusicSystem{},
}
}
func (shf *SmartHomeFacade) LeaveHome() {
fmt.Println("Smart Home: Leaving home sequence initiated")
shf.lighting.TurnOff()
shf.temperature.SetTemperature(18.0) // 设置节能温度
shf.security.Arm()
shf.music.StopMusic()
}
func (shf *SmartHomeFacade) ReturnHome() {
fmt.Println("Smart Home: Returning home sequence initiated")
shf.security.Disarm()
shf.lighting.TurnOn()
shf.temperature.SetTemperature(22.0) // 设置舒适温度
shf.music.PlayMusic("relaxing")
}
func (shf *SmartHomeFacade) MovieMode() {
fmt.Println("Smart Home: Movie mode activated")
shf.lighting.Dim(30)
shf.temperature.SetTemperature(21.0)
shf.music.StopMusic()
}
func main() {
smartHome := NewSmartHomeFacade()
fmt.Println("=== Leaving Home ===")
smartHome.LeaveHome()
fmt.Println("\n=== Returning Home ===")
smartHome.ReturnHome()
fmt.Println("\n=== Activating Movie Mode ===")
smartHome.MovieMode()
}
这个例子展示了外观模式如何简化复杂的智能家居系统操作。通过SmartHomeFacade
,用户可以轻松执行复杂的场景,而不需要直接与各个子系统交互。
模板方法模式 (Template Method Pattern)
介绍
模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
特点
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现
应用场景
让我们以一个数据处理管道为例,展示模板方法模式如何定义一个算法的骨架,同时允许子类定制某些步骤。
package main
import (
"fmt"
"strings"
)
// DataProcessor 定义了数据处理的模板方法
type DataProcessor interface {
Process(data []string) []string
ReadData() []string
FilterData(data []string) []string
TransformData(data []string) []string
OutputData(data []string)
}
// BaseDataProcessor 提供了基本实现
type BaseDataProcessor struct{}
func (bdp *BaseDataProcessor) Process(data []string) []string {
data = bdp.ReadData()
data = bdp.FilterData(data)
data = bdp.TransformData(data)
bdp.OutputData(data)
return data
}
func (bdp *BaseDataProcessor) ReadData() []string {
return []string{"apple", "banana", "cherry", "date", "elderberry"}
}
func (bdp *BaseDataProcessor) FilterData(data []string) []string {
filtered := make([]string, 0)
for _, item := range data {
if len(item) > 4 {
filtered = append(filtered, item)
}
}
return filtered
}
func (bdp *BaseDataProcessor) TransformData(data []string) []string {
transformed := make([]string, len(data))
for i, item := range data {
transformed[i] = strings.ToUpper(item)
}
return transformed
}
func (bdp *BaseDataProcessor) OutputData(data []string) {
fmt.Println("Processed data:", data)
}
// CSVDataProcessor 自定义了某些步骤
type CSVDataProcessor struct {
BaseDataProcessor
}
func (csvdp *CSVDataProcessor) ReadData() []string {
fmt.Println("Reading data from CSV file")
return []string{"apple,red", "banana,yellow", "cherry,red", "date,brown", "elderberry,purple"}
}
func (csvdp *CSVDataProcessor) FilterData(data []string) []string {
filtered := make([]string, 0)
for _, item := range data {
if strings.Contains(item, "red") {
filtered = append(filtered, item)
}
}
return filtered
}
func (csvdp *CSVDataProcessor) TransformData(data []string) []string {
transformed := make([]string, len(data))
for i, item := range data {
parts := strings.Split(item, ",")
transformed[i] = fmt.Sprintf("%s is %s", parts[0], parts[1])
}
return transformed
}
// JSONDataProcessor 自定义了某些步骤
type JSONDataProcessor struct {
BaseDataProcessor
}
func (jdp *JSONDataProcessor) ReadData() []string {
fmt.Println("Reading data from JSON file")
return []string{`{"name": "apple", "color": "red"}`, `{"name": "banana", "color": "yellow"}`, `{"name": "cherry", "color": "red"}`}
}
func (jdp *JSONDataProcessor) TransformData(data []string) []string {
transformed := make([]string, len(data))
for i, item := range data {
item = strings.ReplaceAll(item, `"`, "")
item = strings.ReplaceAll(item, "{", "")
item = strings.ReplaceAll(item, "}", "")
transformed[i] = strings.ReplaceAll(item, ",", " -")
}
return transformed
}
func main() {
baseProcessor := &BaseDataProcessor{}
csvProcessor := &CSVDataProcessor{}
jsonProcessor := &JSONDataProcessor{}
fmt.Println("=== Base Data Processing ===")
baseProcessor.Process(nil)
fmt.Println("\n=== CSV Data Processing ===")
csvProcessor.Process(nil)
fmt.Println("\n=== JSON Data Processing ===")
jsonProcessor.Process(nil)
}
这个例子展示了模板方法模式如何定义一个数据处理的通用流程,同时允许不同的数据处理器(CSV、JSON)自定义某些步骤。
命令模式 (Command Pattern)
介绍
命令模式将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。命令模式可以将请求发送者和接收者完全解耦。
特点
- 降低系统的耦合度
- 新的命令可以很容易地加入到系统中
- 可以比较容易地设计一个命令队列和宏命令
应用场景
让我们以一个文本编辑器为例,展示命令模式如何支持撤销和重做操作。
package main
import (
"fmt"
"strings"
)
// Command 接口定义了执行和撤销操作
type Command interface {
Execute() string
Undo() string
}
// TextEditor 是命令的接收者
type TextEditor struct {
content string
}
func (te *TextEditor) InsertText(text string) {
te.content += text
}
func (te *TextEditor) DeleteText(length int) {
if length <= len(te.content) {
te.content = te.content[:len(te.content)-length]
}
}
// InsertCommand 实现了插入文本的命令
type InsertCommand struct {
editor *TextEditor
text string
}
func (ic *InsertCommand) Execute() string {
ic.editor.InsertText(ic.text)
return fmt.Sprintf("Inserted: %s", ic.text)
}
func (ic *InsertCommand) Undo() string {
ic.editor.DeleteText(len(ic.text))
return fmt.Sprintf("Undid insertion: %s", ic.text)
}
// DeleteCommand 实现了删除文本的命令
type DeleteCommand struct {
editor *TextEditor
text string
}
func (dc *DeleteCommand) Execute() string {
if len(dc.editor.content) >= len(dc.text) {
dc.text = dc.editor.content[len(dc.editor.content)-len(dc.text):]
dc.editor.DeleteText(len(dc.text))
return fmt.Sprintf("Deleted: %s", dc.text)
}
return "Nothing to delete"
}
func (dc *DeleteCommand) Undo() string {
dc.editor.InsertText(dc.text)
return fmt.Sprintf("Undid deletion: %s", dc.text)
}
// CommandInvoker 负责执行命令和管理撤销/重做栈
type CommandInvoker struct {
editor *TextEditor
undoStack []Command
redoStack []Command
}
func (ci *CommandInvoker) ExecuteCommand(cmd Command) string {
result := cmd.Execute()
ci.undoStack = append(ci.undoStack, cmd)
ci.redoStack = []Command{} // Clear redo stack
return result
}
func (ci *CommandInvoker) Undo() string {
if len(ci.undoStack) > 0 {
cmd := ci.undoStack[len(ci.undoStack)-1]
ci.undoStack = ci.undoStack[:len(ci.undoStack)-1]
result := cmd.Undo()
ci.redoStack = append(ci.redoStack, cmd)
return result
}
return "Nothing to undo"
}
func (ci *CommandInvoker) Redo() string {
if len(ci.redoStack) > 0 {
cmd := ci.redoStack[len(ci.redoStack)-1]
ci.redoStack = ci.redoStack[:len(ci.redoStack)-1]
result := cmd.Execute()
ci.undoStack = append(ci.undoStack, cmd)
return result
}
return "Nothing to redo"
}
func (ci *CommandInvoker) GetContent() string {
return ci.editor.content
}
func main() {
editor := &TextEditor{}
invoker := &CommandInvoker{editor: editor}
fmt.Println("=== Text Editor Operations ===")
fmt.Println(invoker.ExecuteCommand(&InsertCommand{editor: editor, text: "Hello, "}))
fmt.Println(invoker.ExecuteCommand(&InsertCommand{editor: editor, text: "World!"}))
fmt.Println("Current content:", invoker.GetContent())
fmt.Println(invoker.ExecuteCommand(&DeleteCommand{editor: editor, text: "World!"}))
fmt.Println("Current content:", invoker.GetContent())
fmt.Println(invoker.Undo())
fmt.Println("Current content after undo:", invoker.GetContent())
fmt.Println(invoker.Redo())
fmt.Println("Current content after redo:", invoker.GetContent())
fmt.Println(invoker.ExecuteCommand(&InsertCommand{editor: editor, text: "Golang!"}))
fmt.Println("Final content:", invoker.GetContent())
}
这个例子展示了命令模式如何在文本编辑器中实现插入、删除、撤销和重做功能。每个操作都被封装为一个命令对象,使得操作可以被参数化、队列化,并支持撤销/重做。
总结
相同点
-
封装:
- 所有这三种模式都在某种程度上使用了封装原则。
- 外观模式封装了子系统的复杂性。
- 模板方法模式封装了算法的整体结构。
- 命令模式封装了具体的操作。
-
解耦:
- 这三种模式都致力于降低系统组件之间的耦合度。
- 外观模式解耦了客户端和子系统。
- 模板方法模式解耦了算法的结构和具体实现。
- 命令模式解耦了命令的发送者和接收者。
-
可扩展性:
- 这些模式都提高了系统的可扩展性。
- 外观模式可以轻松添加新的子系统。
- 模板方法模式可以方便地添加新的算法变体。
- 命令模式可以轻松添加新的命令。
不同点
-
目的:
- 外观模式主要用于简化复杂系统的接口。
- 模板方法模式用于定义算法骨架和重用代码。
- 命令模式用于将请求参数化和支持可撤销的操作。
-
结构:
- 外观模式通常只有一个外观类作为入口点。
- 模板方法模式使用继承结构,有抽象父类和具体子类。
- 命令模式使用组合,有命令、接收者和调用者。
-
灵活性:
- 外观模式在运行时相对静态,主要在设计时提供灵活性。
- 模板方法模式允许在运行时选择不同的算法实现。
- 命令模式提供了最大的运行时灵活性,可以动态地组合和修改命令。
-
应用场景:
- 外观模式适用于需要简化复杂系统接口的场景。
- 模板方法模式适用于有固定步骤但细节可能变化的算法。
- 命令模式适用于需要将动作参数化或支持撤销/重做的场景。