avatarJordan P. Raychev

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

3251

Abstract

the customer to be aware of how this classes are created, we will create a common interface in the form of abstract class like so.</p><div id="9790"><pre><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod</pre></div><div id="2b37"><pre><span class="hljs-keyword">class</span> <span class="hljs-symbol">Ticket</span>(<span class="hljs-symbol">ABC</span>): @<span class="hljs-symbol">abstractmethod</span> <span class="hljs-symbol">def</span> <span class="hljs-symbol">ticket_type</span>(): <span class="hljs-symbol">pass</span></pre></div><p id="96db">Since we now have a common interface to adhere to, we need to inherit from it for our concrete classes.</p><div id="340f"><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-type">IncidentTicket</span>(<span class="hljs-type">Ticket</span>):... <span class="hljs-keyword">class</span> <span class="hljs-type">ProblemTicket</span>(<span class="hljs-type">Ticket</span>):...</span></pre></div><p id="5116">The next thing to do is create a <b>Factory class</b> (you may encounter it as Creator in other literature). The factory class will be responsible to check if the current ticket type is supported and if it is, we will need to return the desired object back to the user. Here is a rough example how we can achieve that.</p><div id="1cba"><pre><span class="hljs-keyword">class</span> <span class="hljs-symbol">TicketFactory: </span>@<span class="hljs-symbol">staticmethod</span> <span class="hljs-symbol">def</span> <span class="hljs-symbol">create_ticket</span>(<span class="hljs-symbol">t_type</span>):</pre></div><div id="a7d9"><pre> tickets = { <span class="hljs-symbol">'incident</span>' : <span class="hljs-type">IncidentTicket</span>, <span class="hljs-symbol">'problem</span>': ProblemTicket }</pre></div><div id="5210"><pre> assert t_<span class="hljs-keyword">type</span> in tickets, f'<span class="hljs-type">Ticket</span> <span class="hljs-keyword">type</span> "{t_type}" is not supported' return tickets[t_<span class="hljs-keyword">type</span>]</pre></div><p id="8aec">Our ticket factory defines a single static method called <b>create_ticket</b>, that is responsible of creating the actual class that the customer would like to instantiate. I’ve added an assert method to check if the ticket type actually exists (not very common, you may want to use <b>try, except</b> block instead).</p><p id="4a45">We now have to create the client side of our “application”.</p><div id="bc1f"><pre><span class="hljs-title">def</span> client_code(ticket_<span class="hljs-keyword">type</span>): factory = <span class="hljs-type">TicketFactory</span>() ticket = factory.create_ticket(ticket_<span class="hljs-keyword">type</span>) print(ticket.ticket_<span class="hljs-keyword">type</span>())</pre></div><div id="5a43"><pre><span class="hljs-variable"><span class="hljs-keyword">if</span></span> <span class="hljs-variable">name</span> == <span class="hljs-string">'main'</span>: <span class="hljs-function"><span class="hljs-title">client_code</span>(<span class="hljs-string">'incident'</span>)</span>

Options

<span class="hljs-function"><span class="hljs-title">client_code</span>(<span class="hljs-string">'problem'</span>)</span> <span class="hljs-function"><span class="hljs-title">client_code</span>(<span class="hljs-string">'servicerequest'</span>)</span></pre></div><p id="b667">The client side is pretty simple — it creates the factory and based on the provided ticket type returns a concrete ticket type. In our case the main part of the program returns two tickets <b>Incident and Problem</b> as shown below but fails to deliver a ticket of type <b>Service Request.</b></p><div id="7000"><pre><span class="hljs-meta prompt_">>>></span> <span class="language-python">IncidentTicket has been created</span> <span class="hljs-meta prompt_">>>></span> <span class="language-python">ProblemTicket has been created</span> <span class="hljs-meta prompt_">>>></span> <span class="language-python">AssertionError: Ticket <span class="hljs-built_in">type</span> <span class="hljs-string">"servicerequest"</span> <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> supported</span></pre></div><p id="b3fa">If we want to support service request type of tickets, we do not have to touch any of the client code but only create a new class and add it to our factory like so.</p><div id="28a5"><pre><span class="hljs-keyword">class</span> <span class="hljs-symbol">ServiceRequest</span>(<span class="hljs-symbol">Ticket</span>): <span class="hljs-symbol">def</span> <span class="hljs-symbol">ticket_type</span>(): <span class="hljs-symbol">return</span> <span class="hljs-symbol">f</span>'{class.name} has been created<span class="hljs-string">'</span></pre></div><div id="b972"><pre><span class="hljs-keyword">class</span> <span class="hljs-symbol">TicketFactory: </span>@<span class="hljs-symbol">staticmethod</span> <span class="hljs-symbol">def</span> <span class="hljs-symbol">create_ticket</span>(<span class="hljs-symbol">t_type</span>):</pre></div><div id="9f2a"><pre> tickets = { <span class="hljs-symbol">'incident</span>' : <span class="hljs-type">IncidentTicket</span>, <span class="hljs-symbol">'problem</span>': ProblemTicket, <span class="hljs-symbol">'servicerequest</span>' : <span class="hljs-type">ServiceRequest</span> }</pre></div><div id="e323"><pre> assert t_<span class="hljs-keyword">type</span> in tickets, f'<span class="hljs-type">Ticket</span> <span class="hljs-keyword">type</span> "{t_type}" is not supported' return tickets[t_<span class="hljs-keyword">type</span>]</pre></div><h2 id="4a5b">Conclusion</h2><p id="fa16">Factory methods eliminate the need to bind application-specific classes into your code. The code only deals with the abstract interface; therefore it can work with any user-defined concrete class (if it exists). As always, I hope this has been informative to you and I would like to thank you for reading!</p><p id="baa5">Note: If you like to grab the working example for this or previous articles of mine, you can check out my github page at <a href="https://github.com/jpraychev/tutorials">https://github.com/jpraychev/tutorials</a></p></article></body>

Factory design pattern in Python

Design patterns: Factory pattern

Hey folks and welcome to yet another article related to Python. In this series of articles we are going to take a look at several design patters (creational, behavioral, structural), what are they and how we can implement them in Python.

Photo by Lalit Kumar on Unsplash

The factory design pattern (FDP) falls under the category of creational design patterns along side abstract factory, builder, prototype and singleton. The FDP is based on a single function written to handle object creation. We execute it, passing a parameter that provides information about what we want, and, as a result, the wanted object is created. During this process we are not interested how the resulted object is implemented or where it is coming from.

Imagine that we are building a ticketing system and we would like our customers to be able to create different type of tickets. Since we are not aware what the customer would like to create (as a ticket type) we need to support it somehow. The factory method would allow us to create a common interface that the user will be using during ticket creation. Since we are in the beginning of the development process, we decide to support only couple ticket types: incident and problem, but we think to expand to other types in the future. That would not be a problem since the factory method would allow us to quickly define new concrete classes without the need to change customers code.

Firstly, lets define ticket class that we will be supporting.

class IncidentTicket:
    def ticket_type():
        return f'{__class__.__name__} has been created'
class ProblemTicket:
    def ticket_type():
        return f'{__class__.__name__} has been created'

As mentioned above, we will have incident and problem ticket types at first. The class itself is pretty simple, it defines a single ticket_type method that returns the name of the instantiated class. For example.

>>> inc = IncidentTicket()
>>> inc.ticket_type()
IncidentTicket has been created

Since we do not want the customer to be aware of how this classes are created, we will create a common interface in the form of abstract class like so.

from abc import ABC, abstractmethod
class Ticket(ABC):
    @abstractmethod
    def ticket_type():
        pass

Since we now have a common interface to adhere to, we need to inherit from it for our concrete classes.

class IncidentTicket(Ticket):...
class ProblemTicket(Ticket):...

The next thing to do is create a Factory class (you may encounter it as Creator in other literature). The factory class will be responsible to check if the current ticket type is supported and if it is, we will need to return the desired object back to the user. Here is a rough example how we can achieve that.

class TicketFactory:
    @staticmethod
    def create_ticket(t_type):
        tickets = {
            'incident' : IncidentTicket,
            'problem': ProblemTicket
        }
        assert t_type in tickets, f'Ticket type "{t_type}" is not supported'
        return tickets[t_type]

Our ticket factory defines a single static method called create_ticket, that is responsible of creating the actual class that the customer would like to instantiate. I’ve added an assert method to check if the ticket type actually exists (not very common, you may want to use try, except block instead).

We now have to create the client side of our “application”.

def client_code(ticket_type):
    factory = TicketFactory()
    ticket = factory.create_ticket(ticket_type)
    print(ticket.ticket_type())
if __name__ == '__main__':
    client_code('incident')
    client_code('problem')
    client_code('servicerequest')

The client side is pretty simple — it creates the factory and based on the provided ticket type returns a concrete ticket type. In our case the main part of the program returns two tickets Incident and Problem as shown below but fails to deliver a ticket of type Service Request.

>>> IncidentTicket has been created
>>> ProblemTicket has been created
>>> AssertionError: Ticket type "servicerequest" is not supported

If we want to support service request type of tickets, we do not have to touch any of the client code but only create a new class and add it to our factory like so.

class ServiceRequest(Ticket):
    def ticket_type():
        return f'{__class__.__name__} has been created'
class TicketFactory:
    @staticmethod
    def create_ticket(t_type):
        tickets = {
                'incident' : IncidentTicket,
                'problem': ProblemTicket,
                'servicerequest' : ServiceRequest
        }
        assert t_type in tickets, f'Ticket type "{t_type}" is not supported'
        return tickets[t_type]

Conclusion

Factory methods eliminate the need to bind application-specific classes into your code. The code only deals with the abstract interface; therefore it can work with any user-defined concrete class (if it exists). As always, I hope this has been informative to you and I would like to thank you for reading!

Note: If you like to grab the working example for this or previous articles of mine, you can check out my github page at https://github.com/jpraychev/tutorials

Python
Python3
Design Patterns
Factory Pattern
Recommended from ReadMedium