A Live Developer Journal

Creating An Abstract Factory (Modern and Victorian Furniture Maker) in Ruby

The c2Wiki has a Roadmap for learning design patterns. I'm going to start by looking at each of the creational design patterns, and note what the purpose of each of them are.

Creational Design patterns: Brief intent overview

Abstract Factory Pattern in Ruby

Source: Refactoring.guru

Abstract factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

I went ahead and played around with this, and attempted to create my own version of abstract factory, using a furniture shop as an example. The final application creates one of each type of furniture. I'll put all my code below:

Modern Chair

The modern chair class has two methods, .describe_legs and .describe_sitting experience. The chair also has a name to reflect it's type. Same for the victorian chair.

I used the RSpec testing suites 'respond_to' method as an interface replacement, to make it explicit which methods the chair is supposed to implement.

require "modern_chair"

describe "modern_chair" do
  let(:modern_chair) { ModernChair.new }
  it { expect(modern_chair).to respond_to(:describe_legs) }
  it { expect(modern_chair).to respond_to(:describe_sitting_experience) }

  describe ".describe_legs" do
    it "has 3 bamboo legs" do
      expect(modern_chair.describe_legs).to eq("3 legs made of cream bamboo")

  describe ".describe_sitting_experience" do
    it "Like being wrapped in a cocoon" do
      expect(modern_chair.describe_sitting_experience).to eq("This round chair feels like being wrapped up in a cocoon")

class ModernChair
  attr_reader :name

  def initialize
    @name = "Modern Chair"

  def describe_legs
    "3 legs made of cream bamboo"

  def describe_sitting_experience
    "This round chair feels like being wrapped up in a cocoon"

Modern Coffee Table

The modern coffee table has one method called .describe_surface. Same for the victorian chair.

require "modern_coffee_table"

describe "modern_coffee_table" do
  let(:modern_coffee_table) { ModernCoffeeTable.new }

  it { expect(modern_coffee_table).to respond_to(:describe_surface) }

  describe ".describe_table_surface" do
    it "Has a glass surface" do
      expect(modern_coffee_table.describe_surface).to eq("Glass surface. Funny when cats sit on it.")

class ModernCoffeeTable
  attr_reader :name

  def initialize
    @name = "Modern Coffee Table"

  def describe_surface
    "Glass surface. Funny when cats sit on it."

Modern Sofa

The modern sofa has one method, .describe_fabric. Same for the Victorian sofa.

require "modern_sofa"

describe "modern_sofa" do
  let(:modern_sofa) { ModernSofa.new }

  it { expect(modern_sofa).to respond_to(:describe_fabric) }

  describe ".describe_fabric" do
    it "Made from cotton" do
      expect(modern_sofa.describe_fabric).to eq("Made from cotton.")

class ModernSofa
  attr_reader :name

  def initialize
    @name = "Modern Sofa"

  def describe_fabric
    "Made from cotton."

Modern Furniture Factory

The modern furniture factory has three methods, which all create an instance of each of the furniture type classes we have already created (Chair, coffee table and sofa). Same for the victorian furniture factory.

require "modern_furniture_factory"

describe "modern_furniture_factory" do
  let(:modernff) { ModernFurnitureFactory.new }

  it { expect(modernff).to respond_to(:create_chair) }
  it { expect(modernff).to respond_to(:create_coffee_table) }
  it { expect(modernff).to respond_to(:create_sofa) }

  describe ".create_chair" do
    it "creates modern chairs" do
      expect(modernff.create_chair).to be_an_instance_of(ModernChair)

  describe ".create_coffee_table" do
    it "creates modern coffee tables" do
      expect(modernff.create_coffee_table).to be_an_instance_of(ModernCoffeeTable)

  describe ".create_sofa" do
    it "creates modern sofas" do
      expect(modernff.create_sofa).to be_an_instance_of(ModernSofa)

Furniture Factory

The furniture factory can be initialized with a specific furniture factory type. I think this is a bit redundant and maybe I misunderstood the example. It would be cool for this to have a factory type list, which you can add to or delete factories from. Then a GUI where you can swap out factories on the fly.

class FurnitureFactory
  attr_reader :factory

  def initialize(factory = ModernFurnitureFactory.new)
    @factory = factory


The application code has a factory type store, then loops through the factories and creates each of the furniture types which are consistent for each of these categories. I think this code here is a little too procedural, but not sure. It seems that the abstract factory pattern is used for reskinning objects into a different style, because this wouldn't work if one of the furniture styles had an extra piece of furniture that the other furniture factories didn't have. Or maybe it would, not sure.

require_relative 'furniture_factory'
require_relative 'modern_furniture_factory'
require_relative 'victorian_furniture_factory'

factories = [(FurnitureFactory.new).factory, (FurnitureFactory.new(VictorianFurnitureFactory.new)).factory]

factories.each do |f|
  # Create Chairs
  chair = f.create_chair
  puts chair.name.upcase
  puts "- #{chair.describe_legs}"
  puts "- #{chair.describe_sitting_experience} \n\n"

  #Create Coffee Tables
  coffee_table = f.create_coffee_table
  puts coffee_table.name.upcase
  puts "- #{coffee_table.describe_surface} \n\n"

  #Create Sofas
  sofa = f.create_sofa
  puts sofa.name.upcase
  puts "- #{sofa.describe_fabric} \n\n"

This was fun. I want to find a couple more examples of the abstract factory pattern so I can play around with it some more and maybe come up with some different use-cases.