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!!




viernes, 24 de agosto de 2018

Design Patterns: Abstract Factory

Hey there,

Long time without writing. I would like to embrace this skill again, so my plan for it is to create a list of daily or weekly articles about design patterns and cool CS stuffs.

So lets get started.

So far we know that we have 23 popular design patterns, designed to solve well known problems in systems.

So my goal will be to analyze them 1 by 1, providing examples on how to use them. For this purpose, I will use Python, which is a language I am getting used to, and strongly recommend to learn and apply, as it is capable of incredible stuffs

OK now seriously. Lets get Started.


Creational Pattern

As the book defines it, Creational design patterns "abstract" the instantiation process. It means that the system will be independent of how their objects are created.

Pattern 1: Abstract Factory.

This one is a bit tricky. But I will read, analyze, create notes so I can get this depicted and simplify for you. Hold on. (12:41 am)


after half an hour of reading, I think the right approach to this is to explain the problem that it intends to solve.


So I am a big soccer fan (HALA Madrid!) and I want to have a way to represent different cups played in the world (I will be focusing on Europe for obvious reasons).

The concern of the abstract factory is to instantiate a group of related or dependent objects, based on a common criteria.

For our case we will define the Following requirement: A cup will contain a title, and a list of clubs that will compete against each other to win it.

So lets first design how the abstract factory for cups will look like:

class KitCup(object):

__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def createTitle(self):
pass
@abc.abstractmethod
def createFavoriteClub(self):
pass

@abc.abstractmethod
def createCountry(self):
pass


KitCup will represent an abstract class, containing all the methods that creates concrete classes + sprecial functions just for printing purposes.

Now let see how everything looks like:


import abc

class Club(object):
__metaclass__ = abc.ABCMeta
NAME = '' #Should be define by Subclasses

def get_name(self):
return self.NAME

class RealMadrid(Club):
NAME = 'Real Madrid FC'

class Juventus(Club):
NAME = 'Juventus FC'

class Bayern(Club):
NAME = ' FC Bayern Munchen'


class KitCup(object):

__metaclass__ = abc.ABCMeta

@abc.abstractmethod
def createTitle(self):
pass
@abc.abstractmethod
def createFavoriteClub(self):
pass

@abc.abstractmethod
def createCountry(self):
pass
class SpainCup(KitCup):

def createCountry(self):
return 'Spain'

def createTitle(self):
return 'La liga'
def createFavoriteClub(self):
return RealMadrid()

class ItalyCup(KitCup):

def createCountry(self):
return 'Italy'

def createTitle(self):
return 'Calccio'
def createFavoriteClub(self):
return Juventus()
class GermanyCup(KitCup):

def createCountry(self):
return 'Germany'

def createTitle(self):
return 'BundesLiga'
def createFavoriteClub(self):
return Bayern()


def main():

leagues = [SpainCup, ItalyCup, GermanyCup]

print('********** Summary of favorites per league **********')
for league in leagues:
print('Country: {}'.format(league().createCountry()))
print('League Name: {}'.format(league().createTitle()))
print('Favorite Club: {}'.format(league().createFavoriteClub().get_name()))
print('<---------- ----------="" next="">')

if __name__ == '__main__':
main()


There is a lot of benefits:

  1. We defer the instantiation of concrete clubs to only when the iteration is achieved (see the for loop in the main class)
  2. We are grouping together as dependent concept things such as favorite club, name of the league and country where this cup belongs
  3. See how easy it is to scale, for instance concepts such as champions league, or world cup can be easily implemented (with small tweaks of course )
In conclusion, the main objective of the Abstract Factory is to group the creation of common concepts, leaving the concrete classes the tsk of instantiating them.

Hope you like it, see you soon!