[PYTHON] Design Pattern #Builder

I practiced design patterns so that I could write code that was conscious of design. Other Design Patterns will be released frequently.

Preface

The primary goal is to understand when, what, and how to use design patterns. (I'm new to Java or a statically typed language, and I don't have a long history of python, so I think there are some things that aren't like Pythonista. If you have any suggestions, please teach me.)

This time, the creational pattern Builder.

What is Builder?

When the object creation procedure is complicated, the creation process is encapsulated and an instance with a structure is assembled. In addition, by preparing various pattern builders, it is possible to handle the production of various patterns, and even complicated method combinations can be executed collectively.

Overview

As a sample, create a program that creates "sentences" using the Builder pattern. The text created here has the following structure.

The Builder class defines the methods for constructing the document. The Director class then uses that method to create a concrete document,

The Builder class is an abstract class and the actual processing is not written. Only the abstract method is declared. It is the subclass of the Builder class that determines the specific processing for writing.

Director can use TextBuilder to create plain text documents, and HTMLBuilder can be used to create HTML documents.

Overall class diagram

builder.py


from abc import ABCMeta, abstractmethod


class Builder(metaclass=ABCMeta):

    @abstractmethod
    def make_title(self, title):
        pass

    @abstractmethod
    def make_string(self, string):
        pass

    @abstractmethod
    def make_items(self, items):
        pass

    @abstractmethod
    def close(self):
        pass

The Builder class is an abstract class that declares the methods that create "documents". make_title, make_string, and make_items are methods for constructing titles, strings, and bullets in sentences, respectively. The close method is the method that completes the document.

director.py


class Director():

    def __init__(self, builder):
        self.__builder = builder

    def construct(self):
        self.__builder.make_title('Greeting')
        self.__builder.make_string('From morning to noon')
        string = ['Good morning.', 'Hello.']
        self.__builder.make_items(string)
        self.__builder.make_string('At night')
        string = ['Good evening.', 'good night.', 'goodbye.']
        self.__builder.make_items(string)
        self.__builder.close()

In the Director class, the document is created using the methods declared in the Builder class.

The argument of the constructor of the Director class is a subclass (TextBuilder class or HTMLBuilder) type of the Builder class. The Builder class is an abstract class, so you can't instantiate it. The type of subclass of the given Builder class determines the specific document format created by the Director class.

The construct method is a method to create a document. Calling this method builds the document. The construct method uses the methods declared in the Builder.

text_builder.py


from builder import Builder


class TextBuilder(Builder):
    __buffer = []

    def make_title(self, title):
        self.__buffer.append('=' * 20)
        self.__buffer.append('[' + title + ']\n')
        self.__buffer.append('\n')

    def make_string(self, string):
        self.__buffer.append('■' + string + '\n')
        self.__buffer.append('\n')

    def make_items(self, items):
        for i in range(0, len(items)):
            self.__buffer.append('●' + items[i] + '\n')
        self.__buffer.append('\n')

    def close(self):
        self.__buffer.append('=' * 20)

    def get_result(self):
        for buffer in self.__buffer:
            print(buffer)

The TextBuilder class is a subclass of the Builder class. Build your document using plain text. The result is returned as a String.

html_builder.py


import logging
from builder import Builder


class HTMLBuilder(Builder):

    def make_title(self, title):
        self.__filename = title + '.html'

        try:
            self.__writer = open(self.__filename, mode='w')
        except IOError as e:
            logging.exception(e)

        self.__writer.write('<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>' + title + '</title></head><body>')
        self.__writer.write('<h1>' + title + '<h1>')

    def make_string(self, string):
        self.__writer.write('<h1>' + string + '</p>')

    def make_items(self, items):
        self.__writer.write('<ul>')
        for i in range(0, len(items)):
            self.__writer.write('<li>' + items[i] + '</li>')
        self.__writer.write('</ul>')

    def close(self):
        self.__writer.write('</body></html>')
        self.__writer.close()

    def get_result(self):
        return self.__filename

The HTMLBuilder class is also a subclass of the Builder class. The HTMLBuilder class builds a document as an HTML file. The constructed result is returned as the file name of the HTML file.

main.py


import sys
from text_builder import TextBuilder
from html_builder import HTMLBuilder
from director import Director


def usage():
    print('Usage: Python main plain')
    print('Usage: Python main html')


def main():
    if len(sys.argv) != 2:
        usage()
        sys.exit()

    if sys.argv[1] == 'plain':
        text_builder = TextBuilder()
        director = Director(text_builder)
        director.construct()
        result = text_builder.get_result()
        print(result)
    elif sys.argv[1] == 'html':
        html_builder = HTMLBuilder()
        director = Director(html_builder)
        director.construct()
        filename = html_builder.get_result()
        print(filename + 'Has been created.')
    else:
        usage()
        sys.exit()


if __name__ == "__main__":
    main()

The main file is a test program for the Builder pattern. Create a document according to the format specified on the command line as shown below.

python main.py plain ・ ・ ・ Create a document in plain text python main.py html ・ ・ ・ Document creation with HTML file

If you specify ** plain ** on the command line, you pass an instance of the TextBuilder class to the constructor of the Director class. Also, if you specify ** html ** on the command line, pass an instance of the HTMLBuilder class to the constructor of the Director class.

Both TextBuilder and HTMLBuilder are subclasses of Builder, and Director uses only Builder methods to create documents. Using only Builder methods means that ** Director is unaware of whether TextBuilder or HTMLBuilder is actually running **.

Therefore, the Builder must declare a set of methods necessary and sufficient to achieve the purpose of building the document. However, the Builder must not provide methods that are specific to plain text or HTML files.

Execution result (plain)

====================
[Greeting]

■ From morning to noon


● Good morning.

● Hello.


■ At night


● Good evening.

● Good night.

● Goodbye.
====================

Execution result (html)

`Greeting.html has been created. `` Greeting.png

Summary

A builder pattern specialized for reducing the instance creation work when the instance creation procedure and contents are complicated.

Regarding the procedure, the difference between the Template Method pattern and the Builder pattern is who is responsible for instantiation. The Template Method pattern determines the procedure for instantiation in a superclass. On the other hand, for the Builder pattern, the Director class (other class) is responsible for the generation procedure.

The key to the Builder pattern is who knows how to create an instance, that is, the Director class. It's also important that users of Director must know what the instance created by the Builder is.

It seems that the Builder pattern comes into play when there is a procedure for creating an instance, when you have to create an instance using an external resource, or when there are many different arguments in the constructor.

reference

Recommended Posts

Design Pattern #Builder
Design Pattern #Adapter
Design Pattern #Decorator
Design Pattern #Observer
Design Pattern #Facade
Design Pattern #Strategy
Design Pattern #Singleton
Design Pattern #Proxy
[Gang of Four] Design pattern learning --Builder
Design Pattern #Factory Method
Design Pattern #Template Method
I wrote a design pattern in kotlin Builder edition
Ore Ore Design Pattern: Glocal Variable
Python Design Pattern --Template method
[Gang of Four] Design pattern learning
GoF java design pattern rough summary
I studied about design patterns (personal memo) Part 3 (Prototype pattern, Builder pattern)
Learn the design pattern "Prototype" in Python
[Gang of Four] Design pattern learning --Singleton
[Gang of Four] Design Pattern Learning --Decorator
[Gang of Four] Design pattern learning --Visitor
Design pattern-Adapter
[Gang of Four] Design pattern learning --Mediator
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
[Gang of Four] Design pattern learning --Iterator
GoF design pattern from the problem 2. Structure
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
Design pattern-Iterator
[Gang of Four] Design pattern learning --Facade
[Gang of Four] Design pattern learning --Composite
[Gang of Four] Design pattern learning --Prototype
GoF design pattern from the problem 1. Generation
Learn the design pattern "Iterator" in Python
[Gang of Four] Design pattern learning --Memento
[Gang of Four] Design pattern learning --State
[Gang of Four] Design pattern learning --Interpreter
Learn the design pattern "Strategy" in Python
[Gang of Four] Design pattern learning --Bridge
Learn the design pattern "Composite" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
[Gang of Four] Design pattern learning --Proxy
[Gang of Four] Design pattern learning --Strategy
[Gang of Four] Design pattern learning --Adapter
Learn the design pattern "Facade" with Python
[Gang of Four] Design pattern learning --Observer
[Gang of Four] Design pattern learning --Command
GoF design pattern from the problem 3. Behavior
[Gang of Four] Design pattern learning --Fly Weight
[Gang of Four] Design pattern learning --Abstract Factory
Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Template Method" in Python
[Gang of Four] Design pattern learning --Factory Method