Choose the right pattern
Choose the right pattern

OO Design Patterns made easyđź‘Ť

Shivaji Chelladurai
4 min readApr 6, 2021

--

Learning will be fun when we relate to real life situation. Smartphones are used by all age groups and introducing Design Patterns with reference to Smartphone features will help in relating and recalling them. Here I have listed only the patterns that may be used to build Smartphone features and few code generated by ChatGPT.

Creational Patterns Dual Sim (Abstract Factory), Themes (Builder), Connection(Factory Method), Camera (Singleton), Browser Tab (Prototype)

Structural Patterns Bluetooth (Adapter), Theme (Bridge), Review (Composite), Caller Tune (Decorator), Call (Facade), Existing Contact (Flyweight), Whatsapp Auto Download (Proxy)

Behavioral Patterns Markup (Command), Calculator (Interpreter), Iterator (Youtube), Merge Calls (Mediator), Undo (Memento), Rotate (Observer), Wifi Mode (State), Wifi (Strategy), Directions (Template), Amazon Cart (Visitor), Call Fwd (Chain of Responsibility)

Abstract Factory — Dual Sim

class MobilePhone:
def _init_(self, factory):
self.factory = factory

def make_call(self, number):
sim = self.factory.get_sim_card()
sim.make_call(number)

class SimCard:
def make_call(self, number):
pass

class SingleSimCard(SimCard):
def make_call(self, number):
print(f"Making call from Single SIM card to {number}")

class DualSimCard(SimCard):
def make_call(self, number):
print(f"Making call from Dual SIM card to {number}")

class SimCardFactory:
def get_sim_card(self):
pass

class SingleSimCardFactory(SimCardFactory):
def get_sim_card(self):
return SingleSimCard()

class DualSimCardFactory(SimCardFactory):
def get_sim_card(self):
return DualSimCard()

if _name_ == "_main_":
single_sim_factory = SingleSimCardFactory()
dual_sim_factory = DualSimCardFactory()

single_sim_phone = MobilePhone(single_sim_factory)
dual_sim_phone = MobilePhone(dual_sim_factory)

single_sim_phone.make_call("123456789")
dual_sim_phone.make_call("987654321")

Factory Method Pattern — Connection

import requests

class ConnectToInternet:
def _init_(self):
pass

def connect(self):
raise NotImplementedError("Subclass must implement this abstract method")

class ConnectToWifi(ConnectToInternet):
def _init_(self):
super()._init_()

def connect(self):
print("Connecting to Wi-Fi...")
# code to connect to Wi-Fi
print("Connected to Wi-Fi")

class ConnectToMobileData(ConnectToInternet):
def _init_(self):
super()._init_()

def connect(self):
print("Connecting to mobile data...")
# code to connect to mobile data
print("Connected to mobile data")

def get_internet_connection(connection_type):
if connection_type == "Wi-Fi":
return ConnectToWifi()
elif connection_type == "Mobile Data":
return ConnectToMobileData()
else:
raise ValueError("Invalid connection type")

# Example usage
connection_type = "Wi-Fi" # or "Mobile Data"
internet_connection = get_internet_connection(connection_type)
internet_connection.connect()

# Connect to the internet and fetch a web page
response = requests.get("https://www.example.com")
print(response.text)

Singleton — Camera

class Camera:
__instance = None

def __init__(self):
if Camera.__instance != None:
raise Exception("This class is a singleton. Use Camera.getInstance() to get the instance.")
else:
Camera.__instance = self

@staticmethod
def getInstance():
if Camera.__instance == None:
Camera()
return Camera.__instance

# Usage
camera1 = Camera.getInstance()
camera2 = Camera.getInstance()

print(camera1 is camera2) # True

Bridge Pattern — Theme

class ThemeImplementation:
def background_color(self):
pass

def font_color(self):
pass

class Light(ThemeImplementation):
def background_color(self):
return "#FFFFFF"

def font_color(self):
return "#000000"

class Dark(ThemeImplementation):
def background_color(self):
return "#000000"

def font_color(self):
return "#FFFFFF"

class Custom(ThemeImplementation):
def __init__(self, background_color, font_color):
self.background_color = background_color
self.font_color = font_color

def background_color(self):
return self.background_color

def font_color(self):
return self.font_color

class Theme:
def __init__(self, implementation):
self.implementation = implementation

def background_color(self):
return self.implementation.background_color()

def font_color(self):
return self.implementation.font_color()

# Usage
light = Theme(Light())
dark = Theme(Dark())
custom = Theme(Custom("#555555", "#EEEEEE"))

print("Light theme background color:", light.background_color()) # Light theme background color: #FFFFFF
print("Dark theme font color:", dark.font_color()) # Dark theme font color: #FFFFFF
print("Custom theme background color:", custom.background_color()) # Custom theme background color: #555555

Adapter Pattern — Bluetooth

class USB:
def connect_usb(self):
print("Connected to audio or video via USB")

class Bluetooth:
def connect_bluetooth(self):
print("Connected to audio or video via Bluetooth")

class Adapter:
def __init__(self, device):
self.device = device

def connect(self):
if isinstance(self.device, USB):
self.device.connect_usb()
elif isinstance(self.device, Bluetooth):
self.device.connect_bluetooth()

usb = USB()
bluetooth = Bluetooth()

adapter_usb = Adapter(usb)
adapter_usb.connect()

adapter_bluetooth = Adapter(bluetooth)
adapter_bluetooth.connect()

# Output
# Connected to audio or video via USB
# Connected to audio or video via Bluetooth

Decorator Pattern — Caller Tune

class PhoneCall:
def _init_(self):
self.duration = 0

def dial(self, number):
print(f"Calling {number}")
self.duration = 10 # 10 seconds

def end(self):
print("Ending call")

class CallerTune(PhoneCall):
def _init_(self, call):
self._call = call

def dial(self, number):
self._call.dial(number)
print("Playing caller tune")

def end(self):
self._call.end()

call = PhoneCall()
call = CallerTune(call)
call.dial("123-456-7890")
call.end()

# Output:
# Calling 123-456-7890
# Playing caller tune
# End

Mediator - Merge Calls

class CallMediator:
def __init__(self):
self._calls = []

def dial(self, call):
for c in self._calls:
if c.is_on_hold():
c.resume()
else:
c.hold()
self._calls.append(call)

def disconnect(self, call):
self._calls.remove(call)

def hold(self, call):
call.set_on_hold(True)

def resume(self, call):
call.set_on_hold(False)

def add_call(self, call1, call2):
call1.disconnect()
call2.disconnect()
new_call = Call(self, call1._number + " and " + call2._number)
self.dial(new_call)

def merge_call(self, call1, call2):
call2.disconnect()
call1._number += " and " + call2._number

State — Wifi

class NetworkState(object):
def connect(self):
pass

def disconnect(self):
pass

class WiFiConnectedState(NetworkState):
def connect(self):
print("The device is already connected to Wi-Fi.")

def disconnect(self):
print("Disconnecting from Wi-Fi.")
return NoConnectionState()

class CellularDataState(NetworkState):
def connect(self):
print("The device is already connected to cellular data.")

def disconnect(self):
print("Disconnecting from cellular data.")
return NoConnectionState()

class AirplaneModeState(NetworkState):
def connect(self):
print("The device is in airplane mode, cannot connect to any network.")

def disconnect(self):
print("The device is already in airplane mode.")

class NoConnectionState(NetworkState):
def connect(self):
print("Connecting to Wi-Fi.")
return WiFiConnectedState()

def disconnect(self):
print("The device is already disconnected.")

class MobilePhone(object):
def __init__(self):
self.state = NoConnectionState()

def connect(self):
self.state = self.state.connect()

def disconnect(self):
self.state = self.state.disconnect()

# Usage
mobile_phone = MobilePhone()
mobile_phone.connect()
mobile_phone.connect()
mobile_phone.disconnect()
mobile_phone.disconnect()

# output
# Connecting to Wi-Fi.
# The device is already connected to Wi-Fi.
# Disconnecting from Wi-Fi.
# The device is already disconnected.

Chain of Responsibility — Call Fwd

class CallHandler(object):
def __init__(self, handler=None):
self.handler = handler

def handle_call(self, call):
if self.handler:
self.handler.handle_call(call)
else:
print("No handler available for call from {}".format(call.number))

class CallForwarder(CallHandler):
def handle_call(self, call):
if call.type == "forwarded":
print("Forwarding call from {} to {}".format(call.number, call.forward_to))
else:
super().handle_call(call)

class JunkCallFilter(CallHandler):
def handle_call(self, call):
if call.number in self.junk_numbers:
print("Blocking junk call from {}".format(call.number))
else:
super().handle_call(call)

junk_numbers = ["111", "222", "333"]

class CallReceiver(CallHandler):
def handle_call(self, call):
print("Receiving call from {}".format(call.number))

class Call(object):
def __init__(self, number, type, forward_to=None):
self.number = number
self.type = type
self.forward_to = forward_to

# Usage
receiver = CallReceiver(CallForwarder(JunkCallFilter()))
receiver.handle_call(Call("123", "received"))
receiver.handle_call(Call("456", "forwarded", "789"))
receiver.handle_call(Call("111", "received"))
receiver.handle_call(Call("999", "received"))

# output
# Receiving call from 123
# Forwarding call from 456 to 789
# Blocking junk call from 111
# Receiving call from 999

--

--