学习笔记:外观、模版方法、命令模式
本文最后更新于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())
}

这个例子展示了命令模式如何在文本编辑器中实现插入、删除、撤销和重做功能。每个操作都被封装为一个命令对象,使得操作可以被参数化、队列化,并支持撤销/重做。

总结

相同点

  1. 封装:

    • 所有这三种模式都在某种程度上使用了封装原则。
    • 外观模式封装了子系统的复杂性。
    • 模板方法模式封装了算法的整体结构。
    • 命令模式封装了具体的操作。
  2. 解耦:

    • 这三种模式都致力于降低系统组件之间的耦合度。
    • 外观模式解耦了客户端和子系统。
    • 模板方法模式解耦了算法的结构和具体实现。
    • 命令模式解耦了命令的发送者和接收者。
  3. 可扩展性:

    • 这些模式都提高了系统的可扩展性。
    • 外观模式可以轻松添加新的子系统。
    • 模板方法模式可以方便地添加新的算法变体。
    • 命令模式可以轻松添加新的命令。

不同点

  1. 目的:

    • 外观模式主要用于简化复杂系统的接口。
    • 模板方法模式用于定义算法骨架和重用代码。
    • 命令模式用于将请求参数化和支持可撤销的操作。
  2. 结构:

    • 外观模式通常只有一个外观类作为入口点。
    • 模板方法模式使用继承结构,有抽象父类和具体子类。
    • 命令模式使用组合,有命令、接收者和调用者。
  3. 灵活性:

    • 外观模式在运行时相对静态,主要在设计时提供灵活性。
    • 模板方法模式允许在运行时选择不同的算法实现。
    • 命令模式提供了最大的运行时灵活性,可以动态地组合和修改命令。
  4. 应用场景:

    • 外观模式适用于需要简化复杂系统接口的场景。
    • 模板方法模式适用于有固定步骤但细节可能变化的算法。
    • 命令模式适用于需要将动作参数化或支持撤销/重做的场景。
感谢阅读~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇