Adapter Design Pattern in Ruby
Introduction
The Adapter
design pattern is used, when there are two or more objects, which need to communicate to each other, but unable to do so, because their interfaces do not match. And the adapter is kind of a bridge between these objects.
This design pattern is heavily used in Rails. Particularly in ActiveRecord
adapters are implemented to communicate with different databases and provide a common interface, so you don't bother whether it is PostgreSQL, MySQL or any other database. In ActiveJob
adapters are used to communicate with background job providers, and so on.
Example
It is always a good idea to write an adapter if you don't want to depend on particular implementation of some functionality.
Say, you want to count and delete some pages in a PDF. The simpliest solution is to use a prawn
gem. Let's define an autonomous service which will be used in the application:
module PDFService
class Document
DEFAULT_ADAPTER = PDFService::PrawnAdapter
attr_reader :adapter, :document
delegate :count_pages, :delete_page, to: :document
def initialize(options = {}, adapter = nil)
@adapter = (adapter || DEFAULT_ADAPTER)
@document = adapter::Document.new(options)
end
end
end
And a corresponding PrawnAdapter
, which is default for this service:
module PDFService
module PrawnAdapter
class Document
attr_reader :document
delegate :delete_page, to: :document
def initialize(options = {})
@document = Prawn::Document.new(options)
end
def count_pages
document.page_count
end
end
end
end
The caller now can either use the default handler or explicitly specify which adapter to use:
service = PDFService::Document.new(template: @document.path)
service.count_pages
service.delete_page(1)
The service is built independent from a prawn
gem. If the other day we decide to use another gem, we won't need to change a single line of code in the application but simply add another adapter file.
Conclusion
Adapter design pattern provides the following benefits:
- Maintainable code
- Encapsulated logic
- Ease in business-logic extension