Understanding Factory Method and Strategy Design Patterns in Ruby on Rails
As developers, we often faced scenarios where keeping our code clean and scalable became a challenge, principally when a new feature demands handling very complex code logic. Today, we will talk about two design patterns that can help you simplify complexity with an elegant, less-coupling, and flexible code architecture.
Switching Behaviors at Runtime with The Strategy Pattern
The Strategy Pattern is an excellent option for scenarios when we have behaviors that can be encapsulated. It helps you to change across these behaviors as the business requirements demand. A good use case would be when we need to switch the entire business logic based on a param received on a controller for example. Let's see how to use The Strategy pattern for this:
Let's imagine we are working on a feature for the app. We'll need to deliver a list of doctors that should allow to be sorted by rating, distance, or availability. Using The Strategy pattern we can make the sorting logic pluggable and avoid hardcode the algorithm on the controller:
# app/strategies/sorting_strategy.rb
class SortingStrategy
def sort(doctors)
raise NotImplementedError, "Subclasses must implement the `sort` method"
end
end
# app/strategies/sort_by_rating.rb
class SortByRating < SortingStrategy
def sort(doctors)
doctors.order(rating: :desc)
end
# app/strategies/sort_by_distance.rb
class SortByDistance < SortingStrategy
def sort(doctors)
doctors.order(distance: :asc)
end
end
# app/controllers/doctors_controller.rb
def index
sorting_strategy = get_sorting_strategy(params[:sort_by])
@doctors = sorting_strategy.sort(Doctor.all)
end
private
def get_sorting_strategy(sort_by)
case sort_by
when 'rating'
SortByRating.new
when 'distance'
SortByDistance.new
else
SortByRating.new
end
end
Now, the sorting logic is modular, and including a new sorter in the future requires just the creation of a new Sort class file without changing any code of the other classes.
The Factory Method Pattern
The Factory Method is a way to encapsulate the object creation logic. It can help you for example, when you need to instantiate different services depending on some input like user roles or choose payment types of the format of a received file.
The Factory Method pattern will provide you with a clean way to delegate the object creation to specialised classes. Let's see an example:
Imagine you are integrating an application with multiple payment gateways (e.g., PayPal, Stripe, etc.). Avoiding let the code on the controller, let's use the Factory Method pattern to delegate this responsibility:
# app/services/payment_processor_factory.rb
class PaymentProcessorFactory
def self.create_processor(payment_method)
case payment_method
when :paypal
PaypalProcessor.new
when :stripe
StripeProcessor.new
else
raise "Unknown payment method"
end
end
end
# app/controllers/payments_controller.rb
def process_payment
processor = PaymentProcessorFactory.create_processor(params[:payment_method])
processor.process(params)
end
Now we have a centralised point to manage object creation, making the controller leaner and more maintainable.
Conclusion
Design patterns like Factory Method and Strategy will make your code base flexible and better structured. They are perfect tools for scenarios where you are managing object creation or swapping out algorithms. The result will be code decoupled, clean, and more testable.
Let us know if you found this useful or show us your way to use these patterns on your applications.
See you all next time! :)
Ps. if you have any questions
Ask here