The appearance model and the flyweight pattern | Python theme on 8/30

Writing in the front

This article is participating in Python Theme Month. See the link for details

This month is Python Activity month, and I decided to try out the daily and random questions in Python for 30 days. Then if I have the energy for the weekend, I’d like to play around with this Python-Patterns

Design mode for me is more learning than my personal experience summary, so I am likely to understand the partial, if there is a boss to see, please timely point out that I choose to write some personal things in Nuggets because of the higher quality of the article here, I do not hope that the later to see these articles are misleading.

The appearance model

The Facade pattern is a way to provide a simpler unified interface to more complex systems. It provides an easier way to access the functionality of the underlying system by providing a single entry point. This abstraction can be seen in many situations in real life. For example, we can turn on a computer with the push of a button, but in reality there are many programs and operations (such as loading programs from disk into memory) when this happens. In this case, the button acts as a unified interface to all the basic programs that start the computer.

Here’s an example:


# Complex computer parts
class CPU:
    """ Simple CPU representation. """

    def freeze(self) :
        print("Freezing processor.")

    def jump(self, position) :
        print("Jumping to:", position)

    def execute(self) :
        print("Executing.")


class Memory:
    """ Simple memory representation. """

    def load(self, position, data) :
        print(f"Loading from {position} data: '{data}'.")


class SolidStateDrive:
    """ Simple solid state drive representation. """

    def read(self, lba, size) :
        return f"Some data from sector {lba} with size {size}"


class ComputerFacade:
    """ Represents a facade for various computer parts. """

    def __init__(self) :
        self.cpu = CPU()
        self.memory = Memory()
        self.ssd = SolidStateDrive()

    def start(self) :
        self.cpu.freeze()
        self.memory.load("0x00", self.ssd.read("100"."1024"))
        self.cpu.jump("0x00")
        self.cpu.execute()


def main() :
    """ >>> computer_facade = ComputerFacade() >>> computer_facade.start() Freezing processor. Loading from 0x00 data: 'Some data from sector 100 with size 1024'. Jumping to: 0x00 Executing. """


if __name__ == "__main__":
    import doctest

    doctest.testmod(optionflags=doctest.ELLIPSIS)

Copy the code

We see this pattern in the Python standard library when we use the isdir function. Although the user only uses this function to know if a path refers to a directory, the system does something and calls other modules (such as os.stat) to give the result.

The flyweight pattern

The purpose of the share mode is to minimize the number of objects the program needs at runtime. Flyweight is an object shared by multiple contexts, no different from an object that is not shared. The state of a Flyweight should not be affected by its context, which is called its inherent state. Decouple the state of the object from its context, allowing the Flyweight to be shared.


import weakref


class Card:
    """The Flyweight"""

    # Could be a simple dict.
    # With WeakValueDictionary garbage collection can reclaim the object
    # when there are no other references to it.
    _pool = weakref.WeakValueDictionary()

    def __new__(cls, value, suit) :
        # If the object exists in the pool - just return it
        obj = cls._pool.get(value + suit)
        # otherwise - create new one (and add it to the pool)
        if obj is None:
            obj = object.__new__(Card)
            cls._pool[value + suit] = obj
            # This row does the part we usually see in `__init__`
            obj.value, obj.suit = value, suit
        return obj

    # If you uncomment `__init__` and comment-out `__new__` -
    # Card becomes normal (non-flyweight).
    # def __init__(self, value, suit):
    # self.value, self.suit = value, suit

    def __repr__(self) :
        return f"<Card: {self.value}{self.suit}>"


def main() :
    """ >>> c1 = Card('9', 'h') >>> c2 = Card('9', 'h') >>> c1, c2 (
      
       , 
       
        ) >>> c1 == c2 True >>> c1 is c2 True >>> c1.new_attr = 'temp' >>> c3 = Card('9', 'h') >>> hasattr(c3, 'new_attr') True >>> Card._pool.clear() >>> c4 = Card('9', 'h') >>> hasattr(c4, 'new_attr') False """
       
      


if __name__ == "__main__":
    import doctest

    doctest.testmod()

Copy the code

The example sets up an “object pool” to store initialized objects. When a “card” is created, it first checks to see if it already exists, rather than creating a new one. The goal is to reduce the number of objects initialized by the program. At least here you can see that the design pattern is not conceptually clear-cut, and that the object pool pattern is used unknowingly.

summary

The facade pattern provides a simpler unified interface for complex systems. The meta pattern minimizes memory usage by sharing data with other similar objects.

reference

  • Sourcemaking.com/design_patt…
  • Fkromer. Making. IO/python – patt…
  • Python – 3 – patterns – idioms – test. Readthedocs. IO/en/latest/C…
  • Codesnipers.com/?q=python-f…
  • Python – patterns. Guide/gang – of – fou…
  • Examples from Python ecology