Imagine you have a class which gets XML data, parses it and then stores the parsed data on a database. You could very well write it like this:
class Consumer def consume(data) parsed_data = XMLParser.parse(data) DB.store(parsed_data) end end
There is (at least) one big problem with this approach, and it will manifest itself when you want to support another data format, such as JSON. The Consumer class is tightly coupled with the XMLParser, and therefore it's hard to add other parsers, so we need to decouple them. We're going to do that using a technique called Dependency Injection, like so:
class Consumer attr_accessor :parser def initialize(parser: XMLParser) @parser = parser end def consume(data) parsed_data = parser.parse(data) DB.store(parsed_data) end end
This way we can inject the parser on initialization, or even after that. Note that we have a default which is the XMLParser, but it is not mandatory to have a default value.
So, after the refactoring, adding a JSONParser is easy.
consumer = Consumer.new(parser: JSONParser) consumer.consume(data)
This technique can be used in many scenarios, in this example we used it as part of a pattern that's called strategy pattern.
Note: The same technique could be applied to inject the DB class instead of hard coding it.