Notes: Go Design Patterns - Adapter Pattern

Notes: Go Design Patterns - Adapter Pattern

This blog contains the implementation of adapter design pattern using XML adapter to implement JSON related interface

ยท

2 min read

Table of contents

No heading

No headings in the article.

Adapter design pattern as the name suggests lets an interface adapt to another similar interface. This is a structural pattern. Let's dive directly into the example.

Let's assume that we have a DataParser interface that contains methods around JSON formatted data. I'm adding a very simple interface where we have one method which just parses JSON data:

type DataParser interface {
    parseJSON(string) *User
}

Now let's say later on we want to add XML parser as well. Now that our code has implementations of DataParser everywhere, it'd be really difficult to change everything again to include XML parser.

So, to avoid this situation what we can do is we can create an adapter that encapsulates XML parser but implements all the DataParsermethods. So, whenever we have to use XML parser, we can use the adapter instead.

Adapter could look like this:

type XMLToJSONAdapter struct {
    XMLParser *XMLParser
}

and its DataParser method implementation could be as simple as this:

func (a *XMLToJSONAdapter) parseJSON(userSerialized string) *User {
    return a.XMLParser.parseXML(userSerialized)
}

As you can see, internally parseJSON is calling XML Parser only. This way everything works without making a lot of changes.

Complete working code:

package main

import (
    "encoding/json"
    "encoding/xml"
    "fmt"
)

type User struct {
    Name     string   `json:"name" xml:"name"`
    Age      int      `json:"age" xml:"age"`
    Skills   []string `json:"skills" xml:"skills"`
    IsActive bool     `json:"isActive" xml:"isActive"`
}

type DataParser interface {
    parseJSON(string) *User
}

type JSONParser struct{}

func (p *JSONParser) parseJSON(userSerialized string) *User {
    var user User

    err := json.Unmarshal([]byte(userSerialized), &user)

    if err != nil {
        fmt.Println(err.Error())
    }

    return &user
}

// now we want to add a new XML parser
// implementing legacy DataParser interface
type XMLParser struct{}

func (p *XMLParser) parseXML(userSerialized string) *User {
    var user User

    err := xml.Unmarshal([]byte(userSerialized), &user)

    if err != nil {
        fmt.Println(err.Error())
    }

    return &user
}

// adapter to convert XML parser
// to JSON parser i.e. to implement
// DataParser interface
type XMLToJSONAdapter struct {
    XMLParser *XMLParser
}

func (a *XMLToJSONAdapter) parseJSON(userSerialized string) *User {
    return a.XMLParser.parseXML(userSerialized)
}

func main() {
    jsonData := `{
        "name": "nidhi pandey",
        "age": 30,
        "skills": ["qa", "testing", "coding"],
        "isActive": false
    }`

    xmlData := `<?xml version="1.0" encoding="UTF-8" ?>
    <root>
        <name>kshitij kumar</name>
        <age>21</age>
        <skills>backend</skills>
        <skills>frontend</skills>
        <isActive>true</isActive>
    </root>`

    jsonParser := JSONParser{}
    fmt.Printf("Parsed JSON: %#v\n\n", jsonParser.parseJSON(jsonData))

    xmlParserAdapter := XMLToJSONAdapter{}
    fmt.Printf("Parsed XML: %#v\n\n", xmlParserAdapter.parseJSON(xmlData))
}
ย