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