Let’s say we have a Rails application and we want to interact with Twitter. There are probably a few good open source gems already that could help us out with this task.
class HomeController < ApplicationController
def index
twitter_client = SomeTwitterClient.connect(ENV['TWITTER_API_KEY'])
feed = twitter_client.feeds.get("some_user")
@tweets = feed.tweets
end
end
Then we just render @tweets
in our view. Although our code is not too bad, we’re probably going to run into unwanted issues if we repeat this throughout our application. Consider what happens if we need to update the gem for security fixes, and we discover it also changed the way it connects and interacts with Twitter.
# Notice we call `tweets` directly from the client now
client = Twitter::Client.connect(api_key)
tweets = client.tweets("some_user")
Even a minor change like this will force us to update every piece of code that referenced the old API. Instead, we can write our own application specific interface, with the methods we wish the third-party API had.
module TwitterClient
def tweets(username)
client.tweets(username)
end
private
def client
@client ||= SomeTwitterClient.connect(ENV['TWITTER_API_KEY'])
end
end
Then in our controller:
def index
@tweets = TwitterClient.tweets("some_user")
end
Now we isolated the code, so if the third-party library changes we only have to update it in a single place. This is known as the adapter design pattern and allows two otherwise incompatible interfaces to work together.