Learn the design pattern "Abstract Factory" in Python

As a material for learning GoF design patterns, the book "Introduction to Design Patterns Learned in the Augmented and Revised Java Language" seems to be helpful. However, since the examples taken up are based on JAVA, I tried the same practice in Python to deepen my understanding.

■ Abstract Factory

The Abstract Factory pattern is one of the design patterns defined by GoF (Gang of Four; 4 gangs). The purpose is to streamline the reuse of multiple modules by aggregating the APIs for generating related instances. In Japanese, it is often translated as "abstract factory". Also called Kit pattern

UML class and sequence diagram W3sDesign_Abstract_Factory_Design_Pattern_UML.jpg UML class diagram 68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3130333539352f62393731323034392d616664392d323330332d653539372d3031393136333530633732382e706e67.png (The above is quoted from Wikipedia)

■ Sample program of "Abstract Factory"

The Abstract Factory pattern seems to make an abstract product by combining abstract parts from ** abstract factory **. In fact, I would like to run the Python implementation code of the Abstract Factory and understand the image of the ** abstract factory **. The sample program taken up here creates a collection of links with a hierarchical structure as an HTML file.

--When operated in ListFactory mode, an HTML file of list-format links will be generated. --When operated in TableFactory mode, an HTML file of table-format links is generated.

(1) Try running ListFactory

First, let's run the code that creates the ** Link-based ** web page.

$ python Main.py ListFactory
[LinkPage.html] was created.

A file called LinkPage.html has been generated. When I checked the appearance with a web browser, it looked like this. listfactory.png

(2) Try running TableFactory

Next, let's run the code that creates a ** Table-based ** web page.

$ python Main.py TableFactory
[LinkPage.html] was created.

A file called LinkPage.html has been generated. When I checked the appearance with a web browser, it looked like this. tablefactory.png

■ Details of sample program

Similar code has been uploaded to the Git repository. https://github.com/ttsubo/study_of_design_pattern/tree/master/AbstractFactory

--Directory structure

.
├── Main.py
└── factory
    ├── __init__.py
    ├── factory.py
    ├── listfactory
    │   ├── __init__.py
    │   └── list_factory.py
    └── tablefactory
        ├── __init__.py
        └── table_factory.py

(1) The role of AbstractProduct

The ʻAbstractProduct role defines the interface of the abstract parts and products created by the ʻAbstractFactory role. In the sample program, the Link class, Tray class, and Page class serve this role.

factory/factory.py


import sys
from abc import ABCMeta, abstractmethod

... (snip)

class Item(metaclass=ABCMeta):
    def __init__(self, caption):
        self.caption = caption

    @abstractmethod
    def makeHtml(self):
        pass

class Link(Item, metaclass=ABCMeta):
    def __init__(self, caption, url):
        super().__init__(caption)
        self.url = url

class Tray(Item, metaclass=ABCMeta):
    def __init__(self, caption):
        super().__init__(caption)
        self.tray = []

    def add(self, item):
        self.tray.append(item)

class Page(metaclass=ABCMeta):
    def __init__(self, title, author):
        self.title = title
        self.author = author
        self.content = []

    def add(self, item):
        self.content.append(item)

    def output(self):
        try:
            filename = self.title + '.html'
            writer = open(filename, 'w')
            writer.write(self.makeHtml())
            writer.close()
            print("[" + filename + "]" + " was created.")
        except Exception as e:
            print(e)
            sys.exit(1)

    @abstractmethod
    def makeHtml(self):
        pass

(2) The role of Abstract Factory

The ʻAbstractFactory role defines the interface for creating an instance of the ʻAbstractProduct role. In the sample program, the Factory class serves this role.

factory/factory.py


import sys
from abc import ABCMeta, abstractmethod

class Factory(metaclass=ABCMeta):
    @abstractmethod
    def createLink(self, caption, url):
        pass

    @abstractmethod
    def createTray(self, caption):
        pass

    @abstractmethod
    def createPage(self, title, author):
        pass

... (snip)

(3) The role of Client

The Client role does its job using only the interfaces of the ʻAbstractFactory and ʻAbstractProduct roles. The Client role does not know about specific parts, products or factories. In the sample program, the startMain method serves this role.

Main.py


import sys
import inspect
import factory

def startMain(factoryObject):
    asahi = factoryObject.createLink("Asahi", "http://www.asahi.com")
    yomiuri = factoryObject.createLink("Yomiuri", "http://www.yomiuri.co.jp")
    us_yahoo = factoryObject.createLink("Yahoo", "http://www.yahoo.com")
    jp_yahoo = factoryObject.createLink("Yahoo!Japan", "http://www.yahoo.co.jp")
    google = factoryObject.createLink("Google", "http://www.google.com")
    excite = factoryObject.createLink("Excite", "http://www.excite.co.jp")

    traynews = factoryObject.createTray("Newspaper")
    traynews.add(asahi)
    traynews.add(yomiuri)

    trayyahoo = factoryObject.createTray("Yahoo!")
    trayyahoo.add(us_yahoo)
    trayyahoo.add(jp_yahoo)

    traysearch = factoryObject.createTray("Search Engine")
    traysearch.add(trayyahoo)
    traysearch.add(excite)
    traysearch.add(google)

    page = factoryObject.createPage("LinkPage", "Hiroshi Yuki")
    page.add(traynews)
    page.add(traysearch)
    page.output()

if __name__ == '__main__':
    for _, plugin in inspect.getmembers(factory, inspect.isclass):
        if plugin.__name__ == sys.argv[1]:
            startMain(plugin())

(4) The role of Concrete Product

The ConcreteProduct role implements the interface for the ʻAbstractProduct` role. In the sample program, the following classes serve this role:

--ListLink class, ListTray class, ListPage class --TableLink class, TableTray class, TablePage class

factory/listfactory/list_factory.py


from factory.factory import Factory, Link, Tray, Page

... (snip)

class ListLink(Link):
    def __init__(self, caption, url):
        super().__init__(caption, url)

    def makeHtml(self):
        return '  <li><a href="{}">{}</a></li>\n'.format(self.url, self.caption)

class ListTray(Tray):
    def __init__(self, caption):
        super().__init__(caption)

    def makeHtml(self):
        buf = []
        buf.append('<li>\n')
        buf.append(self.caption + '\n')
        buf.append('<ul>\n')

        for item in self.tray:
            buf.append(item.makeHtml())

        buf.append('</ul>\n')
        buf.append('</li>\n')
        return ''.join(buf)

class ListPage(Page):
    def __init__(self, title, author):
        super().__init__(title, author)

    def makeHtml(self):
        buf = []
        buf.append('''
<html>
  <head><title>{}</title></head>
'''.format(self.title))
        buf.append('<body>\n')
        buf.append('<h1>{}</h1>'.format(self.title))
        buf.append('<ul>')

        for item in self.content:
            buf.append(item.makeHtml())

        buf.append('</ul>')
        buf.append('<hr><adress>{}</adress>'.format(self.author))
        buf.append('</body>\n</html>\n')
        return ''.join(buf)

factory/tablefactory/table_factory.py


from factory.factory import Factory, Link, Tray, Page

... (snip)

class TableLink(Link):
    def __init__(self, caption, url):
        super().__init__(caption, url)

    def makeHtml(self):
        return '<td><a href={}>{}</a></td>'.format(self.url, self.caption)

class TableTray(Tray):
    def __init__(self, caption):
        super().__init__(caption)

    def makeHtml(self):
        buf = []
        buf.append('<td>')
        buf.append('<table width="100%" border="1"><tr>')
        buf.append('<td bgcolor="#cccccc" algin="center" colsapn="{}"><b>{}</b></td>'.format(len(self.tray), self.caption))
        buf.append('</tr>\n')
        buf.append('<tr>\n')

        for item in self.tray:
            buf.append(item.makeHtml())

        buf.append('</tr></table>')
        buf.append('</td>')
        return ''.join(buf)

class TablePage(Page):
    def __init__(self, title, author):
        super().__init__(title, author)

    def makeHtml(self):
        buf = []
        buf.append('''
<html>
  <head><title>{}</title></head>
    '''.format(self.title))
        buf.append('<body>\n')
        buf.append('<h1>{}</h1>'.format(self.title))
        buf.append('<table width="80%" border="3">\n')

        for item in self.content:
            buf.append('<tr>{}</tr>'.format(item.makeHtml()))

        buf.append('</table>')
        buf.append('<hr><adress>{}</adress>'.format(self.author))
        buf.append('</body>\n</html>\n')
        return ''.join(buf)

(5) The role of Concrete Factory

The ConcreteFactory role implements the interface for the ʻAbstractFactory` role. In the sample program, the following classes serve this role:

--ListFactory class --TableFactory class

factory/__init__.py


from factory.listfactory.list_factory import ListFactory
from factory.tablefactory.table_factory import TableFactory

__all__ = [
    "ListFactory",
    "TableFactory"   
]

factory/listfactory/list_factory.py


from factory.factory import Factory, Link, Tray, Page

class ListFactory(Factory):
    def createLink(self, caption, url):
        return ListLink(caption, url)

    def createTray(self, caption):
        return ListTray(caption)

    def createPage(self, title, author):
        return ListPage(title, author)

... (snip)

factory/tablefactory/table_factory.py


from factory.factory import Factory, Link, Tray, Page

class TableFactory(Factory):
    def createLink(self, caption, url):
        return TableLink(caption, url)

    def createTray(self, caption):
        return TableTray(caption)

    def createPage(self, title, author):
        return TablePage(title, author)

... (snip)

■ Reference URL

-[Finishing "Introduction to Design Patterns Learned in Java Language" (Not)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java Introduction to Design Patterns Learned in Language-Finishing-Not-2cc9b34a30b2) -Abstract Factory pattern from "diary of tachikawa844"

Recommended Posts

Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Factory Method" in Python
Learn the design pattern "Prototype" in Python
Learn the design pattern "Builder" in Python
Learn the design pattern "Flyweight" in Python
Learn the design pattern "Observer" in Python
Learn the design pattern "Memento" in Python
Learn the design pattern "Proxy" in Python
Learn the design pattern "Command" in Python
Learn the design pattern "Visitor" in Python
Learn the design pattern "Bridge" in Python
Learn the design pattern "Mediator" in Python
Learn the design pattern "Decorator" in Python
Learn the design pattern "Iterator" in Python
Learn the design pattern "Strategy" in Python
Learn the design pattern "Composite" in Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
Learn the design pattern "Template Method" in Python
Learn the design pattern "Chain of Responsibility" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "Facade" with Python
Implement the Singleton pattern in Python
Singleton pattern in Python
[Gang of Four] Design pattern learning --Abstract Factory
Design Pattern #Factory Method
Visitor pattern in Python
I wrote a design pattern in kotlin Factory edition
Download the file in Python
Find the difference in Python
Learn cumulative sum in Python
Design Patterns in Python: Introduction
Learn exploration in Python # 1 Full exploration
Python Design Pattern --Template method
Getting the arXiv API in Python
Python in the browser: Brython's recommendation
Save the binary file in Python
Hit the Sesami API in Python
Get the desktop path in Python
Get the script path in Python
In the python command python points to python3.8
Hit the web API in Python
I wrote the queue in Python
Calculate the previous month in Python
Examine the object's class in python
Get the desktop path in Python
Get the host name in Python
Access the Twitter API in Python
The first step in Python Matplotlib
I wrote the stack in Python
Master the weakref module in Python
Learn the basics of Python ① Beginners
Load the remote Python SDK in IntelliJ
Try using the Wunderlist API in Python
Check the behavior of destructor in Python
[Python Kivy] About changing the design theme
Try using the Kraken API in Python
Learn the basics while touching python Variables
Write the test in a python docstring
OR the List in Python (zip function)
GoF design pattern from the problem 2. Structure