Interpreter Design Pattern in Ruby

1. Definition

The Interpreter Design Pattern provides a way to evaluate language grammars or expressions for particular languages. It involves implementing an interpreter for a defined grammar. The pattern involves multiple levels of interpretation to evaluate complex expressions.

2. Problem Statement

Imagine you have a specific language, perhaps for configuration, querying, or certain domain-specific operations and you need to evaluate and execute expressions in this language. Implementing this directly can be cumbersome and hard to maintain.

3. Solution

The Interpreter pattern suggests modeling the domain with classes representing each grammar rule. Each rule can be interpreted or evaluated, leading to a tree of objects that can be traversed to interpret complex expressions.

4. Real-World Use Cases

1. SQL parsers evaluate SQL queries.

2. Regular expression engines interpret and match regex patterns.

3. Specialized languages like configuration or query languages.

5. Implementation Steps

1. Define a grammar for the language.

2. Create an abstract base class to represent individual grammar rules.

3. Implement concrete classes for each grammar rule.

4. Interpret the expressions by traversing the object structure.

6. Implementation in Ruby

# Step 1 and 2: Abstract Expression class Expression def interpret(context) raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'" end end # Step 3: Concrete Terminal Expression class TerminalExpression < Expression def initialize(data) @data = data end def interpret(context) context.include?(@data) end end # Step 3: Concrete OR Expression class OrExpression < Expression def initialize(expr1, expr2) @expr1 = expr1 @expr2 = expr2 end def interpret(context) @expr1.interpret(context) || @expr2.interpret(context) end end # Step 3: Concrete AND Expression class AndExpression < Expression def initialize(expr1, expr2) @expr1 = expr1 @expr2 = expr2 end def interpret(context) @expr1.interpret(context) && @expr2.interpret(context) end end # Client code def get_male_expression robert = TerminalExpression.new("Robert") john = TerminalExpression.new("John") OrExpression.new(robert, john) end def get_married_woman_expression julie = TerminalExpression.new("Julie") married = TerminalExpression.new("Married") AndExpression.new(julie, married) end male_expr = get_male_expression married_woman_expr = get_married_woman_expression puts "John is male? #{male_expr.interpret('John')}" puts "Julie is a married woman? #{married_woman_expr.interpret('Married Julie')}" 

Output:

John is male? true Julie is a married woman? true 

Explanation:

1. Expression is an abstract class that represents a grammar rule.

2. TerminalExpression represents individual data or terminal symbols in the grammar.

3. OrExpression and AndExpression are composite expressions that combine other expressions.

4. In the client code, we create expressions to check if a string represents a male name or a married woman's name and interpret these expressions.

7. When to use?

Use the Interpreter Pattern when:

1. You need to interpret a specialized language.

2. The grammar of the language is relatively simple.

3. Efficiency is not a top priority (interpreters can be slow compared to other approaches).


Comments