Создание объекта выбора между различными конкретными реализациями ABC - PullRequest
0 голосов
/ 30 ноября 2018

В моем коде в настоящее время у меня есть класс Obj, реализующий некоторые низкоуровневые функциональные возможности, ничего особенного.Теперь мне нужно добавить вторую реализацию Obj.Какая реализация создается, должно быть решено на основе некоторых свойств, которые должны быть проверены во время создания экземпляра.

Решение, которое я придумал, состоит в том, чтобы иметь абстрактный класс _absObj и две его реализации, _Obj1и _Obj2.Я все еще хочу представить на уровне API только один класс Obj, поэтому я заново реализовал Obj просто так:

class Obj:
    __new__(cls, stuff):
        if _check_something(stuff):
            return _Obj1(stuff)
        else:
            return _Obj2(stuff)

Это прекрасно работает, но я вижу несколько неудобств:

  • [несовершеннолетний] Obj не имеет документированных методов.Вероятно, это на самом деле преимущество.
  • [major] Obj не является подклассом absObj, даже если каждый объект, экземпляр которого создан из него, происходит от абстрактного класса.
  • [major]В тестах мне часто нужно высмеивать Obj, но я не могу высмеять ни один из его методов, у него нет ни одного!Я могу только посмеяться над одной из его реализаций, которая является хрупкой, поскольку я могу изменить реализацию, или может быть изменена конкретная реализация.

Есть ли более разумное решение?Если решение подразумевает сброс некоторого шума, еще лучше.

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Я бы написал так.AbsObj будет единственным "публичным" классом.Его реализации будут выбраны и возвращены AbsObj#create.

from abc import ABCMeta, abstractmethod

class AbsObj(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        '''Document this.'''
        pass

    @staticmethod
    def create(stuff):
        '''
        All the user needs to know is that create returns an AbsObj.
        The specific implementation shouldn't matter.
        '''
        if stuff:
            return Obj1(stuff)
        else:
            return Obj2(stuff)

class Obj1(AbsObj):
    def foo(self):
        print('number 1')

class Obj2(AbsObj):
    def foo(self):
        print('number 2')

AbsObj.create(True).foo
AbsObj.create(False).foo
0 голосов
/ 30 ноября 2018

Возможное решение заключается в следующем:

class Obj(ABC):
    @abstractmethod
    def f1(...):
        pass

    @abstractmethod
    def f1(...):
        pass

    @classmethod
    def factory(stuff):
        if _check_something(stuff):
             return _Obj1(stuff)
        else:
             return _Obj2(stuff)

Где _Obj1 и _Obj2 происходят от самого Obj.Это действительно работает, даже если я не совсем уверен во всех последствиях.

...