Imagine now that we are building a music player, and recently, market demands have grown. Now, users expect support not just for MP3 and WAV but also for FLAC files within our music player system. This development poses a unique challenge: How do we extend our music player's capabilities to embrace this new format without altering its established interface or the adapter we've already implemented for WAV support?
Let's say that we currently have a MusicPlayer struct that can only play MP3 files:
package main
import (
"fmt"
"strings"
)
type MusicPlayer struct{}
func (mp MusicPlayer) Play(file string) {
if strings.HasSuffix(file, ".mp3") {
fmt.Println("Playing", file, "as mp3.")
} else {
fmt.Println("File format not supported.")
}
}
Let's approach this challenge by introducing a composite adapter, a design that encapsulates multiple strategies to extend functionality in a modular and maintainable manner.
package main
import (
"fmt"
"strings"
)
type MusicPlayerAdapter struct {
player *MusicPlayer
formatAdapters map[string]func(string)
}
func NewMusicPlayerAdapter(player *MusicPlayer) *MusicPlayerAdapter {
a := &MusicPlayerAdapter{
player: player,
formatAdapters: map[string]func(string){
".wav": ConvertAndPlayWav,
".flac": ConvertAndPlayFlac,
},
}
return a
}
func (a *MusicPlayerAdapter) Play(file string) {
// Extract the file extension by finding the last dot.
extensionIndex := strings.LastIndex(file, ".")
if extensionIndex != -1 {
extension := strings.ToLower(file[extensionIndex:])
if adapterFunc, exists := a.formatAdapters[extension]; exists {
adapterFunc(file)
} else {
a.player.Play(file)
}
} else {
a.player.Play(file) // No extension found
}
}
func ConvertAndPlayWav(file string) {
// Simulate conversion
convertedFile := strings.Replace(file, ".wav", ".mp3", 1)
fmt.Println("Converting", file, "to", convertedFile, "and playing as mp3...")
}
func ConvertAndPlayFlac(file string) {
// Simulate conversion
convertedFile := strings.Replace(file, ".flac", ".mp3", 1)
fmt.Println("Converting", file, "to", convertedFile, "and playing as mp3...")
}
// Upgraded music player with enhanced functionality through the composite adapter
func main() {
legacyPlayer := &MusicPlayer{}
enhancedPlayer := NewMusicPlayerAdapter(legacyPlayer)
enhancedPlayer.Play("song.mp3") // Supported directly
enhancedPlayer.Play("song.wav") // Supported through adaptation
enhancedPlayer.Play("song.flac") // Newly supported through additional adaptation
}
This sophisticated adaptation strategy ensures that we can extend the MusicPlayer to include support for additional file formats without disturbing its original code or the initial adapter pattern's implementation. The MusicPlayerAdapter thus acts as a unified interface to the legacy MusicPlayer, capable of handling various formats by determining the appropriate conversion strategy based on the file type.