samedi 19 décembre 2020

Is a python `abstract property` that returns an abstract class an example of the Factory Pattern

I need to document my design, in particular, the design patterns used, and would like to use the standard terminology.

From Refactoring Guru, "Factory Method defines a method, which should be used for creating objects instead of direct constructor call. Subclasses can override this method to change the class of objects that will be created".

I have a CraneInterface class with an abstract method, and the signature of this method enforces that its return type is an implementation of an abstract class. Essentially, subclasses of CraneInterface "override this method to change the class of objects that will be created". The only diference with my version is that it does not necessarily "create" a new instance, it could also return one that already exists. Is this still the Factory Pattern? And if not, does this design pattern have a common name?

i.e: A traditional factory looks like this:

class IAnimal(ABC):
    @abstractmethod
    def speak(self):
        pass

class Dog(IAnimal):
    #overide
    def speak(self):
        print('Woof')

class Cat(IAnimal):
    #overide
    def speak(self):
        print('Meow')
    
class AnimalFactory(ABC):
    @abstractmethod
    def make_animal(self) -> IAnimal:
        pass

class CatFactory(AnimalFactory):
    #overide
    def make_animal(self) -> IAnimal:
        return Cat()

class DogFactory(AnimalFactory):
    #overide
    def make_animal(self) -> IAnimal:
        return Dog()

My code looks more like this:

class AnimalFactory2(ABC):
    @property
    @abstractmethod
    def animal(self) -> IAnimal:
        pass

class CatFactory2(AnimalFactory2):

    def __init__(self):
        self.__cat = Cat()

    #overide
    @property
    def animal(self) -> IAnimal:
        return self.__cat

class DogFactory2(AnimalFactory2):

    def __init__(self):
        self.__dog = Dog()    
    
    #overide
    @property
    def animal(self) -> IAnimal:
        return self.__dog

Does the second example use the Factory Pattern? Does it have a different name or even have a name at all? The main difference is that it does not create a new instance each time it is called.

Extra info:

In my actual code, I have a 3 axis CraneInterface class that has abstract methods for all the ways you can interact with a crane. It is implemented by a CraneSimulator and a CraneOpcuaClient that actually talks to a real crane. The original design of the simulator implemented the abstract method inside the CraneSimulator class, however, this had lots of duplicated code, as every function of the crane was repeated for each of the 3 axes. To solve this, I created an AxisSimulation class which had methods to interact with it, and then there are 3 instantiations inside the CraneSimulator, and the implementations of the CraneInterface abstract methods simply forward the request to one of the 3 axis objects.

The problem was that the CraneInterface also needed the ability to notify "observers" whenever the state of the crane changed, for example, a position or temperature change. I.e the CraneInterface needed to have a function add_on_x_position_changed_callback(self, callback: Callable[[Position],None]). To do this, the CraneInterface had properties with custom setters that notified a list of observers whenever the value was set. By putting the AxisSimulation inside the CraneSimulator the properties had moved out of the CraneInterface, and the add_on_changed_callback methods of the CraneInterface would no longer work.

So to solve this, the CraneInterface had an abstract property to return an abstract AxisInterface class (like the AnimalFactory2 example). The AxisInterface then had the observable properties with a custom setter (and methods to add observers), so that users of the CraneInterface can add observers to the data.

I know that the "observable" part is an example of the Observer pattern, but is the deferring of the type of Axis implementation returned, an example of the Factory Pattern?

Thanks.

Aucun commentaire:

Enregistrer un commentaire