domingo, 9 de septiembre de 2018

Design Patterns: Builder


Hello again!

In my last post, I was showing you a basic example on how to implement and use the "Abstract Factory" pattern.

This time, it is Builder's patterns turn. I will describe the intent, and a simple use case where it naturally fits well (I hope).

Intent:

Separate the construction of a complex object from its representation so that the same construction process can create different representations.

As an example is best than tones of lines, lets create a requirement:

"We need to create a small representation on how Ice creams will be built, and who is going to deal with the specifics of how to create/handle ice creams"

The code that uses the builder pattern to solve the requirement would look like this:


import abc


class InvalidIceCreamException(Exception):
pass


class IceCreamChashier:

def __init__(self):
self._ice_cream_builder = None

def construct(self, builder):
self._ice_cream_builder = builder
self._ice_cream_builder.build_type()
self._ice_cream_builder.build_flavors()
class IceCreamBuilder(metaclass=abc.ABCMeta):

def __init__(self):
self._ice_cream = IceCream()

@abc.abstractmethod
def build_type(self):
pass

@abc.abstractmethod
def build_flavors(self):
pass

@property
def ice_cream(self):
return self._ice_cream

@property
def ice_type(self):
return self.ice_cream.inner_type

@property
def flavors(self):
return self.ice_cream.list_flavors()



class IceCream(object):
GELATO = 'gelato'
CONE = 'cone'
SORBET = 'sorbet'
DONDURMA = 'dondurma'

TYPES = [GELATO, CONE, SORBET, DONDURMA]

def __init__(self):
self._type = None
self._flavors = set()

@property
def inner_type(self):
return self._type

def list_flavors(self):
return self._flavors

def set_type(self, ice_type):
if ice_type not in self.TYPES:
raise InvalidIceCreamException("This is an invalid icecream!")
self._type = ice_type

def set_flavor(self, flavor):
self._flavors.add(flavor)


class VanillaGelatoIceCreamBuilder(IceCreamBuilder):
def build_type(self):
self._ice_cream.set_type(IceCream.GELATO)

def build_flavors(self):
self._ice_cream.set_flavor('vanilla')
self._ice_cream.set_flavor('chocolate') #added just because... yes! Internal politics.
def main():
vanillaBuilder = VanillaGelatoIceCreamBuilder()
cashier = IceCreamChashier()
cashier.construct(vanillaBuilder)
print('Type of ice cream: {}'.format(vanillaBuilder.ice_type))
print('Flavors of ice cream: {}'.format(vanillaBuilder.flavors))


if __name__ == "__main__":
main()


So we can see that the complex object (IceCream) is hidden in the IceCreamBuilder abstraction, and we provide a small API to access to its main data, which is the ice cream type, and the flavors needed

Thats it!!




No hay comentarios: