tags:

views:

2790

answers:

4

I'm new to Python and need some advice implementing the scenario below.

I have two classes for managing domains at two different registrars. Both have the same interface, e.g.

class RegistrarA:
  __init__(self, domain):
    self.domain = domain

  def lookup(self):
    ...

  def register(self, info):
    ...

and

class RegistrarB:
  __init__(self, domain):
    self.domain = domain

  def lookup(self):
    ...

  def register(self, info):
    ...

I would like to create a Domain class that, given a domain name, loads the correct registrar class based on the extension, e.g.

com = Domain('test.com') #load RegistrarA
com.lookup()

biz = Domain('test.biz') #load RegistrarB
biz.lookup()

I know this can be accomplished using a factory function (see below), but is this the best way of doing it or is there a better way using OOP features?

def factory(domain):
  if ...:
    return RegistrarA(domain)
  else:
    return RegistrarB(domain)
+2  A: 

Assuming you need separate classes for different registrars (though it's not obvious in your example) your solution looks okay, though RegistrarA and RegistrarB probably share functionality and could be derived from an abstract base class.

As an alternative to your factory function, you could specify a dict, mapping to your registrar classes:

Registrar = {'test.com': RegistrarA, 'test.biz': RegistrarB}

Then:

registrar = Registrar['test.com'](domain)

One quibble: You're not really doing a Class Factory here as you're returning instances rather than classes.

Jeff Bauer
+5  A: 

I think using a function is fine.

The more interesting question is how do you determine which registrar to load? One option is to have an abstract base Registrar class which concrete implementations subclass, then iterate over its __subclasses__() calling an is_registrar_for() class method:

class Registrar(object):
  def __init__(self, domain):
    self.domain = domain

class RegistrarA(Registrar):
  @classmethod
  def is_registrar_for(cls, domain):
    return domain == 'foo.com'

class RegistrarB(Registrar):
  @classmethod
  def is_registrar_for(cls, domain):
    return domain == 'bar.com'


def Domain(domain):
  for cls in Registrar.__subclasses__():
    if cls.is_registrar_for(domain):
      return cls(domain)
  raise ValueError


print Domain('foo.com')
print Domain('bar.com')

This will let you transparently add new Registrars and delegate the decision of which domains each supports, to them.

Alec Thomas
A: 

Thanks Alec and Jeff for the quick responses

Alec: delegating the decision of which Registrar to load based on the domain extension is exactly what I'm after

Jeff: I do need separate classes for different registrars as their APIs have nothing in common apart from the basic interface (lookup, register etc) their implementations are totally different. I agree the examples are a little simplistic

Thanks again for the insight

This is not a forum, answer that in comments
e-satis
+2  A: 

In Python you can change the actual class directly:

class Domain(object):
  def __init__(self, domain):
    self.domain = domain
    if ...:
      self.__class__ = RegistrarA
    else:
      self.__class__ = RegistrarB

And then following will work.

com = Domain('test.com') #load RegistrarA
com.lookup()

I'm using this approach successfully.

bialix