1

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.

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.

3
  • 3
    The purpose of design patterns is to communicate. If you have to explain why a particular maneuver you've accomplished fits a particular design pattern, then the communication advantage of using the pattern name is lost. – Robert Harvey Dec 19 '20 at 15:05
  • @Robert Harvey I agree, and this is exactly what I'm trying to do. I want to succinctly communicate my design. I.e, Say, "The Crane Interface has a factory method to retrieve the Axis class". But I'm not sure if that is true. Perhaps it isn't a Factory method. Perhaps this maneuver doesn't have a name at all. In that case I will have to think of another way to succinctly communicate the design. – Blue7 Dec 19 '20 at 15:17
  • Just explain what the method does. You don't have to communicate every last detail of how the method works; just give the broad brush strokes. Your developers know how to read code. – Robert Harvey Dec 19 '20 at 15:25
1

After some thought, I think the second code snippet does not use the Factory method as it is not concerned with the creation of objects.

However, the code can be refactored to use the Factory method and better communicate the intent:

class CraneInterface(ABC):

    def __init__(self):
        self.__axis = self._make_axis()

    @property
    def axis(self) -> AxisInterface:
        return self.__axis

    @abstractmethod
    def _make_axis(self) -> AxisInterface:
        """
        Factory method: "Subclasses override to change the class of object that will be created"
        """
        pass
3
  • 1
    You are focusing on the wrong things. Patterns aren't defined by the details of a particular implementation, it's the intent and the relationships between elements that count. Factory Method doesn't require inheritance, or prevent you from caching. The idea behind it is that the code that needs to invoke the method that creates some other object doesn't know what to create, so you supply it with an object that does know - the FactoryMethod. It's literally an object that represents a creation function. In Python, you can do this with a simple lambda - it would still be the same pattern. – Filip Milovanović Dec 19 '20 at 15:56
  • So it's enough to just say that your clients should pass one of these objects if they wish to allow some other code to invoke it as a factory, or to derive from the base class if they wish to supply their own factory. Regarding caching a previously created instance: you need to think about whether this behavior is surprising to the client or not. E.g., it might be OK if the returned object is immutable, or if client code doesn't make the assumption that it will always get a new instance. – Filip Milovanović Dec 19 '20 at 15:56
  • @FilipMilovanović is right. Patterns are not about structure. They’re about intent. The best proof is this is that some patterns have exactly the same structure yet different names for their different intents. – candied_orange Dec 19 '20 at 16:07
0

To re-iterate the most important part of your quote from Refactoring Guru:

Factory Method defines a method, which should be used for creating objects instead of direct constructor call.

If the function that returns an AxisInterface does not have the purpose of creating an axis for the caller, then you are not using the Factory Method pattern, regardless of how much your code resembles the implementation of the pattern.

Design patterns are not defined by their structure but by the intent of the code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.