Advertisement
GeorgiLukanov87

8 Design Patterns Every Programmer Should Know

Mar 7th, 2025 (edited)
182
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.07 KB | None | 0 0
  1. # 8 Design Patterns Every Programmer Should Know
  2. # Creational Patterns
  3. =========================================================================================================
  4.  
  5. Factory
  6. class Burger:
  7.     def __init__(self, ingredients):
  8.         self.ingredients = ingredients
  9.  
  10.     def print(self):
  11.         print(self.ingredients)
  12.  
  13. class BurgerFactory:
  14.    
  15.     def createCheeseBurger(self):
  16.         ingredients = ["bun", "cheese", "beef-patty"]
  17.         return Burger(ingredients)
  18.    
  19.     def createDeluxeCheeseBurger(self):
  20.         ingredients = ["bun", "tomatoe", "lettuce", "cheese", "beef-patty"]
  21.         return Burger(ingredients)
  22.  
  23.     def createVeganBurger(self):
  24.         ingredients = ["bun", "special-sauce", "veggie-patty"]
  25.         return Burger(ingredients)
  26.  
  27. burgerFactory = BurgerFactory()
  28. burgerFactory.createCheeseBurger().print()
  29. burgerFactory.createDeluxeCheeseBurger().print()
  30. burgerFactory.createVeganBurger().print()
  31.  
  32. Output:
  33. ['bun', 'cheese', 'beef-patty'] ['bun', 'tomatoe', 'lettuce', 'cheese', 'beef-patty'] ['bun', 'special-sauce', 'veggie-patty']
  34. =========================================================================================================
  35.  
  36. Builder
  37. class Burger:
  38.     def __init__(self):
  39.         self.buns = None
  40.         self.patty = None
  41.         self.cheese = None
  42.  
  43.     def setBuns(self, bunStyle):
  44.         self.buns = bunStyle
  45.    
  46.     def setPatty(self, pattyStyle):
  47.         self.patty = pattyStyle
  48.    
  49.     def setCheese(self, cheeseStyle):
  50.         self.cheese = cheeseStyle
  51.  
  52. class BurgerBuilder:
  53.     def __init__(self):
  54.         self.burger = Burger()
  55.  
  56.     def addBuns(self, bunStyle):
  57.         self.burger.setBuns(bunStyle)
  58.         return self
  59.    
  60.     def addPatty(self, pattyStyle):
  61.         self.burger.setPatty(pattyStyle)
  62.         return self
  63.    
  64.     def addCheese(self, cheeseStyle):
  65.         self.burger.setCheese(cheeseStyle)
  66.         return self  
  67.  
  68.     def build(self):
  69.         return self.burger
  70.  
  71. burger = BurgerBuilder() \
  72.             .addBuns("sesame") \
  73.             .addPatty("fish-patty") \
  74.             .addCheese("swiss cheese") \
  75.             .build()
  76.  
  77. =========================================================================================================
  78.  
  79. Singleton
  80. class ApplicationState:
  81.     instance = None
  82.  
  83.     def __init__(self):
  84.         self.isLoggedIn = False
  85.  
  86.     @staticmethod
  87.     def getAppState():
  88.         if not ApplicationState.instance:  
  89.             ApplicationState.instance = ApplicationState()
  90.         return ApplicationState.instance
  91.  
  92. appState1 = ApplicationState.getAppState()
  93. print(appState1.isLoggedIn)
  94.  
  95. appState2 = ApplicationState.getAppState()
  96. appState1.isLoggedIn = True
  97.  
  98. print(appState1.isLoggedIn)
  99. print(appState2.isLoggedIn)
  100.  
  101. Output:
  102. False True True
  103.  
  104. =========================================================================================================
  105.  
  106. Behavioural Patterns
  107. """Observer / PubSub
  108. It's common for different components of an app to respond to events or state changes, but how can we communicate these events?
  109. The Observer pattern is a popular solution. We have a Subject (aka Publisher) which will be the source of events. And we could have multiple Observers (aka Subscribers) which will recieve events from the Subject in realtime."""
  110.  
  111. class YoutubeChannel:
  112.     def __init__(self, name):
  113.         self.name = name
  114.         self.subscribers = []
  115.  
  116.     def subscribe(self, sub):
  117.         self.subscribers.append(sub)
  118.    
  119.     def notify(self, event):
  120.         for sub in self.subscribers:
  121.             sub.sendNotification(self.name, event)
  122.  
  123. from abc import ABC, abstractmethod
  124.  
  125. class YoutubeSubscriber(ABC):
  126.     @abstractmethod
  127.     def sendNotification(self, event):
  128.         pass
  129.  
  130. class YoutubeUser(YoutubeSubscriber):
  131.     def __init__(self, name):
  132.         self.name = name
  133.    
  134.     def sendNotification(self, channel, event):
  135.         print(f"User {self.name} received notification from {channel}: {event}")
  136.  
  137. channel = YoutubeChannel("neetcode")
  138.  
  139. channel.subscribe(YoutubeUser("sub1"))
  140. channel.subscribe(YoutubeUser("sub2"))
  141. channel.subscribe(YoutubeUser("sub3"))
  142.  
  143. channel.notify("A new video released")
  144. """In this case we have multiple Subscribers listening to a single published. But users could also be subscribed to multiple channels.
  145. Since the Publishers & Subscribers don't have to worry about each others' implementations, they are loosely coupled."""
  146.  
  147. User sub1 received notification from neetcode: A new video released User sub2 received notification from neetcode: A new video released User sub3 received notification from neetcode: A new video released
  148.  
  149. =========================================================================================================
  150.  
  151. Iterator
  152. """Many objects in python have built-in iterators. That's why we can conveniently iterate through an array using the key word in."""
  153.  
  154. myList = [1, 2, 3]
  155. for n in myList:
  156.     print(n)
  157. Output:
  158. 1 2 3
  159.  
  160. """For more complex objects, like Linked Lists or Binary Search Trees, we can define our own iterators."""
  161.  
  162. class ListNode:
  163.     def __init__(self, val):
  164.         self.val = val
  165.         self.next = None
  166.  
  167. class LinkedList:
  168.     def __init__(self, head):
  169.         self.head = head
  170.         self.cur = None
  171.  
  172.     # Define Iterator
  173.     def __iter__(self):
  174.         self.cur = self.head
  175.         return self
  176.  
  177.     # Iterate
  178.     def __next__(self):
  179.         if self.cur:
  180.             val = self.cur.val
  181.             self.cur = self.cur.next
  182.             return val
  183.         else:
  184.             raise StopIteration
  185.  
  186. # Initialize LinkedList
  187. head = ListNode(1)
  188. head.next = ListNode(2)
  189. head.next.next = ListNode(3)
  190. myList = LinkedList(head)
  191.  
  192. # Iterate through LinkedList
  193. for n in myList:
  194.     print(n)
  195.    
  196. Output:
  197. 1 2 3
  198.  
  199. =========================================================================================================
  200.  
  201. Strategy
  202. """A Class may have different behaviour, or invoke a different method based on something we define (i.e. a Strategy). For example, we can filter an array by removing positive values; or we could filter it by removing all odd values. These are two filtering strategies we could implement, but we could add many more."""
  203.  
  204. from abc import ABC, abstractmethod
  205.  
  206. class FilterStrategy(ABC):
  207.  
  208.     @abstractmethod
  209.     def removeValue(self, val):
  210.         pass
  211.  
  212. class RemoveNegativeStrategy(FilterStrategy):
  213.  
  214.     def removeValue(self, val):
  215.         return val < 0
  216.  
  217. class RemoveOddStrategy(FilterStrategy):
  218.  
  219.     def removeValue(self, val):
  220.         return abs(val) % 2
  221.  
  222.  
  223. class Values:
  224.     def __init__(self, vals):
  225.         self.vals = vals
  226.    
  227.     def filter(self, strategy):
  228.         res = []
  229.         for n in self.vals:
  230.             if not strategy.removeValue(n):
  231.                 res.append(n)
  232.         return res
  233.  
  234. values = Values([-7, -4, -1, 0, 2, 6, 9])
  235.  
  236. print(values.filter(RemoveNegativeStrategy()))
  237. print(values.filter(RemoveOddStrategy()))
  238.  
  239. Output:
  240. [0, 2, 6, 9] [-4, 0, 2, 6]
  241.  
  242. A common alternative to this pattern is to simply pass in an inline / lambda function, which allows us to extend the behaviour of a method or class.
  243.  
  244. =========================================================================================================
  245.  
  246. Structural Patterns
  247. Facade
  248. """According to Oxford Languages, a Facade is
  249. an outward appearance that is maintained to conceal a less pleasant or creditable reality.
  250. In the programming world, the "outward appearance" is the class or interface we interact with as programmers. And the "less pleasant reality" is the complexity that is hidden from us.
  251. So a Facade, is simply a wrapper class that can be used to abstract lower-level details that we don't want to worry about."""
  252.  
  253. # Python arrays are dynamic by default, but this is an example of resizing.
  254. class Array:
  255.     def __init__(self):
  256.         self.capacity = 2
  257.         self.length = 0
  258.         self.arr = [0] * 2 # Array of capacity = 2
  259.  
  260.     # Insert n in the last position of the array
  261.     def pushback(self, n):
  262.         if self.length == self.capacity:
  263.             self.resize()
  264.            
  265.         # insert at next empty position
  266.         self.arr[self.length] = n
  267.         self.length += 1
  268.  
  269.     def resize(self):
  270.         # Create new array of double capacity
  271.         self.capacity = 2 * self.capacity
  272.         newArr = [0] * self.capacity
  273.        
  274.         # Copy elements to newArr
  275.         for i in range(self.length):
  276.             newArr[i] = self.arr[i]
  277.         self.arr = newArr
  278.        
  279.     # Remove the last element in the array
  280.     def popback(self):
  281.         if self.length > 0:
  282.             self.length -= 1
  283.            
  284. =========================================================================================================
  285.  
  286. Adapter
  287. """Adapters allow incompatible objects to be used together. Following the Open-Closed principle, we can extend class behaviour without modifying the class itself.
  288. If a MicroUsbCable class is initially incompatible with UsbPort, we can create a wrapper class (i.e. an Adapter), which makes them compatible. In this case, a MicroToUsbAdapter makes them compatible, similar to how we use adapters in the real-world."""
  289.  
  290. class UsbCable:
  291.     def __init__(self):
  292.         self.isPlugged = False
  293.    
  294.     def plugUsb(self):
  295.         self.isPlugged = True
  296.  
  297. class UsbPort:
  298.     def __init__(self):
  299.         self.portAvailable = True
  300.    
  301.     def plug(self, usb):
  302.         if self.portAvailable:
  303.             usb.plugUsb()
  304.             self.portAvailable = False
  305.  
  306. # UsbCables can plug directly into Usb ports
  307. usbCable = UsbCable()
  308. usbPort1 = UsbPort()
  309. usbPort1.plug(usbCable)
  310.  
  311. class MicroUsbCable:
  312.     def __init__(self):
  313.         self.isPlugged = False
  314.    
  315.     def plugMicroUsb(self):
  316.         self.isPlugged = True
  317.  
  318. class MicroToUsbAdapter(UsbCable):
  319.     def __init__(self, microUsbCable):
  320.         self.microUsbCable = microUsbCable
  321.         self.microUsbCable.plugMicroUsb()
  322.  
  323.     # can override UsbCable.plugUsb() if needed
  324.  
  325. # MicroUsbCables can plug into Usb ports via an adapter
  326. microToUsbAdapter = MicroToUsbAdapter(MicroUsbCable())
  327. usbPort2 = UsbPort()
  328. usbPort2.plug(microToUsbAdapter)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement