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
AxisInterface. 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.
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?