设计模式——七大结构型模式(Java&Golang实现)

探索结构型设计模式的神奇力量✨:这是软件工程师的秘密武器🛠️,让复杂的系统变得简洁高效。从桥接到代理,这些模式不仅仅是代码行,它们是构建强大应用架构的基石🏗️。深入了解这些模式,就是打开高质量编程世界的钥匙🗝️。加入我们,一起揭开结构型设计模式的神秘面纱,让你的编程之路更加清晰和光明!🌟 本期主要讲解结构型模式,如需学习创建型模式请看 设计模式------五大创建型模式(Java&Golang实现)

结构型模式

组织不同的类和对象,以形成更大的结构。主要思想是通过组合较小的、简单的部分来构建复杂和功能强大的系统。像用小积木搭建一个大型的模型一样。

适配器模式(Adapter)

  • 目的:解决接口不兼容的问题,可以将一个类的接口转换成客户端所期望的另一个接口,使得原本用于接口不兼容而无法一起工作的类可以协同工作。
  • 应用场景:需要在系统中集成一个新的组件或服务,但其接口与现有系统不兼容时,可以使用适配器模式。
  • 案例:一个音频播放器应用,它支持播放MP3格式的文件。现在希望扩展该应用以支持更多格式的音频文件,比如WAV和MP4。但是播放器原生只支持MP3格式。这里可以使用适配器模式来适配其他格式的音频文件。
  • 结构图

java 实现

java 复制代码
// 音频播放器只支持播放MP3
// MediaPlayer.java
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

// AudioPlayer.java
// MP3播放器实现
public class AudioPlayer implements MediaPlayer {
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing MP3 file: " + fileName);
        } else {
            System.out.println("Invalid file format");
        }
    }
}
// Client.java
// 客户端代码
public class Client {
    public static void main(String[] args) {
        MediaPlayer mediaPlayer = new AudioPlayer();
        mediaPlayer.play("mp3", "horizon.mp3");
    }
}

// 希望支持MAV和MP4格式,待适配的类
// WAVPlayer.java
public class WAVPlayer {
    public void playWAV(String fileName) {
        System.out.println("Playing WAV file: " + fileName);
    }
}

// MP4Player.java
public class MP4Player {
    public void playMP4(String fileName) {
        System.out.println("Playing MP4 file: " + fileName);
    }
}

// 创建适配类
// WAVAdapter.java
public class WAVAdapter implements MediaPlayer {
    private WAVPlayer wavPlayer;

    public WAVAdapter() {
        wavPlayer = new WAVPlayer();
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("wav")) {
            wavPlayer.playWAV(fileName);
        }
    }
}

// MP4Adapter.java
public class MP4Adapter implements MediaPlayer {
    private MP4Player mp4Player;

    public MP4Adapter() {
        mp4Player = new MP4Player();
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("mp4")) {
            mp4Player.playMP4(fileName);
        }
    }
}

// 修改AudioPlayer.java文件,使其适配mav和mp4
// AudioPlayer.java
public class AudioPlayer implements MediaPlayer {
    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing MP3 file: " + fileName);
        } else if(audioType.equalsIgnoreCase("wav")) {
            new WAVAdapter().play(audioType, fileName);
        } else if(audioType.equalsIgnoreCase("mp4")) {
            new MP4Adapter().play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

// Client.java
public class Client {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("wav", "mind me.wav");
        audioPlayer.play("avi", "my movie.avi"); // 不支持的格式
    }
}

golang实现

go 复制代码
// 定义MediaPlayer接口,声明了播放音频的方法
type MediaPlayer interface {
    Play(audioType, fileName string)
}

// 创建WAV和MP4格式的播放器,这些将作为待适配的类
type WAVPlayer struct{}

func (w *WAVPlayer) PlayWAV(fileName string) {
    fmt.Println("Playing WAV file:", fileName)
}

type MP4Player struct{}

func (m *MP4Player) PlayMP4(fileName string) {
    fmt.Println("Playing MP4 file:", fileName)
}

// 为每种格式创建一个适配器结构体,这些适配器实现了MediaPlayer接口,并内部使用对应格式的播放器来播放音频。
type WAVAdapter struct {
    wavPlayer *WAVPlayer
}

func NewWAVAdapter() *WAVAdapter {
    return &WAVAdapter{wavPlayer: &WAVPlayer{}}
}

func (wa *WAVAdapter) Play(audioType, fileName string) {
    if audioType == "wav" {
        wa.wavPlayer.PlayWAV(fileName)
    }
}

type MP4Adapter struct {
    mp4Player *MP4Player
}

func NewMP4Adapter() *MP4Adapter {
    return &MP4Adapter{mp4Player: &MP4Player{}}
}

func (ma *MP4Adapter) Play(audioType, fileName string) {
    if audioType == "mp4" {
        ma.mp4Player.PlayMP4(fileName)
    }
}
// 创建一个AudioPlayer结构体,它使用MediaPlayer接口来播放不同格式的音频文件
type AudioPlayer struct{}

func (a *AudioPlayer) Play(audioType, fileName string) {
    switch audioType {
    case "mp3":
        fmt.Println("Playing MP3 file:", fileName)
    case "wav":
        adapter := NewWAVAdapter()
        adapter.Play(audioType, fileName)
    case "mp4":
        adapter := NewMP4Adapter()
        adapter.Play(audioType, fileName)
    default:
        fmt.Println("Invalid media. ", audioType, " format not supported")
    }
}
// 使用
func main() {
    audioPlayer := &AudioPlayer{}

    audioPlayer.Play("mp3", "beyond_the_horizon.mp3")
    audioPlayer.Play("wav", "mind_me.wav")
    audioPlayer.Play("mp4", "alone.mp4")
    audioPlayer.Play("avi", "movie.avi") // 不支持的格式
}

装饰者模式(Decorator)

  • 目的:不改变原始类的基础上,通过添加包装(或装饰)来扩展对象的功能。这种模式创建了一个装饰类,用来包裹原有的类,并在保持原类方法签名完整性的前提下,提供了额外的功能。
  • 应用场景:Java中的java.io包中广泛使用了装饰者模式。例如,BufferedReader和BufferedWriter就是装饰了Reader和Writer接口的具体装饰者,提供了缓冲功能,以提高读写的性能。
  • 案例:开发一个文本编辑器,基本功能是显示和编辑文本。现希望在不改变现有代码的基础上,动态地添加一些额外的功能,例如拼写检查和语法高亮。
  • 结构图:

Java实现

java 复制代码
// TextEditor.java
// 抽象组件
public interface TextEditor {
    void display();
}

// TextEditor.java
// 具体组件
public class BasicTextEditor implements TextEditor {
    @Override
    public void display() {
        System.out.println("Displaying basic text.");
    }
}

// TextEditorDecorator.java
// 装饰者抽象类
public abstract class TextEditorDecorator implements TextEditor {
    protected TextEditor decoratedEditor;

    public TextEditorDecorator(TextEditor decoratedEditor) {
        this.decoratedEditor = decoratedEditor;
    }

    @Override
    public void display() {
        decoratedEditor.display();
    }
}

// SpellCheckDecorator.java
// 具体装饰者:拼写检查
public class SpellCheckDecorator extends TextEditorDecorator {
    public SpellCheckDecorator(TextEditor decoratedEditor) {
        super(decoratedEditor);
    }

    @Override
    public void display() {
        super.display();
        addSpellCheck();
    }

    private void addSpellCheck() {
        System.out.println("Spell checking enabled.");
    }
}

// SyntaxHighlightDecorator.java
// 具体装饰者:语法高亮
public class SyntaxHighlightDecorator extends TextEditorDecorator {
    public SyntaxHighlightDecorator(TextEditor decoratedEditor) {
        super(decoratedEditor);
    }

    @Override
    public void display() {
        super.display();
        addSyntaxHighlighting();
    }

    private void addSyntaxHighlighting() {
        System.out.println("Syntax highlighting enabled.");
    }
}

// Client.java
// 使用装饰者
public class Client {
    public static void main(String[] args) {
        TextEditor basicEditor = new BasicTextEditor();
        TextEditor spellCheckedEditor = new SpellCheckDecorator(basicEditor);
        TextEditor highlightedEditor = new SyntaxHighlightDecorator(spellCheckedEditor);

        highlightedEditor.display();
    }
}

golang实现

go 复制代码
package main

import "fmt"

// 抽象组件
type TextEditor interface {
	Display()
}

// 具体组件
type BasicTextEditor struct{}

func (b *BasicTextEditor) Display() {
	fmt.Println("Displaying basic text.")
}

// 装饰者抽象类
type TextEditorDecorator struct {
	decoratedEditor TextEditor
}

func (t *TextEditorDecorator) Display() {
	t.decoratedEditor.Display()
}

// 具体装饰者:拼写检查
type SpellCheckDecorator struct {
	TextEditorDecorator
}

func (s *SpellCheckDecorator) Display() {
	s.TextEditorDecorator.Display()
	s.AddSpellCheck()
}

func (s *SpellCheckDecorator) AddSpellCheck() {
	fmt.Println("Spell checking enabled.")
}

// 具体装饰者:语法高亮
type SyntaxHighlightDecorator struct {
	TextEditorDecorator
}

func (s *SyntaxHighlightDecorator) Display() {
	s.TextEditorDecorator.Display()
	s.AddSyntaxHighlighting()
}

func (s *SyntaxHighlightDecorator) AddSyntaxHighlighting() {
	fmt.Println("Syntax highlighting enabled.")
}

// 使用装饰者
func main() {
	basicEditor := &BasicTextEditor{}

	spellCheckedEditor := &SpellCheckDecorator{TextEditorDecorator{decoratedEditor: basicEditor}}
	highlightedEditor := &SyntaxHighlightDecorator{TextEditorDecorator{decoratedEditor: spellCheckedEditor}}

	highlightedEditor.Display()
}

代理模式(Proxy)

  • 目的:不改变原始类接口的情况下,控制对对象的访问。
  • 应用场景: 安全代理用于控制对对象的访问权限。代理对象可以在执行操作前进行身份验证,确保只有经过授权的用户可以访问真实对象。
  • 案例:有一个文件操作类,我们希望只有具有特定权限的用户才能执行文件的读写操作。

Java实现

java 复制代码
// FileOperation.java
// 接口
public interface FileOperation {
    void read();
    void write(String data);
}

// RealFileOperation.java
// 真实类
public class RealFileOperation implements FileOperation{
    private String fileName;

    public RealFileOperation(String fileName) {
        this.fileName = fileName;
    }
    @Override
    public void read() {
        System.out.println("Reading content from file: " + fileName);
    }

    @Override
    public void write(String data) {
        System.out.println("Writing data to file " + fileName + ": " + data);
    }
}

// SecureFileProxy.java
// 安全代理
public class SecureFileProxy implements FileOperation{
    private RealFileOperation realFile;
    private String username;

    public SecureFileProxy(String fileName, String username) {
        this.realFile = new RealFileOperation(fileName);
        this.username = username;
    }

    public SecureFileProxy() {
    }

    @Override
    public void read() {
        if (auth()){
            realFile.read();;
        } else {
            System.out.println("Access denied. Unauthorized user: " + username);
        }
    }

    @Override
    public void write(String data) {
        if (auth()){
            realFile.write(data);
        } else {
            System.out.println("Access denied. Unauthorized user: " + username);
        }
    }

    private boolean auth(){
        // 鉴定用户是否有权限操作
        return "admin".equals(username);
    }
}

// SecurityProxyMain.java
// 使用
public class SecurityProxyMain {

    public static void main(String[] args) {
        FileOperation secureFileProxy = new SecureFileProxy("test.log", "admin");

        // admin用户可以访问
        secureFileProxy.read();
        secureFileProxy.write("New content");

        FileOperation secureFileProxyForUser = new SecureFileProxy("example.txt", "user");

        //普通用户不能访问
        secureFileProxyForUser.read();
        secureFileProxyForUser.write("Unauthorized attempt");
    }
}

golang实现

go 复制代码
package main

import "fmt"

// FileOperation 接口
type FileOperation interface {
	Read()
	Write(data string)
}

// RealFile 实现了 FileOperation 接口的真实文件操作类
type RealFile struct {
	FileName string
}

func (rf *RealFile) Read() {
	fmt.Println("Reading content from file:", rf.FileName)
}

func (rf *RealFile) Write(data string) {
	fmt.Printf("Writing data to file %s: %s\n", rf.FileName, data)
}

// SecureFileProxy 是安全代理,通过组合实现了 FileOperation 接口
type SecureFileProxy struct {
	RealFile    *RealFile
	Username    string
	Auth  bool
}

func NewSecureFileProxy(fileName, username string) *SecureFileProxy {
	return &SecureFileProxy{
		RealFile:   &RealFile{FileName: fileName},
		Username:   username,
		Auth: (username == "admin"), // 仅允许Admin用户访问
	}
}

func (sfp *SecureFileProxy) Read() {
	if sfp.Auth {
		sfp.RealFile.Read()
	} else {
		fmt.Println("Access denied. Unauthorized user:", sfp.Username)
	}
}

func (sfp *SecureFileProxy) Write(data string) {
	if sfp.Auth {
		sfp.RealFile.Write(data)
	} else {
		fmt.Println("Access denied. Unauthorized user:", sfp.Username)
	}
}

func main() {
	// 创建真实文件和安全代理
	secureFileProxy := NewSecureFileProxy("example.txt", "Admin")

	// Admin用户可以访问
	secureFileProxy.Read()
	secureFileProxy.Write("New content")

	// 普通用户被拒绝访问
	secureFileProxyForUser := NewSecureFileProxy("example.txt", "User")
	secureFileProxyForUser.Read()
	secureFileProxyForUser.Write("Unauthorized attempt")
}

桥接模式(Bridge)

  • 目的:将抽象部分与其实现部分分离,使它们都可以独立地变化。通过提供一个桥接结构,能够把类的功能层次结构和实现层次结构分离开来,从而在两者间建立一个桥接通道。
  • 应用场景:当一个类存在两个独立变化的维度时,可以使用桥接模式分别在两个维度上进行扩展;当需要避免一个类的子类数量的爆炸式增长时。
  • 案例:有一个图形绘制的应用程序,其中可以绘制不同类型的图形(如圆形、矩形)并且支持不同的绘图API(如API1、API2)。使用桥接模式,我们可以独立地改变图形的类型和使用的绘图API。
  • 结构图:

Java实现

java 复制代码
// DrawAPI.java
// 实现化角色
interface DrawAPI {
    void drawShape(int radius, int x, int y);
}

// DrawAPI1.java
// 具体实现化角色
class DrawAPI1 implements DrawAPI {
    public void drawShape(int radius, int x, int y) {
        System.out.println("API1.drawShape at " + x + ", " + y + " with radius " + radius);
    }
}

// DrawAPI2.java
class DrawAPI2 implements DrawAPI {
    public void drawShape(int radius, int x, int y) {
        System.out.println("API2.drawShape at " + x + ", " + y + " with radius " + radius);
    }
}

// Shape.java
// 抽象化角色
abstract class Shape {
    protected DrawAPI drawAPI;

    protected Shape(DrawAPI drawAPI) {
        this.drawAPI = drawAPI;
    }

    public abstract void draw();
}

// Circle.java
// 修正抽象化角色
class Circle extends Shape {
    private int x, y, radius;

    public Circle(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public void draw() {
        drawAPI.drawShape(radius, x, y);
    }
}

// Main.java
// 使用
public class Main {
    public static void main(String[] args) {
        DrawAPI1 drawAPI1 = new DrawAPI1();
        DrawAPI2 drawAPI2 = new DrawAPI2();
        Shape circle1 = new Circle(5,2, 10, drawAPI1);
        Shape circle2 = new Circle(2,5, 15, drawAPI2);

        circle1.draw();
        circle2.draw();
    }
}

golang实现

go 复制代码
package main

import "fmt"

// Implementor 角色 - 绘制API接口
type DrawAPI interface {
	drawShape(radius, x, y int)
}

// ConcreteImplementor 角色 - 具体绘制API1
type DrawAPI1 struct{}

func (d *DrawAPI1) drawShape(radius, x, y int) {
	fmt.Printf("API1.drawShape at %d, %d with radius %d\n", x, y, radius)
}

// ConcreteImplementor 角色 - 具体绘制API2
type DrawAPI2 struct{}

func (d *DrawAPI2) drawShape(radius, x, y int) {
	fmt.Printf("API2.drawShape at %d, %d with radius %d\n", x, y, radius)
}

// Abstraction 角色 - 抽象化接口
type Shape interface {
	draw()
}

// RefinedAbstraction 角色 - 修正抽象化,具体的图形类型
type Circle struct {
	x, y, radius int
	drawAPI      DrawAPI
}

func (c *Circle) draw() {
	c.drawAPI.drawShape(c.radius, c.x, c.y)
}

// Client 角色
func main() {
	// 创建具体绘制API
	api1 := &DrawAPI1{}
	api2 := &DrawAPI2{}

	// 创建具体图形类型
	circle1 := &Circle{x: 100, y: 100, radius: 10, drawAPI: api1}
	circle2 := &Circle{x: 100, y: 100, radius: 10, drawAPI: api2}

	// 使用桥接模式进行绘制
	circle1.draw()
	circle2.draw()
}

组合模式(Composite)

  • 目的:组合模式的主要目的是将对象组织成树形结构,使得客户端可以统一地对待单个对象和组合对象。
  • 应用场景:表示部分-整体层次结构,例如树形结构、菜单系统等;用户希望统一对待树中的所有对象,而不关心当前处理的是单个对象还是组合对象;当要求体现整体与部分的层次结构时,可以使用组合模式。
  • 案例:一个文件系统,文件系统由文件(File)和文件夹(Folder)组成。文件夹可以包含文件和其他文件夹,实现了递归结构。

java实现

java 复制代码
// FileSystemComponent.java
// 抽象组件
interface FileSystemComponent {
    void display();
}

// File.java
// 文件
class File implements FileSystemComponent {
    private String name;

    public File(String name) {
        this.name = name;
    }

    public void display() {
        System.out.println("File: " + name);
    }
}

// Floder.java
// 文件夹
class Folder implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();

    public Folder(String name) {
        this.name = name;
    }

    public void addComponent(FileSystemComponent component) {
        components.add(component);
    }

    public void removeComponent(FileSystemComponent component) {
        components.remove(component);
    }

    public void display() {
        System.out.println("Folder: " + name);
        for (FileSystemComponent component : components) {
            component.display();
        }
    }
}

// Main.java
// 使用
public class Main {
    public static void main(String[] args) {
        File file1 = new File("File1.txt");
        File file2 = new File("File2.txt");

        Folder folder1 = new Folder("Folder1");
        folder1.addComponent(file1);
        folder1.addComponent(file2);

        File file3 = new File("File3.txt");
        
        Folder mainFolder = new Folder("MainFolder");
        mainFolder.addComponent(folder1);
        mainFolder.addComponent(file3);

        mainFolder.display();
    }
}

golang实现

go 复制代码
package main

import "fmt"

type FileSystemComponent interface {
    display()
}

type File struct {
    name string
}

func (f *File) display() {
    fmt.Println("File: " + f.name)
}

type Folder struct {
    name       string
    components []FileSystemComponent
}

func (f *Folder) addComponent(component FileSystemComponent) {
    f.components = append(f.components, component)
}

func (f *Folder) removeComponent(component FileSystemComponent) {
    for i, c := range f.components {
        if c == component {
            f.components = append(f.components[:i], f.components[i+1:]...)
        }
    }
}

func (f *Folder) display() {
    fmt.Println("Folder: ", f.name)
    for _, component := range f.components {
        component.display()
    }
}

func main() {
    file1 := &File{name: "File1.txt"}
    file2 := &File{name: "File2.txt"}
    file11 := &File{name: "File11.txt"}

    folder1 := &Folder{name: "Folder1"}
    folder1.addComponent(file1)
    folder1.addComponent(file2)
    folder1.addComponent(file11)

    file3 := &File{name: "File3.txt"}

    mainFolder := &Folder{name: "MainFolder"}
    mainFolder.addComponent(folder1)
    mainFolder.addComponent(file3)

    mainFolder.display()
}

外观模式(Facade)

  • 目的:观模式的主要目的是提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,使得子系统更容易使用。
  • 应用场景:当需要为复杂的模块或子系统提供一个简单的接口时;客户程序与多个子系统之间存在很大的依赖性时,可以使用外观来解耦;分层架构中,可以用外观作为每层的入口点,简化层间的通信。
  • 案例:考虑一个家庭影院系统,包括灯光、音响、投影仪等组件。每个组件都有自己的操作接口,但为了方便用户,提供一个外观,它提供简单的方法来控制所有这些复杂的设备。
java 复制代码
// 子系统
// Lights.java
// 灯光系统
public class Lights {
    private Integer level;

    public Lights() {
    }

    public Lights(Integer level) {
        this.level = level;
    }

    public void dim() {
        System.out.println("把灯光调暗到:" + level + "%");
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }
}

// SoundSystem.java
// 声音系统
public class SoundSystem {

    private Integer level;

    public SoundSystem() {
    }

    public SoundSystem(Integer level) {
        this.level = level;
    }

    public void setVolume() {
        System.out.println("将音量设置为:" + level);
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }
}

// Projector.java
// 投影仪系统
public class Projector {

    private String movie;

    public Projector() {
    }

    public Projector(String movie) {
        this.movie = movie;
    }

    void turnOn() {
        System.out.println("打开投影仪,播放:" + movie);
    }

    public String getMovie() {
        return movie;
    }

    public void setMovie(String movie) {
        this.movie = movie;
    }
}

// HomeTheaterFacade.java
// 外观类
public class HomeTheaterFacade {
    private Lights lights;
    private SoundSystem soundSystem;
    private Projector projector;

    public HomeTheaterFacade(Lights lights, SoundSystem soundSystem, Projector projector) {
        this.lights = lights;
        this.soundSystem = soundSystem;
        this.projector = projector;
    }

    void startMovieNight(Integer lightLevel, Integer soundLevel, String movieName) {
        lights.setLevel(lightLevel);
        lights.dim();
        soundSystem.setLevel(soundLevel);
        soundSystem.setVolume();
        projector.setMovie(movieName);
        projector.turnOn();
    }
}

// Main.java
// 客户端
public class Main {
    public static void main(String[] args) {
        Lights lights = new Lights();
        SoundSystem soundSystem = new SoundSystem();
        Projector projector = new Projector();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(lights, soundSystem, projector);
        homeTheater.startMovieNight(80, 7, "<<我不是程序员>>");
    }
}

golang实现

go 复制代码
package main

import "fmt"

type Lights struct {
    level int
}

func (l *Lights) dim() {
    fmt.Printf("把灯光调暗到:%d%%\n", l.level)
}

type SoundSystem struct {
    level int
}

func (s *SoundSystem) setVolume() {
    fmt.Printf("将音量设置为: %d \n", s.level)
}

type Projector struct {
    movie string
}

func (p *Projector) turnOn() {
    fmt.Printf("打开投影仪,播放: %s \n", p.movie)
}

type HomeTheaterFacaden struct {
    lights      Lights
    soundSystem SoundSystem
    projector   Projector
}

func (h *HomeTheaterFacaden) startMovieNight(lightLevel, soundLevel int, movie string) {
    h.lights.level = lightLevel
    h.lights.dim()
    h.soundSystem.level = soundLevel
    h.soundSystem.setVolume()
    h.projector.movie = movie
    h.projector.turnOn()
}

func main() {
    lights := Lights{}
    soundSystem := SoundSystem{}
    projector := Projector{}
    homeTheater := &HomeTheaterFacaden{
        lights:      lights,
        soundSystem: soundSystem,
        projector:   projector,
    }
    homeTheater.startMovieNight(80, 7, "<<我不是程序员>>")
}

享元模式(Flyweight)

  • 目的:通过共享对象以减少内存或计算开销;适用于大量相似对象存在的场景,以便节省系统资源。
  • 应用场景:当一个应用程序使用了大量相似对象,这些对象可以被共享时;当对象的大多数状态都可以变为外部状态,而剩余部分可以提取出来作为共享状态时。
  • 案例:一个文本编辑器中字符对象的例子,在一个文本中,有很多相同的字符,例如字母、数字等。为了减少内存占用,可以使用享元模式来共享相同的字符对象,而不是为每个字符都创建一个新的对象。

Java实现

java 复制代码
// Char.java
// 享元接口
interface Char {
    void print();
}

// ConcreteCharacter.java
// 具体享元类
class ConcreteCharacter implements Char {
    private char symbol;

    public ConcreteCharacter(char symbol) {
        this.symbol = symbol;
    }

    @Override
    public void print() {
        System.out.print(symbol);
    }
}

// CharacterFactory.java
// 享元工厂
class CharacterFactory {
    private Map<Character, ConcreteCharacter> characters = new HashMap<>();

    public Char getCharacter(char symbol) {
        if (!characters.containsKey(symbol)) {
            characters.put(symbol, new ConcreteCharacter(symbol));
        }
        return characters.get(symbol);
    }
}

// 客户端
public class Main {
    public static void main(String[] args) {
        CharacterFactory characterFactory = new CharacterFactory();

        String text = "Hello, World!";

        for (char c : text.toCharArray()) {
            Char character = characterFactory.getCharacter(c);
            character.print();
        }
    }
}

golang实现

go 复制代码
package main

import (
	"fmt"
	"sync"
)

// Char 享元接口
type Char interface {
	Print()
}

// ConcreteCharacter 具体享元类
type ConcreteCharacter struct {
	symbol rune
}

func (c *ConcreteCharacter) Print() {
	fmt.Printf("%c", c.symbol)
}

// CharacterFactory 享元工厂
type CharacterFactory struct {
	characters map[rune]*ConcreteCharacter
	mu         sync.Mutex
}

func NewCharacterFactory() *CharacterFactory {
	return &CharacterFactory{
		characters: make(map[rune]*ConcreteCharacter),
	}
}

func (cf *CharacterFactory) GetCharacter(symbol rune) Char {
	cf.mu.Lock()
	defer cf.mu.Unlock()

	character, ok := cf.characters[symbol]
	if !ok {
		character = &ConcreteCharacter{symbol: symbol}
		cf.characters[symbol] = character
	}
	return character
}

// 客户端
func main() {
	charFactory := NewCharacterFactory()

	text := "Hello, World!"

	for _, char := range text {
		character := charFactory.GetCharacter(char)
		character.Print()
	}
	fmt.Println()
}
相关推荐
lxyzcm10 小时前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
越甲八千10 小时前
重温设计模式--单例模式
单例模式·设计模式
Vincent(朱志强)10 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
诸葛悠闲12 小时前
设计模式——桥接模式
设计模式·桥接模式
捕鲸叉16 小时前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
小小小妮子~17 小时前
框架专题:设计模式
设计模式·框架
先睡17 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
Damon_X1 天前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千1 天前
重温设计模式--享元模式
设计模式·享元模式
码农爱java1 天前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式