Design Patterns in Elixir

Rodrigo Martins
2 min readJul 3, 2023

--

Design patterns are fundamental solutions to common software design problems. While design patterns are often associated with object-oriented languages, they can also be applied in functional programming languages like Elixir. In this post, we’ll explore some design patterns that can be used effectively in Elixir, along with practical examples to illustrate their usage.

  • Singleton Pattern:

The Singleton pattern ensures that a class has only one instance and provides a global access point to that instance. In Elixir, we can achieve this using modules and ETS (Erlang Term Storage). Here’s a simple example of a Singleton pattern in Elixir:

defmodule Singleton do
use GenServer

def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def get_instance do
{:ok, pid} = GenServer.whereis(__MODULE__)
pid
end

# Implement GenServer callbacks here (handle_call, handle_cast, etc.)
end

By calling Singleton.get_instance, we can always get the same instance of the Singleton module throughout the application's lifecycle.

  • Factory Pattern:

The Factory pattern provides an interface for creating objects without specifying their concrete classes. In Elixir, we can implement the Factory pattern using functions that create and initialize specific structs or modules. Here’s an example:

defmodule ShapeFactory do
def create_shape(:circle) do
%Circle{}
end

def create_shape(:rectangle) do
%Rectangle{}
end
end

defstruct radius: 0, width: 0, height: 0

defmodule Circle do
def new(radius) do
%__MODULE__{radius: radius}
end
end

defmodule Rectangle do
def new(width, height) do
%__MODULE__{width: width, height: height}
end
end

Using ShapeFactory.create_shape, we can easily create instances of circles or rectangles without exposing the concrete implementations.

  • Observer Pattern:

The Observer pattern establishes a dependency between objects, so that when one object changes state, its dependents are notified and updated automatically. In Elixir, we can use Phoenix PubSub to implement the Observer pattern. Here’s an example:

defmodule StockMarket do
use Phoenix.PubSub

defmodule StockWatcher do
def start_link do
Phoenix.PubSub.subscribe(StockMarket, "stocks")
end

def handle_info({:stock_update, symbol, price}, state) do
IO.puts("Stock #{symbol} updated: #{price}")
{:noreply, state}
end
end
end

By subscribing StockWatcher to the "stocks" topic, it will be notified of any stock updates and react accordingly.

Design patterns offer elegant solutions to common design challenges, regardless of the programming paradigm. In Elixir, we can leverage patterns like Singleton, Factory, and Observer to improve code organization, promote reusability, and simplify complex designs.

By incorporating these design patterns into your Elixir projects, you can create more maintainable and flexible software solutions. Happy coding! 🚀💻

--

--

Rodrigo Martins

Inovação vem de pessoas que se divertem com seus trabalhos (Dr. Demin)