3
\$\begingroup\$

I have been reading today about the Abstract Factory Pattern, and tried to make the following implementation.

I have seen a lot of implementations in the internet, where they use switch statements, but I must say that I didn't like that much, since the more factories you make, it seems to me that it makes very difficult to add new products, if needed.

Anyways, I was hoping you to take a look at it and let me know your opinions. Thanks in advance for taking your time to review it.

Factories

from abc import ABC, abstractmethod


class PlayerFactory(ABC):
    """
    This class is meant to be an interface
    """
    @abstractmethod
    def create_goalkeeper(self):
        pass

    @abstractmethod
    def create_defender(self):
        pass


class FootballPlayerFactory(PlayerFactory):
    def create_goalkeeper(self):
        return FootballGoalkeeper()

    def create_defender(self):
        return FootballDefender()


class HockeyPlayerFactory(PlayerFactory):
    def create_goalkeeper(self):
        return HockeyGoalkeeper()

    def create_defender(self):
        return HockeyDefender()

Football players

class FootballPlayer:
    def __init__(self, uses_hands):
        self.uses_hands = uses_hands

    def play(self):
        print("I'm playing football!")


class FootballGoalkeeper(FootballPlayer):
    def __init__(self):
        super(FootballGoalkeeper, self).__init__(uses_hands=True)


class FootballDefender(FootballPlayer):
    def __init__(self):
        super(FootballDefender, self).__init__(uses_hands=False)

Hockey players (my creativity stopped here, so I didn't include any difference between goalkeepers and defenders)

class HockeyPlayer:
    def play(self):
        print("I'm playing hockey!")


class HockeyGoalkeeper(HockeyPlayer):
    pass


class HockeyDefender(HockeyPlayer):
    pass

\$\endgroup\$
2
  • 2
    \$\begingroup\$ super(FootballGoalKeeper, self) can be replaced by super() \$\endgroup\$
    – hjpotter92
    Aug 17 '20 at 12:26
  • \$\begingroup\$ Thanks! Oh yes, you're right. I'm just too used to that old syntax. Thanks for pointing that out. @hjpotter92 \$\endgroup\$
    – revliscano
    Aug 17 '20 at 14:38
2
\$\begingroup\$

As your code presently stands, you don't need the derived Factory classes. They don't do anything different from each other, so they can all be handled by a concrete base class.

class PlayerFactory:

    def __init__(self, goal_keeper_class, defender_class):
        self._goal_keeper_class = goal_keeper_class
        self._defender_class = defender_class

    def create_goalkeeper(self):
        return self._goal_keeper_class()

    def create_defender(self):
        return self._defender_class()

player_factory = {
    "Football": PlayerFactory(FootballGoalkeeper, FootballDefender),
    "Hockey": PlayerFactory(HockeyGoalkeeper, HockeyDefender),
}

Example Usage:

>>> player = player_factory["Hockey"].create_defender()
>>> type(player)
<class '__main__.HockeyDefender'>
>>> player.play()
I'm playing hockey!
>>>

If there is some aspect of the factories which actually do something different, and thus necessitate separated derived classes, you'll need to include that in your question.

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for this, it's very useful. Hmm yes, your code makes so much more sense, but I was just trying to think in a way to use or make an example of Abstract Factory pattern. :-( I guess I'm still confused by the use cases of this pattern. \$\endgroup\$
    – revliscano
    Aug 17 '20 at 16:12
  • 2
    \$\begingroup\$ Well, if you added a "create_team" method, your various factories might return different numbers of players for the various positions. You could implement it with code in different derived classes, but it could be simply data-driven without needing derived classes. There is always more than one way to do it. The Abstract Factory pattern is a useful tool for your toolbox, but it isn't necessarily the right tool to use until your problem becomes significantly more complex. \$\endgroup\$
    – AJNeufeld
    Aug 17 '20 at 16:43
  • \$\begingroup\$ Invaluable information. This is the kind of clarification and inputs I was expecting to have. @AJNeufeld \$\endgroup\$
    – revliscano
    Aug 17 '20 at 16:53

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.