Saturday, September 2, 2023

Iterator Design Pattern in Python...

Iterator design pattern is a behavioral design pattern that lets you access the element of a complex data structure - a collection class - without the knowledge of the internal structure of the elements it is accessing.

To create an iterator in Python, there are two abstract classes from the built-in `collections` module - Iterable, and Iterator. We need to implement the

`__iter__()` method in the iterated object (collection), and the `__next__ ()` method in the iterator.

The concrete class that implements the Iterator class provides various means of different ways of iteration through the collection class.

In our example, AlphabeticalOrderIterator is such a class that has provided forward and reverse ways of iteration. In this example, the WordsCollection class represents a collection of words and implements the Iterable interface.

The source code is as follows:

from collections.abc import Iterator, Iterable
from typing import Any, List

class WordsCollection(Iterable):
def __init__(self, collection: List[Any]=[]):
self._collection = collection

def __iter__(self):
return AlphabeticalOrderIterator(self._collection)

def get_reverse_iterator(self):
return AlphabeticalOrderIterator(self._collection, True)

def add_item(self, item:Any):
self._collection.append(item)

class AlphabeticalOrderIterator(Iterator):
_position = None
_reverse = False

def __init__(self, collection : WordsCollection, reverse: bool = False):
self._collection = collection
self._reverse = reverse
self._position = -1 if self._reverse else 0

def __next__(self):
try:
value = self._collection[self._position]
self._position += -1 if self._reverse else 1
except IndexError:
raise StopIteration()

return value


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
wordsCollection = WordsCollection()
wordsCollection.add_item("Som")
wordsCollection.add_item("Reema")
wordsCollection.add_item("Ridit")

print("Forward traversal:")
print("\n".join(wordsCollection))
print("")

print("Reverse traversal:")
print("\n".join(wordsCollection.get_reverse_iterator()), end="")

If we run the above program the output will be as follows:

Forward traversal:

Som

Reema

Ridit


Reverse traversal:

Ridit

Reema

Som

 

Friday, September 1, 2023

Prototype Design Pattern in Python...

The prototype design pattern is a creational pattern.

If the creation of an object is a very costly affair and we already have a similar kind of object, instead of creating it each time we need it, we use the clone of the object that is already there.

Suppose, there is a large object with many attributes whose data we read from a database. We need to modify that data multiple times in our program. So instead of creating the object each time by reading from the database, we tend to keep a prototype object - we clone it, and then work on it.

In Python, there is a module called copy which has two methods - copy() and deepcopy(). The first one is for the shallow copy and the second one is for the deepcopy.

Here is the source code of an example of the Prototype design pattern.

from abc import ABC, abstractmethod
import copy

class Person(ABC):
def __init__(self, name):
self._name = name
def set_name(self, name):
self._name = name
def get_name(self):
return self._name
@abstractmethod
def clone(self):
pass
@abstractmethod
def display(self):
pass

class Teacher(Person):
def __init__(self, name, course):
super().__init__(name)
self._course = course
def set_course(self, course):
self._course = course
def get_course(self):
return self._course
def display(self):
print("\nTeacher's name : ", self._name, "\n")
print("\nHe teaches : ", self.get_course())

def clone(self):
return copy.deepcopy(self)

class Student(Person):
def __init__(self, name, teacher):
super().__init__(name)
self._teacher = teacher

def display(self):
print("\n Student's name : ", self.get_name())
print("\n Taught by : ", self._teacher.get_name())
print("\n Enrolled in the subject : ", self._teacher.get_course())

def clone(self):
return copy.deepcopy(self)



if __name__ == '__main__':
teacher = Teacher('Som', "Python Design Patttern")
teacherClone1 = teacher.clone()

student = Student ("Ridit", teacherClone1)

studentClone1 = student.clone()

teacherClone1.set_course("DSA")

teacherClone1.display()

studentClone1.display()



If we run the above piece of code the result will be as follows:

Teacher's name :  Som 

He teaches :  Python Design Patttern

Teacher's name :  Som 

He teaches :  DSA

Student's name :  Ridit

Taught by :  Som

Enrolled in the subject :   Python Design Patttern


As you can see, we have used deep copy here.

Sunday, August 27, 2023

Decorator Design Pattern in Python

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.

What problems can it solve?

- Responsibilities should be added to (and removed from) an object dynamically at run-time

- A flexible alternative to subclassing for extending functionality should be provided.

The UML diagram of the example is given below:



When using subclassing, different subclasses extend a class in different ways. But an extension is bound to the class at compile-time and can't be changed at run-time.

The decorator pattern can be used to extend (decorate) the functionality of a certain object statically, or in some cases at run-time, independently of other instances of the same class.

This is achieved by designing a new Decorator class that wraps the original class. This wrapping could be achieved by the following sequence of steps:

Subclass the original Component class into a Decorator class (see UML diagram);

- In the Decorator class (SpecialistTeacher), add a Component reference as an attribute;

- In the Decorator class, pass a Component to the Decorator constructor to initialize the Component attribute;

- In the Decorator class, forward all Component methods to the Component pointer; and

- In the ConcreteDecorator class (specialistMathTeacher, specialistChemistryTeacher, etc), override any Component method(s) whose behavior needs to be modified.

This pattern is designed so that multiple decorators can be stacked on top of each other, each time adding a new functionality to the overridden method(s).

Note that decorators and the original class object share a common set of features. In the UML diagram, the doWork() and calculateSalary() methods were available in both the decorated and undecorated versions.

Here is the source code of the Decorator Design Pattern in Python:

from abc import ABC, abstractmethod

class Teacher(ABC):
@abstractmethod
def calculateSalary(self):
pass
def doWork(self):
pass

class BasicTeacher(Teacher):
def __init__(self, baseSalaryForATeacher):
self.baseSalaryForTeacher = baseSalaryForATeacher

def calculateSalary(self):
return self.baseSalaryForTeacher
def doWork(self):
pass

##Decorator
class SpecialistTeacher(Teacher):
def __init__(self, teacher):
self.teacher = teacher

def calculateSalary(self):
self.specialistTeacherBaseSalary = self.teacher.calculateSalary()
return self.specialistTeacherBaseSalary
def doWork(self):
super().doWork()
self.teacher.doWork()

class SpecialistPhysicsTeacher(SpecialistTeacher):
def __init__(self, specialistTeacher):
super().__init__(specialistTeacher)

def calculateSalary(self):
return super().calculateSalary() + 8000

def doWork(self):
super().doWork()
print("\n I am a specialist Physics Teacher. I teach Physics")


class SpecialistMathsTeacher(SpecialistTeacher):
def __init__(self, specialistTeacher):
super().__init__(specialistTeacher)

def calculateSalary(self):
return super().calculateSalary() + 10000

def doWork(self):
super().doWork()
print("\n I am a specialist Maths Teacher. I teach Maths")


class SpecialistChemistryTeacher(SpecialistTeacher):
def __init__(self, specialistTeacher):
super().__init__(specialistTeacher)

def calculateSalary(self):
return super().calculateSalary() + 7000

def doWork(self):
super().doWork()
print("\n I am a specialist Chemistry Teacher. I teach Chemistry")

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
specialistPhyTeacher = SpecialistPhysicsTeacher(SpecialistTeacher(BasicTeacher(10000)))

specialistPhyTeacher.doWork()

print("\n My Salary is ", specialistPhyTeacher.calculateSalary())

specialistphymathsTeacher = SpecialistMathsTeacher(SpecialistPhysicsTeacher
                                          (SpecialistTeacher(BasicTeacher(10000))))

specialistphymathsTeacher.doWork()

print("\n My Salary is ", specialistphymathsTeacher.calculateSalary())

If we run the above program, the output will look like the following:


I am a specialist Physics Teacher. I teach Physics


My Salary is  18000


 I am a specialist Physics Teacher. I teach Physics


 I am a specialist Maths Teacher. I teach Maths


 My Salary is  28000


Friday, August 25, 2023

The Template Method Design Pattern...

 It is a behavioral design pattern. It lets us define the skeleton of an algorithm, delegating some steps to the subclasses.

There are mainly two participants in this design pattern

- AbstractClass : It defines the abstract primitive methods that the subclasses will override. It also implements a template method which define the algorithm.

- ConcreteClass : it implements the abstract primitive methods of the Abstract Class.

Let us try to understand this design pattern from an example.

Suppose we are modeling the daily activity of an engineering student in college.

All students need to sign in and sign out in a common register. So these common tasks are part of the Base class.

However, each student has to attend the classes of his own stream - maybe Electronics, Computer Science, Mechanicals, etc.

So this part of the basic activity depends on the stream of the student and constitutes part of the Concrete class.

The UML diagram of the code is as follows:



Here is the source code of the Template Method Design Pattern

from abc import ABC, abstractmethod

class EngineeringStudent(ABC):
def __init__(self):
pass
def signIn(self):
print("\n Start of the day. I need to sign in the common register")
def signOut(self):
print("\n End of the day. I need to sign out in the common register")
@abstractmethod
def attendClass(self):
pass
def activityofAStudent(self):
self.signIn()
self.attendClass()
self.signOut()

class ElectronicsStudent(EngineeringStudent):

def __init__(self):
print("\n I am an Electronics Student")

def attendClass(self):
print("\n Attend the classes in the Electronics dept")

class ComputerScienceStudent(EngineeringStudent):
def __init__(self):
print("\n I am a CS Student")
def attendClass(self):
print("\n Attend the classes in the CS dept")


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
electronicsStudent = ElectronicsStudent()
electronicsStudent.activityofAStudent()
csStudent = ComputerScienceStudent()
csStudent.activityofAStudent()

# See PyCharm help at https://www.jetbrains.com/help/pycharm/

If we run the above program, the output will be as follows:


 I am an Electronics Student


 Start of the day. I need to  sign in the common register


 Attend the classes in the Electronics dept


 End of the day. I need to sign out in the common register


 I am a CS Student


 Start of the day. I need to  sign in the common register


 Attend the classes in the CS dept


 End of the day. I need to sign out in the common register


Thursday, August 24, 2023

Memento Design Pattern in Python...

This design pattern lets you save the state of an object without revealing the implementation.

There are mainly three participants in this design pattern

- Memento: stores the internal state of the Originator object. How much state of the Originator the memento will store completely depends upon the Originator

- Originator: It creates a memento containing a snapshot of its current internal state. It also uses the Memento to restore the Original state

Caretaker: It's responsible for the memento's safekeeping.

The memento design pattern helps to implement Undo facilities for any application. Think about a word processor app - at any point in time we can undo and get back the earlier state of the document - here lies the greatness of the memento design pattern.

The UML class diagram of the Memento pattern looks like the following:




The source code for the Memento Design Pattern is as follows:

cclass Originator:
    _state = ""

def set(self, state):
print(f"Originator: Setting state to {state}")
self._state = state

def save_to_memento(self):
return self.Memento(self._state)

def restore_from_memento(self, m) :
self._state = m.get_saved_state()
print(f"Originator: State after restoring from Memento: {self._state}")

class Memento:

def __init__(self, state):
self._state = state

def get_saved_state(self):
return self._state

class Caretaker:
def __init__(self, originator):
self.originator = originator
self.saved_states = []

def saveState(self):
self.saved_states.append(self.originator.save_to_memento())

def undo(self):
if not len(self.saved_states):
return
self.originator.restore_from_memento(self.saved_states.pop())

# Press the green button in the gutter to run the script.
if __name__ == '__main__':
originator = Originator()
caretaker = Caretaker(originator)
originator.set("State1")
caretaker.saveState()
originator.set('State2')
caretaker.saveState()

originator.set("State3")
caretaker.saveState()

originator.set("State4")
caretaker.saveState()
print("\nClient: Now, let's rollback!\n")
caretaker.undo()
print("\nClient: Once more!\n")
caretaker.undo()

If we run the above lines of code, the output will be:


Originator: Setting state to State1
Originator: Setting state to State2
Originator: Setting state to State3
Originator: Setting state to State4

Client: Now, let's rollback!

Originator: State after restoring from Memento: State4

Client: Once more!

Originator: State after restoring from Memento: State3

Wednesday, August 23, 2023

Facade Design Pattern in Python...

This design pattern provides a simplified and unified interface to hide the inner complexities of several subsystems or libraries.

As most of the time, the user of a subsystem does not want to know about the internal complexities. He just wants a simplified interface to use the subsystem. Subsystems become complex as they evolve. A facade can provide this simplified interface to the users of the subsystem.

The main participants of the Facade design pattern are

- facade itself

- and the several subsystems

The subsystems have no knowledge about the facade - meaning they don't keep any reference to the facade.

Let me give you an example.

Suppose there are many geometrical shapes whose drawing functionalities are complex and each one must be drawn differently. But the client really does not want to go into that complexity.

Here comes the facade pattern in the rescue. This pattern will give a single method called drawShapes which will take care of all the internal complexity of the individual shapes.

The UML class diagram of the facade design pattern example looks like the following:


Here goes the source code of the example.

from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def draw(self):
pass

class Circle(Shape):
def draw(self):
print("Special method to draw a circle")

class Rectangle(Shape):
def draw(self):
print("Special method to draw a rectangle")

class Triangle(Shape):
def draw(self):
print("Special method to draw a triangle")

class FacadeToShape:
def __init__(self, circle, rectangle, triangle):
self.circle = circle
self.rectangle = rectangle
self.triangle = triangle

def drawCircle(self):
self.circle.draw()
def drawRectangle(self):
self.rectangle.draw()
def drawTriangle(self):
self.triangle.draw()

def drawShapes(self):
self.drawCircle()
self.drawTriangle()
self.drawRectangle()


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
facateToShape = FacadeToShape(Circle(), Rectangle(), Triangle())
facateToShape.drawShapes()

Tuesday, August 22, 2023

Builder Design Pattern implemented using Python...

 Today I am presenting the Builder Pattern in Python.

As the name suggests - it is part of the creational pattern.

The idea is that if a class has many member variables - and the final object may not depend on all of them - and the number of parameters that the constructor takes varies - instead of making a lot of overloaded constructors, we use this Builder pattern

The code footprint is heavily reduced - as there is no need to create a matching number of overloaded constructors to match each and every permutation and combination of the member variables passed in the constructors.

In the given example, we have created a common Student class which may represent a 10th std, a 12th std, or an engineering student - maybe in 1st yr or 2nd year, or third year or maybe in the final fourth year.

In the case of a student who is a qualified engineer, we need to supply all the marks…

But in the case of a 2nd-year student - we don’t need to bother about third-year marks or fourth-year marks, the 10th, 12th, and 1st-year marks are sufficient to represent the object of a second-year student vis-a-vis marks.

Here goes the code for the CS students of the UNIVERSE.

from abc import  ABC, abstractmethod

class Student:
class Builder:

def __init__(self):
self.name=""
self.address = ""
self.tenthmarks = 0
self.twelfthmarks = 0
self.firstyrmarks = 0
self.secondyrmarks = 0
self.thirdyrmarks = 0
self.fourthyrmarks = 0

def setname(self, name):
self.name = name
return self
def setaddress(self, address):
self.address = address
return self
def settenthmarks(self, tenthmarks):
self.tenthmarks = tenthmarks
return self
def settwelfthmarks(self, twelfthmarks):
self.twelfthmarks = twelfthmarks
return self
def setfirstyrmarks(self, firstyrmarks):
self.firstyrmarks = firstyrmarks
return self
def setsecondyrmarks(self, secondyrmarks):
self.secondyrmarks = secondyrmarks
return self
def setthirdyrmarks9(self, thirdyrmarks):
self.thirdyrmarks = thirdyrmarks
return self
def setfourthyrmarks(self, fourthyrmarks):
self.fourthyrmarks = fourthyrmarks
return self

def build(self):
return Student(self)

def __init__(self, builder):
self.name = builder.name
self.addrees = builder.address
self.tenthmarks = builder.tenthmarks
self.twelfthmarks = builder.twelfthmarks
self.firstyrmarks = builder.firstyrmarks
self.secondyrmarks = builder.secondyrmarks
self.thirdyrmarks = builder.thirdyrmarks
self.fourthyrmarks = builder.fourthyrmarks

def displayData(self):
if self.name != "":
print("Name : " , self.name)

if self.addrees != "":
print("Address : ", self.addrees)

if self.tenthmarks != 0:
print("10th Marks : ", self.tenthmarks)

if self.twelfthmarks != 0:
print("12th Marks : ", self.twelfthmarks)

if self.firstyrmarks != 0:
print("1st Yr Marks : ",self.firstyrmarks)

if self.secondyrmarks != 0:
print("2nd Yr Marks : ", self.secondyrmarks)

if self.thirdyrmarks != 0:
print ("3rd Yr marks : ", self.thirdyrmarks)

if self.fourthyrmarks != 0:
print ("4th Yr Marks : ", self.fourthyrmarks)


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
ram12th = Student.Builder().setname("Ram").setaddress("Kolkata").
settenthmarks(50).build()

shyam2ndYr = Student.Builder().setname("Shyam").setaddress("Delhi")
.settenthmarks(70).settwelfthmarks(78).setfirstyrmarks(70).build()

ram12th.displayData()

shyam2ndYr.displayData()

If we run the above program, the output will be as follows:

Name :  Ram
Address :  Kolkata
10th Marks :  50

Name :  Shyam
Address :  Delhi
10th Marks :  70
12th Marks :  78
1st Yr Marks :  70

As it is clear for Ram, till 10th marks was important whereas, for Shyam, on the other hand, marks till 1st year are important.