Вариант с фабрикой также придется дополнить методом регистрации новой реализации.
Накидал небольшой прототип.
_IMPLEMENTATIONS = {}
class Factory(object):
def __init__(self, implementation_selector):
self._implementation_selector = implementation_selector
@classmethod
def register_implementation(cls, implementation):
_IMPLEMENTATIONS.setdefault(cls.__name__, {})[
implementation.name] = implementation()
def __call__(self):
return _IMPLEMENTATIONS[self.__class__.__name__][
self._implementation_selector()]
def __getattr__(self, name):
return getattr(self(), name)
class Implementation(object):
name = None
class AvatarFactory(Factory):
'''Avatar'''
class AvatarDefaultImplementation(Implementation):
name = 'default'
def get_avatar_path(self, user, size=48):
return '/static/avatars/%s/%d' % (user.login, size,)
class GrvatarImplementation(Implementation):
name = 'gravatar'
def get_avatar_path(self, user, size=48):
import hashlib, urllib
hash = hashlib.md5(user.email.lower()).hexdigest()
gravatar_url = 'http://www.gravatar.com/avatar/' + hash + '?'
gravatar_url += urllib.urlencode({'s':str(size)})
return gravatar_url
AvatarFactory.register_implementation(AvatarDefaultImplementation)
AvatarFactory.register_implementation(GrvatarImplementation)
implementation = 'default'
def implementation_selector():
return implementation
class User(object):
avatar = AvatarFactory(implementation_selector)
def __init__(self, login, email):
self.login = login
self.email = email
def get_avatar_path(self, size=48):
return self.avatar.get_avatar_path(self, size)
user = User('login', 'test@example.com')
user2 = User('test2', 'test2@example.com')
print user.get_avatar_path()
print user2.get_avatar_path()
implementation = 'gravatar'
print user.get_avatar_path()
print user2.get_avatar_path()
И останется решить еще один вопрос, так как методов в User много, то придется выбрать из вариантов:
1. создавать по фабрике на каждый чих
class User(object):
avatar = AvatarFactory(...)
amount = AmountFactory(...)
gender = GenderFactory(...)
some_future = SomeFutureFactory(...)
...
Как вариант возникла идея создания декоратора который будет помечать метод, как имеющий возможность заменяться фабрикой, его реализация и будет дефолтной, и будет возможность зарегистрировать новые реализации для этого метода.
class AvatarFactory(Factory):
'''Avatar'''
class User(db.Model):
...
@factory(AvatarFactory):
def get_avatar_path(self, size=48):
return 'default path'