Table-driven factory vs inheritance

144 Views Asked by At

I have a bunch of derived classes that only differ by the static methods.

(It's Ruby, but question is not about Ruby, it's about the design.)

class Exporter
  def export
    # ...
  end
end

class ExporterA < Exporter
  def from
    'aaa_table'
  end

  def fields
    ['a1', 'a2', 'a3']
  end

  def to
    'aaa_file'
  end
end

class ExporterB < Exporter
  def from
    'bbb_table'
  end

  def fields
    ['b1', 'b2', 'b3']
  end

  def to
    'bbb_file'
  end
end

So, I looked at this and came up with idea of placing all this static data to some kind of a table and just use Exporter with the appropriate attributes. In this case, I would need some kind of ExporterFactory class which is going to know who is who and how to create the A and B exporters.

class ExporterFactory
  def _table
    return {
      :a => {
        :from => 'aaa_table',
        :fields => ['a1', 'a2', 'a3'],
        :to => 'aaa_file',
      },
      :b => {
        :from => 'bbb_table',
        :fields => ['b1', 'b2', 'b3'],
        :to => 'bbb_file',
      },
    }
  end

  def create(type)
    return Exporter.new(self._table[type])
  end
end

Again, I looked at this and now I don't really like this approach. Reasons:

  • My real data for _table is much bigger so my table looks heavy and ugly.
  • Now you can create Exporters that don't really make sense.
  • It looks like the factory knows too much, I would prefer to have data about A-export encapsulated in ExporterA.

I can't decide. The second approach seems more logical, but I still want to use the first one. My main idea is “I want to use inheritance just to organize that big and ugly table”.

What should I choose? What kind of problems I'm going to have in each of that ways?

1

There are 1 best solutions below

0
On BEST ANSWER

I agree that your Factory knows too much, meaning that it has to change every time any Exporter changes. Also, if you ever have an Exporter that needs additional code, there will be no way to create it. Your first design allows you to write Exporters that override superclass methods if you ever need them.

You could make your first design more succinct by putting the data in initialize and overriding that rather than three methods:

class Exporter
  attr_accessor :from, :fields, :to

  def initialize(from, fields, to)
    self.from = from
    self.fields = fields
    self.to = to
  end

  def export
    # ...
  end

end

class ExporterA < Exporter
  def initialize
    super 'aaa_table', ['a1', 'a2', 'a3'], 'aaa_file'
  end 
end

class ExporterB < Exporter
  def initialize
    super 'bbb_table', ['b1', 'b2', 'b3'], 'bbb_file'
  end 
end