ProgrammerGuide.Net | Step by Step Programmer Guide

PROVIDER PATTERN AND ADAPTER PATTERN

One of the biggest problem or challenge coders face while writing code is interacting with an external service. To make the code open to any sort of changes later on, it is suggested to use a dependency injection known as a provider. In this article, we will learn more about this so-called provider.

PROVIDER PATTERN

Provider patterns adapt the different data sources third party APIs to be used uniformly for .NET applications to choose from one or more implementations. This provider pattern model was formulated by Microsoft in around 2005. While in this article we are firm to believe that a provider pattern is just a use of dependency injection several others say that it’s not even a real pattern and it just another name for the already existing strategy pattern which we don’t think is right.

Let’s understand everything better with an example.

SIMPLE WEATHER EXAMPLE

In this example, we will write a program which will examine the weather outside in a specific city using an external service and tell us if we should be wearing long sleeves or short sleeves.

The components of this program include – an entity, a use case service and a provider package.

Entity

Entity in our program is a struct representing the weather. Usually in bigger applications, some business policies need to be kept in mind whereas in this case we don’t have to do so.

package weather

type Weather struct {
    Temp     float32
    Pressure float32
    MinTemp  float32
    MaxTemp  float32
}

In this above example, the program will tell us to have short sleeves if the weather is above 20 degrees or in the other scenario, long-sleeved clothing. It gets the weather from a WeatherProvider interface.

type WeatherProvider interface {
GetWeatherByCity(city string) (weather.Weather, error)
}

Since interfaces are implemented implicity, it allows you to declare the interface next to use and not the implementation.

The WeatherProvider interface method only knows about the existence of our entity and wouldn’t care who brings the data nor how is it being generated.

Let’s look at the following code:

package forecasting

import (
    "fmt"

    "github.com/OrenRosen/simpleweather/weather"
)

type WeatherProvider interface {
    GetWeatherByCity(city string) (weather.Weather, error)
}

type service struct {
    weatherProvider WeatherProvider
}

func NewService(p WeatherProvider) *service {
    return &service{
        weatherProvider: p,
    }
}

func (s *service) WhatToWear(city string) (string, error) {
    w, err := s.weatherProvider.GetWeatherByCity(city)
    if err != nil {
        return "", fmt.Errorf("WhatToWear: %w", err)
    }

    if w.Temp < 21 {
        return "long sleeves", nil
    }

    return "short sleeves", nil
}

Here the forecasting service fetches the weather using WeatherProvider interface and calculates the weather accordingly giving us the result as to what will be the most convenient for us to wear.

PROVIDER

The provider can be implemented only after knowing the details of the external service hereby acting as a adapter between the external service and our application. In our example, the OpenWeather API collects the information about weather and the provider will convert the response into the application Weather entity.

package openweather

import (
“github.com/Orenrosen/simpleweather/weather”
)
Type weatherResponse struct {
Message string
Main struct {
Temp float32 ‘json:”temp”’
Pressure float32 ‘json:”pressure”’
TempMin float32 ‘json:”temp_min”’
tempMax float32 ‘json:”temp_max”’
}
}
func (r weatherResponse) ToWeather() weather.weather {
Return weather.Weather{
Temp: r.Main.Temp,
Pressure: r.Main.Pressure,
MinTemp: r.Main.TempMin,
MaxTemp: r.Main.TempMax,
}
}

The response struct is used in defined in our openweather package which is used to convert the language of the external service to our own application language.

The response struct WeatherResponse is coupled to the OpenWeather API and contains JSON tags according to it. On the contrary, application entity doesn’t have any tags, which is why it is essential to make this distinction so that we don’t mix up our entity with the JSON tags. If the OpenWeather API changes, our entity won’t change. In our example, if we return “temperature” instead of “temp” we would only have to change the provider’s code and not the application code.

The provider itself calls the external API and converts the response into the entity as shown below:

Package openweather
Import (
“encoding/json”
“fmt”
“io/ioutil”
“net/http”
“github.com/Orenrosen/simpleweather/weather”
)
Const (
Endpoint = https://api.openweathermap.org/data/2.5
patheFormatWeatherByCity = “/weather?q=%s&appid=%s&units=metric”
)
Type provider struct {
Apikey string
}
Func NewProvider(apikey string) *provider {
Return &provider{
Apikey: apiKey,
}
}
Func (p * provider) GetWeatherByCity(city string) (weather.weather, error) {
// compose the url. Note that it’s not the best way to add query params.
Path := fmt.Sprintf(pathFormatWeatherByCity, city, p.apiKey)
U := endpoint + path
Res, err : = http.Get(u)
If err != nil {
    Return weather. Weather{}, fmt.errorf(“openweather.GetweatherBycity failed http GET: %s”, err)
}
Defer res.body.Close()
// read the response body and encode it into the response struct
bodyRaw, err := ioutil.ReadAll(res.Body)
if err != nil {
return weather.Weather{}, fmt.errorf(“openweather.GetWeatherByCity failed reading: “%s”, err)
}
Var weatherRes weatherResponse
If err = json.Unmarshal(bodyRaw, &weatherRes); err != nil {
Return weather.Weather{}, fmt.errorf(“openweather.getWeatherByCity got error from OpenWeather: %s”, weatherRes.Message)
}
If res.StatusCode != http.StatusOK {
Return weather.Weather{}. Fmt.Errorf(“openweather.GEtWeatherByCity got error from OpenWeather: %s”, weatherRes.Message)
}
//return the external response converted into an entity
return weatherRes.ToWeather(), nil
}

It is to be noted here that this package’s name is the same as the actual external service’s name that is openweather.

Main

This is the part where we compose everything.

package main

import (
“flag”
“fmt”
“log”
“github.com/OrenRosen/simpleweather/forecasting”
“github.com/OrenRosen/simpleweather/openweather”
)
const apiKey = “put_your_api_key_here”
func main() {
cityP := flag.String(“city”, “London”, “City to be queried”)
flag.Parse()
city := *cityP
openweatherProvider := openweather.NewProvider(apiKey)
weatherService := forecasting.NewService(openweatherProvider)
outfit, err := weatherservice.WhatTowear(city)
if err != nil {
log.Fatalf(“could’nt get what to wear in %s: %v”, city, err)
}
fmt.Printf(“you should wear in %s: %s\n”, city, outfit)

}

Main gets a flag representing a city, initializes the provider and the service, and triggers our forecasting service method.

Separating the code that knows about the outside world is very important to enable fluidity of the program so that we can make changes whenever required.

Clean Architecture

A person known as Uncle Bob is said to have designed and created clean architecture. It provides an effective testing strategy and its main objective is the separation of concerns, allowing developers to segregate different business logics in a use case so its easier to g=find and cannot be duplicated elsewhere.

This is the most commonly seen picture of the clean architecture.

• The arrows inside the circles show the dependency rule. It is done to demonstrate that the inner layers are not dependent on any outer layer.
• The outer layer contains low-level components such as UI, DB, or a 3rd party service.

CROSSING BOUNDARIES

The question may arise if our mind whether we can cross the boundaries outwards; the only answer to this would be to use interfaces. Although, we cannot really call the outer layer directly, the application-service calls the interface.

Now, let us discuss about the ways to check ourselves if we violate the dependency rule.

  • Wording: Our application is required to say what it needs and not how it needs to be done. The way we present ourselves in the program is very important as each word matters a lot. For example, in our program, it was important to mention WeatherProvider and not OpenWeatherProvider as the former is the name of the application layer.
  • Import: Since the package belongs to the outermost layer, it can’t be a dependency on any other package except for main. Hence, the only import to our package should be from main.
  • External Awareness: The only component that is aware of the external OpenWeather API’s existence is the provider and main.
  • Package Naming: The name of the external service that the package uses is the name of the package. It is used to clear that it is an implementation detail and a mechanis,.

Let’s summarize some of the benefits now:

Separation of dependencies
As the name suggests, making our dependencies separated is important and so is authentication into the external service. It helps during pull requests.
Consistency and Standardization
Maintaining a consistent style guide for dealing with familiar problems helps to make the work a lot easier.
Testable
Since any program relies on an interface rather than an actual implementation, it makes the use case testable.

CONCLUSION

The Go software is suitable for the example that we just saw and learnt a lot about as it has a very strong implicit interface implementation and package structure as well as naming. It has a lot of benefits in the long run as it is very easy to customize as well. For now, to many people it may look like some sort of rocket science as it might seem there is a lot to write, but it’s the same pattern that always needs to be followed and it gives us a code that is separated by dependencies, is flexible and very testable. Hope you all got to know a lot about the world of coding even if you are someone who is not interested in programming and coding stuff as it was just a slight intro into the world of coding.provider-pattern-and-adapter-pattern

Please click here for related products on Amazon!

Jayashee

Add comment

Want to Check Celebrities News?