Design Pattern: Facade - Programmazione Avanzata

 
CONTINUE READING
02/12/20

                      Programmazione Avanzata
                           Design Pattern: Facade

                                   Programmazione Avanzata a.a. 2020-21
                                               A. De Bonis

45

     Il Design Pattern Facade
     • Il design pattern Facade è un design pattern strutturale che fornisce
       un’interfaccia semplificata per un sistema costituito da interfacce o classi
       troppo complesse o troppo di basso livello.
     • Esempio: La libreria standard di Python fornisce moduli per gestire file
       compressi gzip, tarballs e zip. Questi moduli hanno interfacce diverse.
     • Immaginiamo di voler accedere ai nomi di un file di archivio ed estrarre i
       suoi file usando un’interfaccia semplice.
     • Soluzione: Usiamo il design pattern Facade per fornire un’interfaccia
       semplice e uniforme che delega la maggior parte del vero lavoro alla
       libreria standard.

                                   Programmazione Avanzata a.a. 2020-21
                                               A. De Bonis

46

                                                                                            1
02/12/20

          Il Design Pattern Facade: un esempio
         60                                           Chapter 2. Structural Design Patterns in Python

                                                               Archive
                                                             filename
                                                             names()
                                                             unpack()

              60                                    Chapter 2. Structural Design Patterns in Python
                                       gzip           tarfile.TarFile               zipfile.ZipFile
                                      open()           getnames()
                                                           Archive                   namelist()
                                                       extractall()
                                                          filename                   extractall()
                                                       names()
                                               Figure 2.7 The Archive façade
                                                       unpack()
                                                       Programmazione Avanzata a.a. 2020-21
                                                                   A. De Bonis

         only when asked for the archive’s names or to unpack the archive will it actually
47
         open the archive file. (The code quoted in this section is from Unpack.py.)
                                        gzip        tarfile.TarFile           zipfile.ZipFile
          class Archive:              open()  getnames()          namelist()
                                              extractall()        extractall()
                def __init__(self, filename):
                    self._names = None   Figure 2.7 The Archive façade

          Il Design Pattern Facade: un esempio
                    self._unpack = None
              only when asked for=the
                    self._file         archive’s names or to unpack the archive will it actually
                                     None
              open the archive file. (The code quoted in this section is from Unpack.py.)
                    self.filename = filename
              class Archive:
         The self._names variable is expected to hold a callable that will return a list
                 def __init__(self,
         of the archive’s    names. filename):
                                       Similarly, the self._unpack variable is for holding a
                     self._names = None
         callable that will extract all the archive’s files into the current directory. The
                     self._unpack = None                                                                                         ptg11
                     is for holding
         self._file self._file        a file object that has been opened on the archive. And
                                 = None
         self.filename   is a read-write   property holding the archive file’s filename.
                     self.filename = filename

             The   self._names variable is expected to hold a callable that will return a list
               @property
     • La variabile
             of theself._names
                     archive’s ènames.
                                 usata perSimilarly,
                                           contenere un callable
                                                      the        che restituisce
                                                           self._unpack          una lista
                                                                            variable    is dei
                                                                                           fornomi dell’archivio.
                                                                                               holding   a
                 def filename(self):
              callable    that will
                       return         extract all the archive’s files into the current directory. The
                                  self.__filename
     • La variabile   the
              self._file   self._unpack  è usata
                             is for holding      per mantere
                                              a file          un callable
                                                     object that  has beenche estrae
                                                                              opened tutti
                                                                                        oni flie
                                                                                             thedell’archivio
                                                                                                 archive. And nella
       directory  corrente.
              self.filename is a read-write property holding the archive file’s filename.
                 @filename.setter
     • La variabile  self._file
                 def@property   è usata per mantenere
                       filename(self,         name): il file object che è stato aperto per accedere all’archivio.
                    defself.close()
                          filename(self):
     • self.filename è una proprietà che mantiene il nome del file di archivio.
                          return self.__filename
                       self.__filename         = name
                                                       Programmazione Avanzata a.a. 2020-21
                                                                   A. De Bonis
                    @filename.setter
         If the user changes the filename (e.g., using archive.filename = newname), the
48              def filename(self, name):
         current archive  file is closed (if it is open). We do not immediately open the new
                    self.close()
         archive, though,  since the= Archive
                    self.__filename    name      class uses lazy evaluation and so only opens
         the archive when necessary.
              If the user changes the filename (e.g., using archive.filename = newname), the
              current archive file is closed (if it is open). We do not immediately open the new                            2
              archive, though, since the Archive class uses lazy evaluation and so only opens
              the archive when necessary.
                                                     www.it-ebooks.info
open the archive file. (The code quoted in this section is from Unpack.py.)

                 class Archive:

                      def __init__(self, filename):
                          self._names = None                                                                                      02/12/20
                          self._unpack = None                                                                                                p
                          self._file = None
                          self.filename = filename

                The self._names variable is expected to hold a callable that will return a list
                of the archive’s names. Similarly, the self._unpack variable is for holding a
                callable that will extract all the archive’s files into the current directory. The

          Il Design Pattern Facade: un esempio
                self._file is for holding a file object that has been opened on the archive. And
                self.filename is a read-write property holding the archive file’s filename.

                      @property
                      def filename(self):
                          return self.__filename

                      @filename.setter
                      def filename(self, name):
                          self.close()
                          self.__filename = name

               If the user changes the filename (e.g., using archive.filename = newname), the
     Se l’utente  cambiaarchive
               current   il filename,  adclosed
                                  file is esempio(if archive.filename
                                                     it is open). We do = newname,    allora il open
                                                                           not immediately      file d’archivio
                                                                                                       the new
     corrente,archive,
                se aperto, viene chiuso
                        though,    since ethe
                                           viene  aggiornata
                                               Archive          la variabile
                                                         class uses          __filename.and so only opens
                                                                      lazy evaluation
     Non vienetheimmediatamente      aperto il nuovo archivio, in quanto la classe Archive apre l’archivio solo
                    archive when necessary.
     se necessario.

                                                        www.it-ebooks.info
                                                        Programmazione Avanzata a.a. 2020-21
                                                                     A. De Bonis

49

          Il Design        Pattern Facade: un esempio
                    2.5. Façade Pattern
              2.5. Façade Pattern                                                                                       61
                                                                                                                           61

                               def close(self):
                        def close(self):
                                      if self._file is not None:
                               if self._fileself._file.close()
                                                  is not None:
                                      self._names = self._unpack = self._file = None
                                     self._file.close()
                               self._names = self._unpack = self._file = None
                         In theory, users of the Archive class are expected to call the close() method when
                         theyusers
                 In theory,      have finished
                                        ofinvocano with an
                                            the Archive        instance.
                                                           class            The method
                                                                    are expected     to call closes  the filemethod
                                                                                                              object (if one is
     Gli utenti della  classe    Archive             close()     quando   hanno finito   contheun’istanza.
                                                                                                  close()
                         open) and sets the self._names, self._unpack, and self._file variables to None to
                                                                                                                      when
                 they have
     Il metodo chiude     il filefinished
                                  objectthem.with
                                          , se     anfile
                                               c’è un   instance.      The method
                                                           object aperto,      e setta closes   the file
                                                                                        self._names,      object (if one
                                                                                                        self._unpack,  e is
                         invalidate
                 open)    and    sets
     self._file a None per invalidarli.the   self._names    ,  self._unpack     , and self._file      variables   to None to
                         We have
                 invalidate           made the Archive class a context manager (as we will see in a moment)
                                  them.
     La classe Archive   so,
                          è unin context
                                  practice,   users don’t
                                           manager         need
                                                      eclass
                                                         così   in  to callgli
                                                                   pratica     utentithemselves,
                                                                           close()    non             providing   they use the
                 We have class made
                                  in a the
                                       withArchive
                                              statement.        a context
                                                              For   example:manager      (ashanno   bisogno
                                                                                              we will   see indiachiamare
                                                                                                                   moment)
     close(), a patto
                 so, inche    usino lausers
                         practice,      classedon’t
                                                in uno  statement
                                                     need              with, come
                                                              to call close()        nel codiceproviding
                                                                                  themselves,    qui in basso: they use the
                 class in    a with
                          with         statement. For example:
                                   Archive(zipFilename)         as archive:
                               print(archive.names())
                  with Archive(zipFilename)
                            archive.unpack() as archive:
                      print(archive.names())
                                                    Programmazione Avanzata a.a. 2020-21
                       Here we create an Archive for a zip
                      archive.unpack()                          A. Defile,
                                                                      Bonis print its names to the console, and then
                       extract all its files in the current directory. And because the archive is a context
50                     manager,
                 Here we            an Archive for aiszip
                           createarchive.close()          called
                                                             file, automatically
                                                                     print its names     when theconsole,
                                                                                          to the          goesthen
                                                                                                  archive and  out of
                       the with  statement’s    scope.
                 extract all its files in the current directory. And because the archive is a context
                 manager, archive.close() is called automatically when the archive goes out of
                            def __enter__(self):
                 the with statement’s  scope.
                                    return self

                        def __enter__(self):                                                                                            3
                             def __exit__(self, exc_type, exc_value, traceback):
                            returnself.close()
                                    self

                        def __exit__(self,
                         These two methodsexc_type,   exc_value,
                                           are sufficient to maketraceback):
                                                                  an Archive into a context manager.
open) and sets the self._names, self._unpack, and self._file variables to None to
                invalidate them.
                We have made the Archive class a context manager (as we will see in a moment)
      2.5. Façade
              so, inPattern                                                                 61the
                     practice, users don’t need to call close() themselves, providing they use
                class in a with statement. For example:                                                             02/12/20
            def close(self):
                with Archive(zipFilename) as archive:
                if self._file is not None:
                    print(archive.names())
                     self._file.close()
                    archive.unpack()
                self._names = self._unpack = self._file = None
                Here we create an Archive for a zip file, print its names to the console, and then
      In theory, usersall
              extract  of its
                          thefiles in theclass
                               Archive          are directory.
                                            current  expected to call
                                                               And    the close()
                                                                    because        method
                                                                            the archive   is awhen
                                                                                               context
         Il Design Pattern Facade: un esempio
      they have
      open) and
                 finished
              manager,
                 sets the
                            with  an  instance.
                         archive.close()
                           self._names
              the with statement’s scope. ,
                                             is    The
                                                called
                                            self._unpack
                                                        method
                                                          , and
                                                                 closes
                                                       automatically    the
                                                                      when
                                                                self._file
                                                                            file
                                                                            the  object
                                                                                 archive
                                                                            variables
                                                                                         (if
                                                                                        to
                                                                                             one
                                                                                          goes
                                                                                            None
                                                                                                  is of
                                                                                                out
                                                                                                 to
      invalidate them.
                      def __enter__(self):
      We have made the      Archive class a context manager (as we will see in a moment)
                         return self
      so, in practice, users don’t need to call close() themselves, providing they use the
      class in a with
                    defstatement.   For example:
                         __exit__(self, exc_type, exc_value, traceback):
                           self.close()
       with Archive(zipFilename) as archive:
            print(archive.names())
                 These two methods are sufficient to make an Archive into a context manager.
     • Questi   due
                 Themetodi   rendonomethod
                       __enter__()
            archive.unpack()           un Archivio   un context
                                                 returns           manager
                                                            self (an  Archive instance), which is assigned to
     • Il metodo    __enter__()   method    restituisce self (un’istanza
                 the with … as statement’s variable. The __exit__()       di Archive) che viene
                                                                                   method       assegnata
                                                                                             closes       alla
                                                                                                    the archive’s
        variabilefile
                  dello statement
                      object        with  ...as
      Here we create       an(ifArchive
                                  one is open),    andfile,
                                           for a zip     since   it (implicitly)
                                                             print   its names returns      None, any
                                                                                  to the console,     exceptions
                                                                                                    and   then
     • Il metodo    __exit__()
                 that  have    chiude il file
                              occurred   will object
                                                be    dell’archivionormally.
                                                   propagated       se c’è ne uno aperto..
      extract all its files in the current directory. And because the archive is a context
      manager, archive.close() is called automatically when the archive goes out of
                   def names(self):
      the with statement’s scope.
                       if self._file is None:
                           self._prepare()
            def __enter__(self):
                       return self._names()
                                         Programmazione Avanzata a.a. 2020-21
                 return self                                   A. De Bonis

51              This method returns a list of the archive’s filenames, opening the archive and
            def setting
                 __exit__(self,
                        self._names  exc_type, exc_value,
                                       and self._unpack     traceback):callables (using self._pre-
                                                        to appropriate
                 self.close()
                pare()) if it isn’t open already.

      These two methods are sufficient to make an Archive into a context manager.
      The __enter__() method returns self (an Archive instance), which is assigned to
      the with … as statement’s variable. The __exit__() method closes the archive’s
         Il Design Pattern Facade: un esempio
      file object (if one is open), and since www.it-ebooks.info
                                              it (implicitly) returns None, any exceptions
      that have occurred will be propagated normally.

            def names(self):
                if self._file is None:
                    self._prepare()
                return self._names()

      This method returns a list of the archive’s filenames, opening the archive and
     Questo
      settingmetodo    restituisce
              self._names          una lista deito
                             and self._unpack    nomi dei file dell’archivio
                                                   appropriate               aprendo
                                                                callables (using     l’archivio
                                                                                  self._pre-
      pare()) if it isn’t open already.
     (se non è già aperto) e ponendo in self._names e in self._unpack i callable
     appropriati utilizzando il metodo self.prepare().

                                              www.it-ebooks.info
                                                   Programmazione Avanzata a.a. 2020-21
                                                               A. De Bonis

52

                                                                                                                          4
02/12/20

          Il62Design Pattern Facade:
                               Chapter 2.un   esempio
                                          Structural Design Patterns in Python

                  def unpack(self):
                      if self._file is None:
                          self._prepare()
                      self._unpack()

           This method unpacks all the archive’s files, but as we will see, only if all of their
           Questo metodo spacchetta tutti i file di archivio ma solo se tutti i loro nomi sono
           names are “safe”.
           “safe”.
                def _prepare(self):
                    if self.filename.endswith((".tar.gz", ".tar.bz2", ".tar.xz",
                            ".zip")):
                        self._prepare_tarball_or_zip()
                    elif self.filename.endswith(".gz"):
                        self._prepare_gzip()
                                       Programmazione Avanzata a.a. 2020-21
                                                   A. De Bonis
                    else:
53           62         raise ValueError("unreadable:
                                     Chapter 2. Structural{}".format(self.filename))
                                                                       Design Patterns in Python

           This def
                 method    delegates the preparation to suitable methods. For tarballs and
                     unpack(self):
           zip files if
                     theself._file
                          necessaryis None:
                                       code is very similar, so they are prepared in the same
                         self._prepare()
           method. self._unpack()
                      But gzipped files are handled differently and so have their own sepa-
           rate method.
          IlThe
              Design
                preparation
             names          Pattern
                   are “safe”. methods must Facade:             un esempio
             This method unpacks all the archive’s files, but as we will see, only if all of their
                                                  assign callables      to the self._names and self._un-
           pack variables, so that these can be called in the names() and unpack() methods
                def _prepare(self):
           we have  just seen.
                        if self.filename.endswith((".tar.gz", ".tar.bz2", ".tar.xz",
                                 ".zip")):
                  def    _prepare_tarball_or_zip(self):
                             self._prepare_tarball_or_zip()
                         def self.filename.endswith(".gz"):
                        elif   safe_extractall():
                               unsafe = []
                             self._prepare_gzip()
                        else: for name in self.names():
                            raise ValueError("unreadable: {}".format(self.filename))
                                      if not self.is_safe(name):
           This method delegates the         unsafe.append(name)
                                                preparation to suitable methods. For tarballs and
   Questozip metodo
                files thedelega ifla preparazione
                                           code is ai
                                     unsafe:
                              necessary               metodi
                                                   very         adatti
                                                           similar,     soathey
                                                                              occuparsene,
                                                                                     are prepared in the same
   Per i tarball
           method. e i file
                         Butzipgzipped
                                il codice necessario
                                         files
                                      raise    are    è moltodifferently
                                                   handled
                                               ValueError("unsafesimile e per     and
                                                                                   toquesto   essi their
                                                                                         so have
                                                                                         unpack:   vengono
                                                                                                         ownpreparati
                                                                                                              sepa- dallo
                                                                                                     {}".format(unsafe))
   stesso rate
            metodo.
                  method.
                                self._file.extractall()                                                                             ptg11
   I file gzip
           The richiedono
                  preparation  unamethods
                                    gestione must
                                              diversa e per callables
                                                    assign   questo hanno   to theunself._names
                                                                                         metodo a parte.
                                                                                                     and self._un-
                          if self.filename.endswith(".zip"):
   I metodi    di preparazione      devono   assegnare  dei  callable
           pack variables, so that these can be called in the names() and alle  variabili   self._names
                                                                                                 unpack()emethods
                                                                                                           self._unpack in
   modo che       queste        self._file
                            possano   essere     = zipfile.ZipFile(self.filename)
                                              chimate  nei  metodi      names()       e  unpack().
           we have just seen.
                                self._names = self._file.namelist
                                                      Programmazione Avanzata a.a. 2020-21
                                self._unpack = safe_extractall
                  def _prepare_tarball_or_zip(self):              A. De Bonis

                          else:
                        def         # Ends with .tar.gz, .tar.bz2, or .tar.xz
                              safe_extractall():
54                            unsafe   = []= os.path.splitext(self.filename)[1]
                                suffix
                              for name in self.names():
                                self._file       = tarfile.open(self.filename, "r:" + suffix[1:])
                                    if not self.is_safe(name):
                                self._names       = self._file.getnames
                                        unsafe.append(name)
                              ifself._unpack
                                   unsafe:         = safe_extractall
                                raise ValueError("unsafe to unpack: {}".format(unsafe))                                            5
                            self._file.extractall()
                        if self.filename.endswith(".zip"):
                            self._file = zipfile.ZipFile(self.filename)
                            self._names = self._file.namelist
def _prepare(self):
                                                               if self.filename.endswith((".tar.gz", ".tar.bz2", ".tar.xz",
                                                                       ".zip")):
                                                                   self._prepare_tarball_or_zip()
                                                               elif self.filename.endswith(".gz"):                                    02/12/20
                                                                   self._prepare_gzip()
                                                               else:
                                                                   raise ValueError("unreadable: {}".format(self.filename))

                                                     This method delegates the preparation to suitable methods. For tarballs and
                                                     zip files the necessary code is very similar, so they are prepared in the same
                                                     method. But gzipped files are handled differently and so have their own sepa-
                                                     rate method.
                                                     The preparation methods must assign callables to the self._names and self._un-

          Il Design Pattern Facade: un esempio       pack variables, so that these can be called in the names() and unpack() methods
                                                     we have just seen.

                                                       def _prepare_tarball_or_zip(self):
•    Questo metodo comincia con il creare una              def safe_extractall():
     funzione innestata safe_extractall() che                  unsafe = []
     controlla tutti i nomi dell’archivio e lancia             for name in self.names():
                                                                    if not self.is_safe(name):
     ValueError se qualcuno di essi non è safe.
                                                                        unsafe.append(name)
                                                               if unsafe:
•    Se tutti i nomi sono safe viene invocato o   62                              Chapter 2. Structural
                                                                    raise ValueError("unsafe     to unpack:Design  Patterns in Python
                                                                                                            {}".format(unsafe))
     il metodo tarball.TarFile.extractall()                    self._file.extractall()
                                                           if self.filename.endswith(".zip"):
     oppure il metodo                                  def unpack(self):
                                                               self._fileis= None:
                                                                              zipfile.ZipFile(self.filename)
     zipfile.ZipFile.extractall().                         if self._file
                                                               self._names = self._file.namelist
                                                               self._prepare()
                                                               self._unpack = safe_extractall
                                                           self._unpack()
                                                           else: # Ends with .tar.gz, .tar.bz2, or .tar.xz
                                                               suffix = all
                                                  This method unpacks    os.path.splitext(self.filename)[1]
                                                                            the archive’s files, but as we will see, only if all of their
                                                               self._file = tarfile.open(self.filename, "r:" + suffix[1:])
                                                  names are “safe”.
                                                               self._names = self._file.getnames
                                                               self._unpack = safe_extractall
                                                       def _prepare(self):
                                                                if self.filename.endswith((".tar.gz", ".tar.bz2", ".tar.xz",
                                                                            ".zip")):
                                                      Programmazione Avanzata a.a. 2020-21
                                                                      self._prepare_tarball_or_zip()
                                                                  A. De Bonis
                                                                elif self.filename.endswith(".gz"):
                                                                                           www.it-ebooks.info
                                                                      self._prepare_gzip()
55                                                              else:
                                                                      raise ValueError("unreadable: {}".format(self.filename))

                                                  This method delegates the preparation to suitable methods. For tarballs and
                                                  zip files the necessary code is very similar, so they are prepared in the same
                                                  method. But gzipped files are handled differently and so have their own sepa-
                                                  rate method.

          Il Design Pattern Facade: un esempio
                                                  The preparation methods must assign callables to the self._names and self._un-
                                                  pack variables, so that these can be called in the names() and unpack() methods
                                                  we have just seen.

•    A seconda dell’estensione del nome                    def _prepare_tarball_or_zip(self):
                                                               def safe_extractall():
     dell’archivio, viene aperto un tarball.TarFile                unsafe = []
     o uno zipfile.ZipFile e assegnato a                           for name in self.names():
     self._file.                                                       if not self.is_safe(name):
•    self._names viene settata al metodo                                   unsafe.append(name)
                                                                   if unsafe:
     bound corrispondente (namelist() o
                                                                       raise ValueError("unsafe to unpack: {}".format(unsafe))
     getnames() )                                                  self._file.extractall()
•     self._unpack viene settata alla funzione                 if self.filename.endswith(".zip"):
     safe_extractall() appena creata. Questa                       self._file = zipfile.ZipFile(self.filename)
                                                                   self._names = self._file.namelist
     funzione è una chiusura che ha catturato
                                                                   self._unpack = safe_extractall
     self e quindi può accedere a self._file e                 else: # Ends with .tar.gz, .tar.bz2, or .tar.xz
     chiamare il metodo appropriato                                suffix = os.path.splitext(self.filename)[1]
     extractall()                                                  self._file = tarfile.open(self.filename, "r:" + suffix[1:])
                                                                   self._names = self._file.getnames
                                                                   self._unpack = safe_extractall

                                                      Programmazione Avanzata a.a. 2020-21
                                                                  A. De Bonis
                                                                                             www.it-ebooks.info
56

                                                                                                                                            6
assigned an object reference to the Form.update_ui() method that is bound to a
           particular instance of the form (self). A bound method can be called directly;
           for example, bound().
           An unbound method is a method with no associated instance. For example,                                         02/12/20
           if we write unbound = Form.update_ui, unbound is assigned an object reference
           to the Form.update_ui() method, but with no binding to any particular in-
           stance. This means that if we want to call the unbound method, we must
           provide a suitable instance as its first argument; for example, form = Form();
           unbound(form). (Strictly speaking, Python 3 doesn’t actually have unbound
           methods, so unbound is really the underlying function object, although this only
        Il Design Pattern Facade: un esempio
           makes a difference in some metaprogramming corner cases.)

              def is_safe(self, filename):
                  return not (filename.startswith(("/", "\\")) or
                      (len(filename) > 1 and filename[1] == ":" and
                       filename[0] in string.ascii_letter) or
                      re.search(r"[.][.][/\\]", filename))

     • AUnmaliciously          created
            file di archivio creato          archive
                                       in modo         filepotrebbe,
                                                 malizioso    that is una
                                                                        unpacked       could overwrite
                                                                            volta spacchettato,  sovrascrivereimportant
        importanti    file di sistema   rimpiazzandoli  con  file non funzionanti  o
       system files with nonfunctional or sinister replacements. In view of this, we pericolosi.
        In considerazione
     • should      never open  di ciò, archives
                                       non dovrebbero
                                                   thatmai    essere aperti
                                                           contain     filesarchivi
                                                                             withcontenenti
                                                                                     absolutefile   con path
                                                                                                  paths    or with rela-
        assoluti o che includono path relative ed evitare di aprire gli archivi con i privilegi di un utente
       tive path components, and we should always open archives as an unprivileged
        come root o Administrator.
     • user    (i.e.,
        is_safe()      never False
                   restituisce    as root     or Administrator).
                                        se il nome del file comincia con un forward slash o con un backslash
        (cioè un path assoluto) o contiene ../ o ..\ (cioè un path relativo che potrebbe condurre
       This method returns False if the filename it is given starts with a forward slash
        ovunque), oppure comincia con D: dove D indica un’unità disco di Windows.
       or a backslash (i.e., an absolute    path), or contains ../ or ..\ (a relative path
                                     Programmazione Avanzata a.a. 2020-21
       that could lead anywhere), or startsA. Dewith  Bonis
                                                               D: where D is a Windows drive letter.
57

      64                                           www.it-ebooks.info
                                                 Chapter 2. Structural Design Patterns in Python

      In other words, any filename that is absolute or that has relative components is
        Il Design Pattern Facade: un esempio
      considered to be unsafe. For any other filename the method returns True.

             def _prepare_gzip(self):
                 self._file = gzip.open(self.filename)
                 filename = self.filename[:-3]
                 self._names = lambda: [filename]
                 def extractall():
                     with open(filename, "wb") as file:
                         file.write(self._file.read())
                 self._unpack = extractall
     Questo metodo fornisce un object file aperto per self._file e assegna callable adatti a self._names e
     This method provides an open file object for self._file and assigns suitable
     self._unpack.
     La  funzione extractall(),
     callables                  legge e scrive
                   to self._names        and dati.
                                               self._unpack. For the extractall() function, we have
     Ilto
        pattern
          readFacade   permette
                 and write         di creare
                                 the  data interfacce
                                             ourselves. semplici e comode che ci permettono di ignorare i dettagli di
     basso livello. Uno svantaggio di questo design pattern potrebbe essere quello di non consentire un controllo
     più
     The  fine.
             Façade Pattern can be very useful for creating simplified and convenient in-
     Tutttavia,
     terfaces.  un facade non nasconte
                    The upside            o elimina
                                    is that   we arele funzionalità
                                                        insulated   delfrom
                                                                        sistema sottostantedetails;
                                                                              low-level     e così è possibile usare un
                                                                                                      the downside
     facade passando però a classi di più basso livello se abbiamo bisogno di un maggiore controllo.
      is that we may have to give up fine   control.
                                        Programmazione     However,
                                                       Avanzata a.a. 2020-21 a façade doesn’t hide or do
      away with the underlying functionality,A.so     De Bonis
                                                         we can always use the façade most of
58    the time, and just drop down to lower-level classes if we need more control.
      The Façade and Adapter Patterns have a superficial similarity. The difference
      is that a façade provides a simple interface on top of a complicated interface,
      whereas an adapter provides a standardized interface on top of another (not
      necessarily complicated) interface. Both patterns can be used together. For ex-                                            7
      ample, we might define an interface for handling archive files (tarballs, zip files,
      Windows .cab files, and so on), use an adapter for each format, and layer a façade
      on top so that users would not need to know or care about which particular file
02/12/20

     I context manager
     I context manager consentono di allocare e rilasciare risorse quando vogliamo
     L’esempio più usato di context manager è lo statement with.

                  with open('some_file’, 'w') as opened_file:
                          opened_file.write('Hola!')

      Questo codice apre il file, scrive alcuni dati in esso e lo chiude. Se si verifica
      un errore mentre si scrivono i dati, esso cerca di chiuderlo.
      Il codice in alto è equivalente a
                    file = open('some_file’, 'w’)
                    try:
                              file.write('Hola!’)
                    finally:
                              file.close()
                                              Programmazione Avanzata a.a. 2020-21
                                                          A. De Bonis

59

     I context manager
     • è possibile implementare un context manager con una classe.

                        class File:
                                  def __init__(self, file_name, method):
                                            self.file_obj = open(file_name, method)
                                  def __enter__(self):
                                           return self.file_obj
                                  def __exit__(self, type, value, traceback):
                                           self.file_obj.close()

                                              Programmazione Avanzata a.a. 2020-21
                                                          A. De Bonis

60

                                                                                                 8
02/12/20

     I context manager
     • è sufficiente definire __enter__() ed __exit__() per poter usare la
       classe File in uno statement with.

           with File('demo.txt', 'w') as opened_file:
                 opened_file.write('Hola!')

                                     Programmazione Avanzata a.a. 2020-21
                                                 A. De Bonis

61

     I context manager
     • Come funziona lo statement with:
        • Immagazzina il metodo __exit__() della classe File
        • Invoca il metodo __enter__() della classe File
            • Il metodo __enter__ restituisce il file object per il file aperto.
        • L’object file è passato a opened_file.
        • Dopo che è stato eseguito il blocco al suo interno, lo statement with invoca il
          metodo __exit__()
            • Il metodo __exit__() chiude il file

           with File('demo.txt', 'w') as opened_file:
                 opened_file.write('Hola!')

                                     Programmazione Avanzata a.a. 2020-21
                                                 A. De Bonis

62

                                                                                                  9
02/12/20

      I context manager
     • Se tra il momento in cui viene passato l’object file a opened_file e il momento in
       cui viene invocata __exit__, si verifica un’eccezione allora Python passa type,
       value e traceback dell’eccezione come argomenti a __exit__() per decidere come
       chiudere il file e se eseguire altri passi. In questo esempio gli argomenti di exit
       non influiscono sul suo comportamento.

                  with File('demo.txt', 'w') as opened_file:
                        opened_file.write('Hola!')

                                              Programmazione Avanzata a.a. 2020-21
                                                          A. De Bonis

63

      I context manager
     • Se il file object lanciasse un’eccezione, come nel caso in cui provassimo ad accedere ad un
       metodo non supportato dal file object:

               with File('demo.txt', 'w') as opened_file:
                       opened_file.undefined_function('Hola!')
     • with eseguirebbe i seguenti passi:
     1.   passerebbe type, value e traceback a __exit__()
     2.   permetterebbe a __exit__() di gestire l’eccezione
     3.   Se __exit__() restituisse True allora l’eccezione non verrebbe rilanciata dallo statement with.
     4.   Se __exit__() restituisse un valore diverso da True allora l’eccezione verrebbe lanciata dallo
          statement with

                                              Programmazione Avanzata a.a. 2020-21
                                                          A. De Bonis

64

                                                                                                                 10
02/12/20

      I context manager
             with File('demo.txt', 'w') as opened_file:
                     opened_file.undefined_function('Hola!')

     Nel nostro esempio, __exit__() restituisce (implicitamente) None per cui with lancerebbe
     l’eccezione:

               Traceback (most recent call last):
                     File "", line 2, in 
               AttributeError: ‘file’ object has no attribute 'undefined_function'

                                             Programmazione Avanzata a.a. 2020-21
                                                         A. De Bonis

65

      I context manager
     Il metodo __exit__() in basso invece gestisce l’eccezione:
         class File(object):
                  def __init__(self, file_name, method):
                         self.file_obj = open(file_name, method)
                  def __enter__(self):
                         return self.file_obj
                  def __exit__(self, type, value, traceback):
                         print("Exception has been handled")
                         self.file_obj.close()
                         return True

         with File('demo.txt', 'w') as opened_file:
                 opened_file.undefined_function()
                                             Programmazione Avanzata a.a. 2020-21
                                                         A. De Bonis

66

                                                                                                     11
02/12/20

     I context manager
     • è possibile implementare un context manager con un generatore utilizzando il
       modulo contextlib. Il decoratore Python contextmanager trasforma il generatore
       open_file in un oggetto GeneratorContextManager
           from contextlib import contextmanager
           @contextmanager
            def open_file(name):
                  f = open(name, 'w’)
                  yield f
                  f.close()

     • Si usa in questo modo
                      with open_file('some_file’) as f:
                              f.write('hola!')
                                         Programmazione Avanzata a.a. 2020-21
                                                     A. De Bonis

67

     I context manager
     • Nel punto in cui c’è yield il blocco nello statement with viene eseguito.
     • Il generatore riprende all’uscita del blocco.
     • Se nel blocco si verifica un’eccezione non gestita, essa viene rilanciata nel
       generatore nel punto dove si trova yield.
     • è possibile usare uno statement try...except...finally per catturare l’errore.
     • Se un’eccezione è catturata solo al fine di registrarla o per svolgere qualche
       azione (piuttosto che per sopprimerla), il generatore deve rilanciare l’eccezione.
       Altrimenti il generatore context manager indicherà allo statement with che
       l’eccezione è stata gestita e l’esecuzione riprenderà dallo statement che segue lo
       statement with.

                                         Programmazione Avanzata a.a. 2020-21
                                                     A. De Bonis

68

                                                                                                 12
02/12/20

      I context manager
     • Lo statement try...finally garantisce che il file venga chiuso anche nel caso si
       verifichi un’ eccezione nel blocco del with

           from contextlib import contextmanager
           @contextmanager
            def open_file(name):
                  f = open(name, 'w’)
                  try:
                           yield f
                  finally:
                           f.close()
                                       Programmazione Avanzata a.a. 2020-21
                                                   A. De Bonis

69

                        Programmazione Avanzata
                      Design Pattern: Factory Method

                                       Programmazione Avanzata a.a. 2020-21
                                                   A. De Bonis

70

                                                                                               13
02/12/20

     Factory Method Pattern
     • È un design pattern creazionale.
     • Si usa quando vogliamo definire un’interfaccia o una classe astratta
       per creare degli oggetti e delegare le sue sottoclassi a decidere quale
       classe istanziare quando viene richiesto un oggetto.
         • Particolarmente utile quando una classe non può conoscere in
           anticipo la classe degli oggetti che deve creare.

                                     Programmazione Avanzata a.a. 2020-21
                                                 A. De Bonis

71

     Factory Method Pattern: un’applicazione
     • Esempio: Consideriamo un framework per delle applicazioni ciascuna
       delle quali elabora documenti di diverso tipo.
        • Abbiamo bisogno di due astrazioni: la classe Application e la classe Document
            • La classe Application gestisce i documenti e li crea su richiesta dell’utente,
              ad esempio, quando l’utente seleziona Open o New dal menu.
        • Entrambe le classi sono astratte e occorre definire delle loro sottoclassi per
          poter realizzare le implementazioni relative a ciascuna applicazione
            • Ad esempio, per creare un’applicazione per disegnare, definiamo le classi
              DrawingApplication e DrawingDocument.
        • Definiamo un’interfaccia per creare un oggetto ma lasciamo alle sottoclassi
          decidere quali classi istanziare.

                                     Programmazione Avanzata a.a. 2020-21
                                                 A. De Bonis

72

                                                                                                    14
FACTORY METHOD                                                                    Class Creational02/12/20

     Intent
           Define an interface for creating an object, but let subclasses decide which class to
           instantiate. Factory Method lets a class defer instantiation to subclasses.
         Factory Method Pattern: un’applicazione
     Also Known As
         PoichéConstructor
      • Virtual   la particolare sottoclasse di Document da istanziare dipende
         dalla particolare applicazione, la classe Application non può fare
         previsioni riguardo alla sottoclasse di Document da istanziare
     Motivation
         La classe Application
      • Frameworks                sa solo
                        use abstract  classes quando
                                                   to define    deveand    essere  creato
                                                                              maintain     un nuovo between
                                                                                         relationships
         documento
        objects.        ma non is
                  A framework    neoften
                                     conosce        il tipo. for creating these objects as well.
                                           responsible
         Problema:
      • Consider       devono essere
                    a framework          istanziate delle
                                   for applications             that classi      ma si conoscono
                                                                          can present                solo
                                                                                        multiple documents    to
         delle  classi astratte che  non     possono            essere       istanziate
        the user. Two key abstractions in this framework are the classes Application and
         Il Factory method
      • Document.             pattern
                      Both classes       risolve questo
                                    are abstract,         and clients   problema
                                                                              have toincapsulando
                                                                                       subclass them to realize
         l’informazione
        their              riguardo implementations.
               application-specific   alla sottoclasse diToDocument                    da creare
                                                                             create a drawing      e
                                                                                                application, for
         sposta questa
        example,          informazione
                    we define the classes all’esterno
                                              DrawingApplication    del framework.and DrawingDocument. The
        Application class is responsible for managing Documents and will create them as
        required—when the user selects          Open         or New from a menu, for example.
                                        Programmazione Avanzata a.a. 2020-21
                                                    A. De Bonis

73      Because the particular Document subclass to instantiate is application-specific, the
        Application class can't predict the subclass of Document to instantiate—the Ap-
        plication class only knows when a new document should be created, not what kind
        of Document to create. This creates a dilemma: The framework must instantiate
        classes, but it only knows about abstract classes, which it cannot instantiate.
        The Factory Method pattern offers a solution. It encapsulates the knowledge
      Factory          Method
        of which Document
        framework.
                                      Pattern:
                                subclass      to create un’applicazione
                                                                 and moves this knowledge out of the

                                              Programmazione Avanzata a.a. 2020-21
                                                          A. De Bonis

74

                                                                                                                   15
02/12/20

     Factory Method Pattern: un’applicazione

       • Le sottoclassi di Application ridefiniscono il metodo astratto
         CreateDocument per restituire la sottoclasse appropriata di
         Document
       • Una volta istanziata, la sottoclasse di Application può creare istanze di
         Document per specifiche applicazioni senza dover conoscere le
         sottoclassi delle istanze create (CreateDocument)
       • CreateDocument è detto factory method perché è responsabile della
         creazione degli oggetti

                                  Programmazione Avanzata a.a. 2020-21
                                              A. De Bonis

75

     Factory Method Pattern: un semplice
     esempio
       class Pizza():
                def __init__(self):
                    self._price = None

                    def get_price(self):
                        return self._price

                                  Programmazione Avanzata a.a. 2020-21
                                              A. De Bonis

76

                                                                                          16
02/12/20

     Factory Method Pattern: un semplice
     esempio
          class HamAndMushroomPizza(Pizza):
                   def __init__(self):
                       self._price = 8.5

          class DeluxePizza(Pizza):
                   def __init__(self):
                       self._price = 10.5

          class HawaiianPizza(Pizza):
                   def __init__(self):
                       self._price = 11.5

                                    Programmazione Avanzata a.a. 2020-21
                                                A. De Bonis

77

     Factory Method Pattern: un semplice
     esempio
     • PizzaFactory fornisce il metodo createPizza che è statico per cui può essere
       invocato quando non è stata ancora creata una pizza
                class PizzaFactory:
                    @staticmethod
                    def create_pizza(pizza_type):
                        if pizza_type == 'HamMushroom':
                            return HamAndMushroomPizza()
                        elif pizza_type == 'Deluxé:
                            return DeluxePizza()
                        elif pizza_type == 'Hawaiian':
                            return HawaiianPizza()
                                    Programmazione Avanzata a.a. 2020-21
                                                A. De Bonis

78

                                                                                           17
02/12/20

         Factory Method Pattern: un semplice
         esempio
                if __name__ == '__main__':
                    for pizza_type in ('HamMushroom', 'Deluxé, 'Hawaiian'):
                       print('Price of {0} is {1}'.format(pizza_type,
                       PizzaFactory.create_pizza(pizza_type).get_price())

            •    il tipo di pizza avrebbe potuto essere fornito dall’utente
            •     il tipo di pizza indicato dall’utente potrebbe essere stato inserito successivamente nel
                 menu e la classe concreta corrispondente creata successivamente al main
            •    occorre modificare solo la factory

                                               Programmazione Avanzata a.a. 2020-21
                                                           A. De Bonis

79

         Factory Method Pattern: un semplice
         esempio
     •    Che cosa accade se vogliamo creare diversi tipi di negozi ciascuno dei quali vende
          pizze nello stile di una certà città
     •    Creiamo una classe astratta PizzaStore al cui interno c’è il metodo astratto
          create_pizza
     •    Dalla classe PizzaStore deriviamo NYPizzaStore, ChicagoPizzaStore e così via. Queste
          sottoclassi sovrascriveranno il metodo astratto. La decisione sul tipo di pizza da
          creare è presa dal metodo create_pizza della specifica sottoclasse.
          • analogamente a quanto accadeva nel framework per la gestione dei documenti
     •    PizzaStore avrà anche un metodo orderPizza() che invoca createPizza ma non ha
          idea su quale pizza verrà creata fino a che non verra` creata una classe concreta di
          PizzaStore

                                               Programmazione Avanzata a.a. 2020-21
                                                           A. De Bonis

80

                                                                                                                  18
02/12/20

     Factory Method Pattern: un semplice
     esempio
      from abc import ABC, abstractmethod

      class Pizza(ABC):

             @abstractmethod
             def prepare(self):
                    pass

             def bake(self):
                    print("baking pizza for 12min in 400 degrees..")

             def cut(self):
                    print("cutting pizza in pieces")

             def box(self):
                    print("putting pizza in box")
                                  Programmazione Avanzata a.a. 2020-21
                                              A. De Bonis

81

     Factory Method Pattern: un semplice
     esempio
      class NYStyleCheesePizza(Pizza):
             def prepare(self):
                    print("preparing a New York style cheese pizza..")

      class ChicagoStyleCheesePizza(Pizza):
             def prepare(self):
                    print("preparing a Chicago style cheese pizza..")

      class NYStyleGreekPizza(Pizza):
             def prepare(self):
                    print("preparing a New York style greek pizza..")

      class ChicagoStyleGreekPizza(Pizza):
             def prepare(self):
                    print("preparing a Chicago style greek pizza..")

                                  Programmazione Avanzata a.a. 2020-21
                                              A. De Bonis

82

                                                                              19
02/12/20

       Factory Method Pattern: un semplice
       esempio
     class PizzaStore(ABC):

           @abstractmethod
           def _createPizza(self, pizzaType: str) -> Pizza:
                  pass

           def orderPizza(self, pizzaType):

                  pizza = self._createPizza(pizzaType)

                  pizza.prepare()
                  pizza.bake()
                  pizza.cut()
                  pizza.box()
                                    Programmazione Avanzata a.a. 2020-21
                                                A. De Bonis

83

       Factory Method Pattern: un semplice
       esempio
       class NYPizzaStore(PizzaStore):

             def _createPizza(self, pizzaType: str) -> Pizza:

                    pizza = None

                    if pizzaType == 'Greek’:
                           pizza = NYStyleGreekPizza()
                    elif pizzaType == 'Cheese’:
                           pizza = NYStyleCheesePizza()
                    else:
                           print("No matching pizza found in the NY pizza store...")
                    return pizza

                                    Programmazione Avanzata a.a. 2020-21
                                                A. De Bonis

84

                                                                                            20
02/12/20

       Factory Method Pattern: un semplice
       esempio
     class ChicagoPizzaStore(PizzaStore):

           def _createPizza(self, pizzaType: str) -> Pizza:

                   pizza = None

                   if pizzaType == 'Greek’:
                          pizza = ChicagoStyleGreekPizza()
                   elif pizzaType == 'Cheese’:
                          pizza = ChicagoStyleCheesePizza()
                   else:
                          print("No matching pizza found in the Chicago pizza store...")
                   return pizza

                                        Programmazione Avanzata a.a. 2020-21
                                                    A. De Bonis

85

     18                              Chapter 1. Creational Design Patterns in Python
       Factory Method Pattern: un esempio
     We will begin by reviewing the top-level code that instantiates and prints the
     boards. Next, we will look at the board classes and some of the piece classes—
     Voglio creare
     starting withuna  scacchiera
                   hard-coded     per la dama
                                classes.  Then ed
                                                weuna
                                                   willper gli scacchi
                                                        review    some variations that
     allow us to avoid hard-coding classes and at the same time use fewer lines of
     code.

      def main():
          checkers = CheckersBoard()
          print(checkers)
          chess = ChessBoard()
          print(chess)

     This function is common to all versions of the program. It simply creates each
     type of board and prints it to the console, relying on the AbstractBoard’s __str__()
     method to convert the board’s internal representation into a string.
                                        Programmazione Avanzata a.a. 2020-21
                                                    A. De Bonis
      BLACK, WHITE = ("BLACK", "WHITE")
86    class AbstractBoard:
          def __init__(self, rows, columns):
              self.board = [[None for _ in range(columns)] for _ in range(rows)]
              self.populate_board()
                                                                                                 21
          def populate_board(self):
              raise NotImplementedError()
18                                     Chapter 1. Creational Design Patterns in Python
                                                                                                                               02/12/20
                               We will begin by reviewing the top-level code that instantiates and prints the
                               boards. Next, we will look at the board classes and some of the piece classes—
                               starting with hard-coded classes. Then we will review some variations that
                               allow us to avoid hard-coding classes and at the same time use fewer lines of
                               code.

                                def main():
                                    checkers = CheckersBoard()
                                    print(checkers)

            Factory Method Pattern: un esempio
                                    chess = ChessBoard()
                                    print(chess)
            • la scacchiera è una lista di liste (righe) di stringhe di un singolo carattere
            • __init__ Inizializza
                               Thisla scacchiera
                                      function is con  tutte le posizioni
                                                     common               vuote eof
                                                                to all versions   poithe
                                                                                      invoca populate_board
                                                                                         program.           per
                                                                                                    It simply   inserireeach
                                                                                                              creates    i
                               type of board and prints it to the console, relying on the AbstractBoard’s __str__()
              pezzi del gioco
                               method to convert the board’s internal representation into a string.
            • populate_board è astratto
                                BLACK, WHITE = ("BLACK", "WHITE")
     •   La funzione console()
                                 class AbstractBoard:
         restituisce una stringa
         che rappresenta il          def __init__(self, rows, columns):
         pezzo ricevuto in input         self.board = [[None for _ in range(columns)] for _ in range(rows)]
         sul colore di sfondo            self.populate_board()
         passato come
         secondo argomento.          def populate_board(self):
                                         raise NotImplementedError()

                                    def __str__(self):
                                        squares = []
                                        for y, row in enumerate(self.board):
                                            for x, piece in enumerate(row):
                                                square = console(piece, BLACK if (y + x) % 2 else WHITE)
                                                squares.append(square)
                                            squares.append("\n")
                                        return "".join(squares)
                                                   Programmazione Avanzata a.a. 2020-21
                                                                   A. De Bonis
                               The BLACK and WHITE constants are used here to indicate each square’s back-
87                             ground color. In later variants they are also used to indicate each piece’s color.
                               This class is quoted from gameboard1.py, but it is the same in all versions.
                          It would have been more conventional to specify the constants by writing: BLACK,
             1.3. Factory Method    Pattern
                          WHITE = range(2). However, using strings is much more helpful when it comes to
                                                                                                           19
                               debugging error messages, and should be just as fast as using integers thanks
                               to Python’s smart interning and identity checks.
             returns a string representing the given piece on the given background color. (On
                           The board is represented by a list of rows of single-character strings—or None for
             Unix-like systems   thissquares.
                           unoccupied   string includes     escape
                                                The console()         codes
                                                                 function (notto colorbut
                                                                               shown,  thein background.)
                                                                                             the source code),

            Factory
            We could haveMethod         Pattern:a formally
                           made the AbstractBoard        un esempio
                                                                 abstract class by giving it a
                                                                      www.it-ebooks.info
            metaclass of abc.ABCMeta (as we did for the AbstractFormBuilder class; 12 ➤). How-
             ever, here we have chosen to use a different approach, and simply raise a NotIm-
             • La classe per creare scacchiere per il gioco della dama
             plementedError exception for any methods we want subclasses to reimplement.

             class CheckersBoard(AbstractBoard):
                   def __init__(self):
                       super().__init__(10, 10)

                   def populate_board(self):
                       for x in range(0, 9, 2):
                           for row in range(4):
                               column = x + ((row + 1) % 2)
                               self.board[row][column] = BlackDraught()
                               self.board[row + 6][column] = WhiteDraught()
                                                       Programmazione Avanzata a.a. 2020-21
                                                    A. De Bonis
             This subclass is used to create a representation       of a 10 × 10 international
88           checkers board. This class’s populate_board() method is not a factory method,
             since it uses hard-coded classes; it is shown in this form as a step on the way to
             making it into a factory method.

             class ChessBoard(AbstractBoard):
                                                                                                                                    22
                   def __init__(self):
                       super().__init__(8, 8)
plementedError exception for any methods we want subclasses to reimplement.

            class CheckersBoard(AbstractBoard):

                def __init__(self):
                    super().__init__(10, 10)                                                                      02/12/20
                def populate_board(self):
                    for x in range(0, 9, 2):
                        for row in range(4):
                            column = x + ((row + 1) % 2)
                            self.board[row][column] = BlackDraught()
                            self.board[row + 6][column] = WhiteDraught()

     Factory     MethodThis class’s Pattern: un          esempio
       This subclass is used to create a representation of a 10 × 10 international
       checkers board.                             method  is not a factory method,
                                              populate_board()
           since it uses hard-coded classes; it is shown in this form as a step on the way to                                  ptg
     •   Lamaking
            classeitper
                      into ascacchiere    per il gioco degli scacchi
                             factory method.

            class Method
      1.3. Factory ChessBoard(AbstractBoard):
                         Pattern                                                       19

                 def __init__(self):
      returns a string representing the given piece on the given background color. (On
                      super().__init__(8,
      Unix-like systems                        8)
                         this string includes escape codes to color the background.)
      We could have made the AbstractBoard a formally abstract class by giving it a
                 def populate_board(self):
      metaclass of abc.ABCMeta (as we did for the AbstractFormBuilder class; 12 ➤). How-
                     self.board[0][0]
      ever, here we have                   = BlackChessRook()
                         chosen to use a different  approach, and simply raise a NotIm-
                     self.board[0][1]
      plementedError exception             = BlackChessKnight()
                                for any methods   we want subclasses to reimplement.
                     ...
       class CheckersBoard(AbstractBoard):
                     self.board[7][7] = WhiteChessRook()
                     for column in range(8):
           def __init__(self):
                          self.board[1][column]
               super().__init__(10,   10)              = BlackChessPawn()
                          self.board[6][column] = WhiteChessPawn()
           def populate_board(self):
           Thisfor x in range(0,
                 version         9, 2):
                         of the ChessBoard  ’s populate_board() method—just like the Checkers-
                   for row in range(4):       Programmazione
           Board’s one—is not a factory method,       but itAvanzata
                                                                doesa.a.illustrate
                                                                          2020-21  how the chess board is
                       column = x + ((row + 1) % 2)       A. De Bonis
           populated.self.board[row][column] = BlackDraught()
89                     self.board[row + 6][column] = WhiteDraught()
            class Piece(str):
      This subclass is used to create a representation of a 10 × 10 international
                 __slots__
      checkers board.        = () populate_board() method is not a factory method,
                        This class’s
      since it uses hard-coded classes; it is shown in this form as a step on the way to                    ptg11539634
      making it into a factory method.

      class ChessBoard(AbstractBoard):
                                               www.it-ebooks.info
     Factory Method Pattern: un esempio
           def __init__(self):
               super().__init__(8, 8)

     • La def
          classe    base per i pezzi
              populate_board(self):
               self.board[0][0] = BlackChessRook()
     • Si è scelto di creare una classe che discende da str invece che usare
               self.board[0][1] = BlackChessKnight()
               ...
       direttamente str per poter facilmente testare se un oggetto z è un
               self.board[7][7] = WhiteChessRook()
       pezzo del gioco con isinstance(z,Piece)
               for column in range(8):
                   self.board[1][column] = BlackChessPawn()
     • ponendo __slots__={} ci assicuriamo che gli oggetti di tipo Piece non
                   self.board[6][column] = WhiteChessPawn()

       abbiano
      This version ofvariabili    di’s populate_board()
                      the ChessBoard   istanza          method—just like the Checkers-
      Board’s one—is not a factory method, but it does illustrate how the chess board is
      populated.

      class Piece(str):

           __slots__ = ()

                                     www.it-ebooks.info
                                                Programmazione Avanzata a.a. 2020-21
                                                            A. De Bonis

90

                                                                                                                          23
02/12/20

     Factory Method Pattern: un esempio
     • La classe pedina nera e la classe re bianco
     • le classi20per gli altri pezzi sono create Chapterin1.modo   analogo
                                                              Creational Design Patterns in Python
         • Ognuna     di queste classi è una sottoclasse immutabile di Piece che è
                This class serves as a base class for pieces. We could have simply used str, but
            sottoclasse
                that woulddinotstrhave allowed us to determine if an object is a piece (e.g., using
         • Inizializzata conPiece)
                isinstance(x,     la stringa
                                       ). Usingdi un unico
                                                __slots__ = ()carattere   (ilinstances
                                                               ensures that   carattere  Unicode
                                                                                       have no data, che
                a topic we’ll discuss later on (§2.6, ➤ 65).
            rappresenta il pezzo)
                   class BlackDraught(Piece):

                        __slots__ = ()

                        def __new__(Class):
                            return super().__new__(Class, "\N{black draughts man}")
                   class WhiteChessKing(Piece):

                        __slots__ = ()
                        def __new__(Class):
                            return super().__new__(Class, "\N{white chess king}")
                                                    Programmazione Avanzata a.a. 2020-21
                                                         A. De Bonisused for all the piece classes. Every
                   These two classes are models for the pattern
                   one is an immutable Piece subclass (itself a str subclass) that is initialized
91                 with a one-character string holding the Unicode character that represents
                   the relevant piece. There are fourteen of these tiny subclasses in all, each one
                   differing only by its class name and the string it holds: clearly, it would be nice                                ptg11539
                   to eliminate all this near-duplication.

                     def populate_board(self):
                         for x in range(0, 9, 2):

     Factory Method Pattern: un esempio
                              for y in range(4):
                                  column = x + ((y + 1) % 2)
                                  for row, color in ((y, "black"), (y + 6, "white")):
     •   Notiamo che qui la stringa cheself.board[row][column]
                                         indica il pezzo è assegnata da __new__
                                                                = create_piece("draught",
     •   Il metodo __new__ non prende argomenti in quanto la stringa che rappresenta il pezzo è
              20
                                                color)
                                                   Chapter 1. Creational Design Patterns in Python
         codificato all’interno del metodo.
                  This new version of the CheckersBoard.populate_board() method (quoted from
         • TypeError:
              This  class__new__()
                          serves) as takes   1 positional   argument    but   2 were  given
                  gameboard2.py    is aa factory
                                          base class   for pieces.
                                                   method,           We
                                                              since it     could
                                                                       depends     have
                                                                                    on a simply  used str, butfac-
                                                                                         new create_piece()
     • Per i tipi tory
              thatche estendono
                    would   not have tipi immutable,
                       function rather  allowed    us  to  come str,classes.
                                                          determine
                                            than on hard-coded          l’inizializzazione
                                                                         if an   object
                                                                                   The is    è fatta(e.g.,
                                                                                           a piece
                                                                                        create_piece()da __new__.
                                                                                                           using
                                                                                                           function
              isinstance(x,
                  returns  an Piece)
                               object  ).
                                        ofUsing
                                           the   __slots__ =type
                                               appropriate       () ensures
                                                                      (e.g.,  a that
         • https://docs.python.org/3/reference/datamodel.html : __new__() is intendedinstances
                                                                                BlackDraught   orhave
                                                                                                  a    no  data,mainly
                                                                                                     WhiteDraught ), to allow
              a topic
            subclasses we’ll
                  depending  discuss
                               on its later
                         of immutable         on(like
                                       arguments.
                                          types  (§2.6, This
                                                      int,   65).
                                                          ➤str,
                                                              version
                                                                or tuple)oftothe  program
                                                                               customize    has a similar
                                                                                          instance  creation.Chess-
                  Board.populate_board() method (not shown), which also uses string color and
               class  BlackDraught(Piece):
                  piece names and the same create_piece() function.
                    __slots__ = ()
                   def create_piece(kind, color):
                    defif
                        __new__(Class):
                          kind == "draught":
                        return super().__new__(Class,
                           return                     "\N{black draughts
                                  eval("{}{}()".format(color.title(),    man}")
                                                                      kind.title()))
                      return eval("{}Chess{}()".format(color.title(), kind.title()))
               class WhiteChessKing(Piece):

                    __slots__ = ()
                    def __new__(Class):
                        return super().__new__(Class, "\N{white chess king}")
                                                www.it-ebooks.info
                                                    Programmazione Avanzata a.a. 2020-21
                                                                A. De Bonis
              These two classes are models for the pattern used for all the piece classes. Every
              one is an immutable Piece subclass (itself a str subclass) that is initialized
92            with a one-character string holding the Unicode character that represents
              the relevant piece. There are fourteen of these tiny subclasses in all, each one
              differing only by its class name and the string it holds: clearly, it would be nice                                   ptg11539634
              to eliminate all this near-duplication.

                    def populate_board(self):                                                                                         24
                        for x in range(0, 9, 2):
                            for y in range(4):
                                column = x + ((y + 1) % 2)
                                for row, color in ((y, "black"), (y + 6, "white")):
class BlackDraught(Piece):
            __slots__ = ()
                                                                                                                   02/12/20
            def __new__(Class):
                return super().__new__(Class, "\N{black draughts man}")
     class WhiteChessKing(Piece):

            __slots__ = ()
            def __new__(Class):
         Factory Method Pattern: un esempio
                return super().__new__(Class, "\N{white chess king}")
         • Questa nuova versione del metodo CheckersBoard.populate_board() è un
     These factory
              two    method in quanto dipende
               20 classes are models for
                                                      dalla
                                                 the pattern
                                               Chapter
                                                             factory
                                                                  usedfunction
                                                        1. Creational
                                                                                    create_piece()
                                                                        for allPatterns
                                                                        Design    the  piece    classes. Every
                                                                                          in Python
     one• is   an  immutable                subclass    (itself    a        subclass)
           Nella versione precedente il tipo di pezzo era indicato nel codice
                                   Piece                              str                 that    is initialized
     with a Thisone-character       string
                    class serves as a         holding
                                      base class          theWeUnicode
                                                 for pieces.      could have character
                                                                              simply used strthat
                                                                                                , butrepresents
         • La that
               funzione
                    would create_piece()
                           not have  allowedrestituisce
                                              us         un oggetto
                                                 to determine           del tipo
                                                                if an object is    appropriato
                                                                                a piece (e.g., in  (ad
                                                                                               using
     the relevant
           esempio,    piece.  There
                       BlackDraught      are  fourteen
                                        o WhiteDraught)    of  these   tiny
                                                             in basethat
                                                                       ai suoisubclasses
                                                                                argomenti.         all, each one
               isinstance(x, Piece)). Using  __slots__ = () ensures        instances  have no data,
     differing    onlywe’ll
                        by discuss
                            its class
                                    latername    and   the string it holds: clearly, it would be nice
         • Il metodo
               a topic   ChessBoard.populate_board()
                                          on (§2.6,
     to eliminate all this near-duplication.
                                                    ➤ 65).    viene anch’esso modificato in modo da
           usare   la stessa funzione    create_piece()
                class BlackDraught(Piece):
                                                           invocata    qui.
            def populate_board(self):
                   __slots__ = ()
                for      in range(0, 9, 2):
                   defx __new__(Class):
                      for   y in
                        return    range(4):
                               super().__new__(Class,     "\N{black draughts man}")
                            column = x + ((y + 1) % 2)
               class WhiteChessKing(Piece):
                            for
                   __slots__ = ()row, color in ((y, "black"), (y + 6, "white")):
                                 self.board[row][column] = create_piece("draught",
                   def __new__(Class):
                                           color)
                        return super().__new__(Class,     "\N{white
                                              Programmazione                chess king}")
                                                             Avanzata a.a. 2020-21
                                                           A. De Bonis

          These
93 This new       two classes
              version      of are
                               themodels  for the pattern used for all the piece classes.
                                    CheckersBoard.populate_board()                method   Every
                                                                                              (quoted from
          one is an immutable Piece subclass (itself a str subclass) that is initialized
   gameboard2.py) is a factory method, since it depends on a new create_piece() fac-
          with a one-character string holding the Unicode character that represents
   tory function    rather
          the relevant   piece.than
                                 Thereon
                                       arehard-coded       classes.
                                           fourteen of these            The create_piece()
                                                              tiny subclasses    in all, each one function
   returnsdiffering
           an objectonlyofbythe   appropriate
                             its class name and thetype   (e.g.,
                                                      string     a BlackDraught
                                                             it holds: clearly, it wouldorbe
                                                                                           a nice
                                                                                             WhiteDraught),                   ptg1
          to eliminate  all this near-duplication.
   depending on its arguments. This version of the program has a similar Chess-
   Board.populate_board()         method (not shown), which also uses string color and
         Factory Method Pattern: un esempio
                def populate_board(self):
   piece names and  for the
                         x in same    create_piece()
                               range(0, 9, 2):             function.
                        for y in range(4):

     def•  Questa funzione factory usa la funzione built-in eval() per creare
                            column = x + ((y + 1) % 2)
            create_piece(kind,   color):
                            for row, color in ((y, "black"), (y + 6, "white")):
           istanze della classe
            if kind == "draught":
                                self.board[row][column] = create_piece("draught",
                return eval("{}{}()".format(color.title(),
                                        color)                       kind.title()))
         • Ad esempio se gli argomenti sono "knight" and "black", la stringa
           return eval("{}Chess{}()".format(color.title(), kind.title()))
           valutata
              This newsarà   "BlackChessKnight()".
                        version   of the CheckersBoard.populate_board() method (quoted from
              gameboard2.py) is a factory method, since it depends on a new create_piece() fac-
         • In tory
              generale
                   functionèrather
                             meglio  thannon  usare eval
                                          on hard-coded      per eseguire
                                                          classes.              il codice
                                                                     The create_piece()  function
           rappresentato da un’espressione perché è potenzialmente
              returns an object  of the appropriate type (e.g., a BlackDraught or             rischioso
                                                                                  a WhiteDraught),
           dalBoard.populate_board()
                momento che permette              di eseguire il codice rappresentato da una
              depending on its arguments. This version of the program has a similar Chess-
                                        method (not shown), which also uses string color and
                                             www.it-ebooks.info
           qualsiasi   espressione
              piece names and the same create_piece() function.

               def create_piece(kind, color):
                   if kind == "draught":
                       return eval("{}{}()".format(color.title(), kind.title()))
                   return eval("{}Chess{}()".format(color.title(), kind.title()))

                                               Programmazione Avanzata a.a. 2020-21
                                                           A. De Bonis

94
                                              www.it-ebooks.info

                                                                                                                        25
You can also read