Unit 20 - Applied Programming and Design Principles_2022
Unit 20 - Applied Programming and Design Principles_2022
Assignment title Sales Analysis System for Sampath Food City (PVT) Ltd
• Constructive?
Y/N
• Linked to relevant assessment
Y/N
criteria?
• Identifying opportunities
for improved performance? Y/N
Y/N
• Agreeing actions?
Does the assessment decision need
Y/N
amending?
Assessor signature Date
Give details:
Internal Verifier
Date
signature
Programme Leader
Date
signature (if required)
LO1. Investigate the impact of SOLID development principles on the OOP paradigm
Resubmission Feedback:
Action Plan
Summative feedback
1. A Cover page or title page – You should always attach a title page to your assignment. Use previous page as
your cover sheet and make sure all the details are accurately filled.
2. Attach this brief as the first section of your assignment.
3. All the assignments should be prepared using a word processing software.
4. All the assignments should be printed on A4 sized papers. Use single side printing.
5. Allow 1” for top, bottom , right margins and 1.25” for the left margin of each page.
1. The font size should be 12 point, and should be in the style of Time New Roman.
2. Use 1.5 line spacing. Left justify all paragraphs.
3. Ensure that all the headings are consistent in terms of the font size and font style.
4. Use footer function in the word processor to insert Your Name, Subject, Assignment No, and Page Number
on each page. This is useful if individual sheets become detached for any reason.
5. Use word processing application spell check and grammar check function to help editing your assignment.
Important Points:
1. It is strictly prohibited to use textboxes to add texts in the assignments, except for the compulsory information.
eg: Figures, tables of comparison etc. Adding text boxes in the body except for the before mentioned
compulsory information will result in rejection of your work.
2. Carefully check the hand in date and the instructions given in the assignment. Late submissions will not be
accepted.
3. Ensure that you give yourself enough time to complete the assignment by the due date.
4. Excuses of any nature will not be accepted for failure to hand in the work on time.
5. You must take responsibility for managing your own time effectively.
6. If you are unable to hand in your assignment on time and have valid reasons such as illness, you may apply (in
writing) for an extension.
7. Failure to achieve at least PASS criteria will result in a REFERRAL grade .
8. Non-submission of work without valid reasons will lead to an automatic RE FERRAL. You will then be asked to
complete an alternative assignment.
9. If you use other people’s work or ideas in your assignment, reference them properly using HARVARD
referencing system to avoid plagiarism. You have to provide both in-text citation and a reference list.
10. If you are proven to be guilty of plagiarism or any academic misconduct, your grade could be reduced to A
REFERRAL or at worst you could be expelled from the course.
11. If you are proven to be guilty of plagiarism or any academic misconduct, your grade could be reduced to A
REFERRAL or at worst you could be expelled from the course.
I hereby, declare that I know what plagiarism entails, namely to use another’s work and to present it as my own
without attributing the sources in the correct way. I further understand what it means to copy another’s work.
thisara1989@hotmail.com
Student’s Signature: Date: 31st May 2024
(Provide E-mail ID) (Provide Submission Date)
Unit Number and Title Unit 20: Applied Programming and Design Principles
Assignment Title Sales Analysis System for Sampath Food City (PVT) Ltd
Issue Date
Submission Format:
Part 1.
Report- Submit a professional report with appropriate report formatting and guidelines followed. All the
research data should be referenced along with in-text citations using the Harvard referencing system.
Part 2
A fully functional standalone software system (command-line interface based)
LO1 Investigate the impact of SOLID development principles on the OOP paradigm.
LO2 Design a large dataset processing application using SOLID principles and clean coding techniques.
LO3 Build a data processing application based on a developed design.
LO4 Perform automatic testing on a data processing application.
Assignment Brief
Scenario.
‘Data Labs’ is a leading software development company in Sri Lanka. They are focusing on helping
businesses to build their businesses through creative and effective solutions. Assume that you work as an
apprentice software developer for Data Labs company. As a part of your role, you have been asked to
develop a software system (command-line interface based) for the following scenario using python
programming language.
Sampath Food City (PVT) Ltd is one of the main supermarket networks in Sri Lanka. Currently, Sampath
Food City has several branches island wide. At present, transactions of each branch are recorded through a
point of sale (pos) system. At the end of each month, recorded data of each point of the sales system are
transferred to a centralized database. Top-level management of the company use the centralized data to do
the monthly sales data analysis of the whole company at the end of the month to find insights to take
managerial decisions for the company. Currently, the company uses a paper-based manual system to do
monthly sales data analysis. Some weaknesses and drawbacks that have occurred in the manual system such
as human errors leading to inaccurate information, time consuming, data redundancy, inconsistency and
difficulty to find insights affect the business performance negatively.
Therefore, the management of Sampath Food City has decided that using a customized software system
for sales data analysis is the solution for eliminating above mentioned weaknesses and drawbacks of the
existing sales data analysis process.
Assume yourself as a software developer of Data Labs (PVT) Ltd and assigned to develop a sales data
analysis system (command-line interface based) using python programming language for scenario given
above.
New system should provide following features:
• Monthly sales analysis of each branch.
Develop a command-line interface-based solution for the above scenario and produce a report
covering the following tasks.
Activity 1
• Explain how clean coding techniques can impact on the use of data structures and operations when
writing algorithms by taking suitable examples from the given scenario. Analyse each of the
creational, structural and behavioral design patterns with relevant examples.
Activity 2
• Design a large data set processing application, utilising SOLID principles, clean coding techniques,
a design pattern and data structures by providing justifications for selected design pattern and
selected data structures.
• Design a suitable testing regime for the application developed with a provision for automated
testing, selected test types and selected automatic testing tools, and provide justifications for the
selections. Refine the design to include multiple design patterns by justifying the reasons for the
inclusion of each design pattern for the given scenario.
Build a large dataset processing application based on the design produced, by using python programming
language and provide evidence for the usage of data structures and file handling techniques. Your answer
must include an assessment of how effective the use of SOLID principles, clean coding techniques and
programming patterns on the application developed. Take suitable examples from the developed application
to elaborate your answer.
Activity 4
• Examine the benefits and drawbacks of different methods of automatic testing of applications and
software systems available for automatic testing by taking examples from the developed application.
Provide an action plan to address the identified drawbacks of testing of the developed application.
• Implement automatic testing of the developed application by using selected testing tools and provide
evidence for the automatic testing. Discuss how developer-produced and vendor-provided automatic
testing tools differ for applications and software systems by taking suitable examples from the
testing of the developed application.
Learner name:
Qualification:
Unit number &
title:
Description of activity undertaken
Assessment criteria
Learner name:
Learner
Date:
signature:
Assessor
name:
WITNESS STATEMENT
Learner
name:
Qualification:
Unit number
& title:
Description of activity undertaken (please be as specific as
possible)
Witness Job
name: role:
1|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
1.1.1 Advantages of Object-Oriented Programming
Object-oriented programming (OOP) has many important benefits that make it a popular
choice for developing different kinds of software.
1. Modularity - OOP lets us break down software into smaller, manageable parts
called classes. Each class can be developed, tested, and debugged on its own,
making the development process more efficient and organized.
3. Scalability - OOP systems can easily grow. We can add new objects and classes
with minimal changes to existing code, making OOP perfect for projects that need
to expand and evolve over time.
9. Efficiency in Problem Solving - OOP breaks down complex problems into smaller,
manageable objects. Each object can be developed to handle a specific part of the
problem, making the problem-solving process more straightforward.
2|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Object-oriented programming (OOP) is used in many software areas because it helps
handle complex systems well. For example, in software development, it's often used to
make big programs, letting teams work on different parts easily. In web development,
languages like JavaScript, PHP, Ruby, and Python use OOP to make dynamic websites
with neat code. Games also use OOP a lot to manage characters and items effectively, using
engines like Unity and Unreal Engine. Mobile apps for Android and iOS benefit from OOP
because it make development, upkeep, and growth simple. Making user interfaces (UIs) is
easier with OOP, using tools like JavaFX, Qt (C++), and Tkinter (Python).
OOP is also handy in other areas like simulations, business software, data analysis,
embedded systems, and robotics because it helps make models accurate, and systems easy
to expand and manage.
Object-oriented programming (OOP) is based on a few main ideas that guide how it works.
These ideas help programmers make code that's easier to understand, organize, and use.
1. Encapsulation: Encapsulation means putting data and the methods that work on
that data together in one place. It helps keep things organized and stops other parts
of the program from messing with the data by mistake. In OOP, we bundle data
(attributes) and the actions that work on that data (methods) into a single unit or
class. This helps hide the details of how things work and protects the data from
being changed by mistake.
Figure 2 - Encapsulation
3|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 3 – Example for encapsulation (Author created)
In this example:
• We have a Car class with encapsulated attributes (__make, __model, and __speed).
• These attributes are marked as private by prefixing them with double underscores
(__), which means they cannot be accessed directly from outside the class.
• We define public methods (accelerate, brake, and get_speed) to interact with these
encapsulated attributes. These methods allow controlled access to the data,
ensuring that the internal state of the Car object is maintained properly.
• When creating a Car object (my_car), we provide the make and model as
arguments.
• We use the accelerate and brake methods to modify the car's speed and the
get_speed method to retrieve the current speed.
2. Inheritance: Inheritance is like passing down traits in families. It lets new types of
things inherit characteristics and behaviors from existing types. This saves time
because we can reuse what's already been made. Inheritance allows a new class
(subclass) to inherit attributes and methods from an existing class (superclass). This
helps us reuse code and create relationships between classes, where subclasses can
have their own special features but also share features with their parent class.
4|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 4 – Inheritance
5|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
In this example:
• We have a parent class Animal with an attribute species and a method speak(). The
speak() method is defined but not implemented (pass) in the parent class.
• We define two child classes Dog and Cat, both inheriting from the Animal class.
They have their own attributes (breed) and implement the speak() method specific
to each type of animal.
• The __init__() method of each child class calls the constructor of the parent class
using super().__init__(), initializing the species attribute inherited from Animal.
• We create objects (dog and cat) of the child classes and access their attributes
(species and breed) as well as the overridden speak() method to get the sound each
animal makes.
Figure 6 – Polymorphism
6|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 7 – Example for Polymorphism (Author created)
In this example:
• We have a parent class Animal with a method speak(). This method is defined but
not implemented (pass) in the parent class.
• We define three child classes Dog, Cat, and Cow, each inheriting from the Animal
class. They implement their own speak() method specific to each type of animal.
• We create objects (dog, cat, and cow) of different classes and pass them to the
make_animal_speak() function.
• Despite the objects being of different types (Dog, Cat, Cow), they all have a speak()
method. When we call make_animal_speak() with each object, it invokes the
respective speak() method associated with that object's class.
7|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
4. Abstraction: Abstraction hides complicated details and only shows what's
important. It's like driving a car without knowing how the engine works – we don't
need to know every detail to use it effectively. Abstraction hides the complex details
of how things work and shows only the important parts. This helps us work with
high-level ideas and concepts in our programs.
Figure 8 – Abstraction
8|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
In this example:
• We have an abstract class Shape that defines two abstract methods area() and
perimeter(). These methods serve as an interface for any shape class to implement.
• We define two concrete classes Rectangle and Circle that inherit from the Shape
class. These classes implement the area() and perimeter() methods according to
their specific shapes.
• We create objects of different shapes (rectangle and circle) and pass them to the
calculate_shape_info() function. Despite their differences in implementation, the
function can work with any shape object because they all adhere to the common
Shape interface.
9|Page
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 11 – Example for Class & Object (Author created)
In these examples:
• We create objects (student1, student2) of these classes, providing initial values for
their attributes.
• We can create multiple objects of the same class, each with its own set of attribute
values.
10 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
In object-oriented programming, classes work together in different ways to show how
objects from those classes connect and depend on each other. These connections help define
how the parts of a program fit together and cooperate.
Inheritance
Think of this as a family tree. A child class (subclass) inherits traits (attributes and methods)
from a parent class (superclass). For example, if we have a class called Animal, a class
called Dog can inherit from it. This means Dog is a type of Animal and gets all the basic
features of an Animal.
Association
This is a basic connection where one class uses or knows about another. For example, a
Teacher class might be associated with a Student class because teachers teach students,
but they are separate entities and don't own each other.
Composition
This is a stronger form of aggregation. Here, the parts cannot exist without the whole. For
example, a House is composed of Rooms. If the house is destroyed, the rooms cease to
exist.
12 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
1.2 SOLID Principles
The SOLID principles are five key guidelines that help make software easier to understand,
flexible, and maintain. Each principle focuses on a different part of designing object-
oriented software, helping to create a codebase that is strong and easy to expand. By
following these principles, we can build software that is easier to manage, extend, and keep
in good shape.
The Single Responsibility Principle (SRP) means that a class should only have one job or
reason to change. This keeps each class focused on just one task, making the system easier
to understand and manage. When classes have a single purpose, changes to one feature will
only affect the relevant class, reducing the chance of causing problems elsewhere.
Single Responsibility Principle (SRP) by separating the User class and the UserRepository
class.
13 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
User Class
The User class is responsible for user-related tasks, such as storing user information.
UserRepository Class
The UserRepository class is responsible for handling database operations related to users.
14 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
1.2.2 Open/Closed Principle (OCP)
The Open/Closed Principle (OCP) means that we should be able to add new features to our
classes, modules, or functions without changing their existing code. This helps keep our
system easy to expand and maintain, reducing the chances of creating bugs when adding
new features.
Without OCP
With OCP
To adhere to the Open/Closed Principle, we can use inheritance or composition to extend
the functionality without modifying the existing code.
15 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Finally, we can use these classes without changing the existing PaymentProcessor interface
Figure 18 – Example for with OCP & Without OCP (Author created)
The Liskov Substitution Principle (LSP) means that we should be able to replace a parent
class with a child class without breaking the program. This principle ensures that a subclass
can be used in place of its parent class without changing how the program works.
Imagine we have a base class called Bird with a method fly. An Ostrich is a type of Bird,
but it cannot fly. If a piece of code expects all Bird objects to be able to fly, using an Ostrich
instead would break the code, which violates the Liskov Substitution Principle.
A FlyingBird is a type of bird that can fly, while an Ostrich is a type of bird that cannot
fly. Code that requires flying birds can use FlyingBird directly, ensuring it never tries to
make a bird fly that can't.
16 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 21 – Adhering to LSP (Author created)
In this redesign,
• Bird is a base class for all birds.
• FlyingBird is a subclass that can fly.
• Ostrich is a subclass that does not have a fly method.
The let_flying_bird_fly function only accepts FlyingBird objects, making sure it only
works with birds that can fly. This design follows the Liskov Substitution Principle because
using a FlyingBird instead of a Bird doesn't break the program.
The Interface Segregation Principle says that if something doesn't need certain features, it
shouldn't have to deal with them. It's like having a phone that only does what we need it to,
without extra stuff we don't use. This makes things easier to understand and keeps
everything neat and tidy.
Imagine we have a class that requires too many responsibilities. If a client only needs a part
of those responsibilities, it's better to create smaller, more specific interfaces for each
responsibility. This way, clients only depend on the methods they actually use.
Let's consider an interface Worker that has both work and eat methods. A robot can work
but doesn't eat, while a human worker does both.
17 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 22 – Violation of ISP (Author created)
the RobotWorker class is forced to implement the eat method, which it does not need,
leading to a NotImplementedError. Now, let's split the Worker interface into two smaller
interfaces Workable and Eatable.
18 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
1.2.5 Dependency Inversion Principle (DIP)
The Dependency Inversion Principle says that the important parts of a system shouldn't rely
too much on the smaller parts. Instead, they should both rely on general ideas or plans. This
makes it easier to change things around without causing big problems. It's like how a team
works better when everyone focuses on the big picture instead of tiny details.
Let's consider a class Switch that controls a Light. The Switch class directly depends on
the Light class.
the Switch class directly depends on the Light class, violating DIP. If we want to switch
to a different type of device, like a fan, we would have to modify the Switch class. Now
we introduce an abstract Switchable interface and make both Light and Fan implement it.
19 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
In this redesign,
• Both Light and Fan implement the Switchable interface.
• The Switch class depends on the abstract Switchable interface, not on specific
implementations like Light or Fan.
In software development, "clean code" means writing code that's easy to understand,
follow, and change. It's like writing a clear and organized story rather than a confusing
jumble of words. When code isn't clean, even small changes can cause big problems,
making it hard to fix or add new features.
The Clean Code Developer School teaches a method called Clean Analysis and Clean
Design, which focuses on planning and designing code well from the start, whether it's a
new project or an existing one. They also promote working closely with customers and
being flexible, like in agile methods, to learn and adapt quickly. The goal is to avoid
building up "technical debt" problems that pile up and become harder to fix over time.
Though it might take more effort upfront, writing clean code saves time and money in the
long run because it's easier to maintain and extend. So, clean code isn't just good for the
technical side of things, It's smart economically too.
Meaningful Names
When we write code, give things names that tell us what they're for. Whether it's a variable,
a function, or a class, its name should make it clear what it does. Imagine reading a story
where characters have names like "Person A" or "Thing B" – it wouldn't make much sense!
Similarly, in code, if we name something like "x" or "temp," it doesn't tell us anything about
what it's doing. But if we name it "userInput" or "calculateTotal," suddenly it's much easier
to understand what's going on. So, make sure our names are clear and help explain what
each part of these code does.
20 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Keep Functions Small
Make sure functions aren't too long or complicated. Each function should do just one thing,
and it should do it really well. Think of it like following a recipe, we wouldn't want a recipe
with a hundred steps all crammed into one Instead, each step focuses on just one part of
making the dish. Similarly, in our code, each function should have a clear purpose and not
try to do too much. Aim for functions that we can see all at once on our computer screen
and try to keep them focused on just one job. That way, our code stays organized and easy
to understand.
Add Comments
It's a good idea to add comments here and there to explain things that might be tricky to
understand. But try to write our code in a way that's clear and easy to follow without
needing too many comments. As per the above example on recipe, we might add a note to
explain a tricky step, but most of the time, the instructions should be clear enough on their
own. So, aim for code that's easy to read without comments, but don't be afraid to add them
when we need to explain something important.
SOLID Principles
When we're building software using object-oriented design, it's helpful to follow a set of
principles called SOLID that we discussed in previous section. These principles are like
guidelines that help us write code that's easier to work with, change, and maintain over time
Testing
It's important to test our code in random time to make sure it works correctly. Automated
tests are like little checks that run automatically to make sure our code does what it's
supposed to do. We can think of them like quality control checks in a factory, they help
catch any mistakes or problems before they cause bigger issues.
21 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Error Handling
Handling errors in our code is like driving carefully over bumps in the road instead of
crashing. It means expecting problems and having plans to deal with them without making
our program crash or act weird. This could mean catching errors and dealing with them in
a smart way. It's also important to explain errors clearly, like figuring out what's wrong
with a broken appliance by looking closely. Avoid giving vague error messages or ignoring
problems altogether, like trying to fix a health issue without knowing what's going on. By
dealing with errors well and explaining them clearly, our code becomes easier to fix and
understand, like having clear signs on a well-lit path that helps us navigate through
obstacles.
Version Control
Think of version control, like Git, as a storybook for our code. It keeps track of every
change we make, so we can see how our code has evolved over time. When we commit
changes regularly, it's like saving different versions of our story. And just like leaving notes
in our storybook to explain each update, clear commit messages explain what we changed
in our code. By using version control and committing often, we can manage our code's
history, work with others, and understand changes easily, kind of like writing a story with
a team where everyone knows what's happening.
Refactoring
Refactoring our code is like tidying up our room regularly to keep it organized and clean.
It involves restructuring our code to make it easier to understand, read, and maintain. Just
like how we might rearrange our furniture or declutter our space to make it more functional,
refactoring improves the structure of our code. It's an ongoing process, similar to how we
keep improving our room layout as we learn more about what works best for us. By
regularly refactoring our code, we ensure that it stays neat and efficient, making it easier to
work with and adapt to changes over time.
Readability
Clean code is like a clear road map. It's easy to understand where we're going and why.
This helps developers work together better and saves time when they need to check or
update the code later. When the code is neat and tidy, it's easier to fix problems or add new
stuff without accidentally messing up other parts. When everyone can understand the code
easily, it makes working together smoother and faster. In the scenario provided, readability
is crucial for ensuring that the code is easily understandable by developers, facilitating
collaboration, maintenance, and future enhancements.
22 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 26 – Without Clean Code (Author created)
• The function and variable names are abbreviated and unclear (cap, d, t, c, r), making
it hard to understand the purpose of the code at a glance.
• All logic is crammed into a single function, which makes the code harder to read
and maintain.
• Clear and descriptive names make the purpose of each function and variable
immediately apparent.
• The code is structured and modular, with each function handling a specific part of
the task.
• Error handling and type checking improve the robustness and readability of the
code.
Maintainability
Maintainability means that when the code is clean and well-organized, it's easier for
developers to find and fix mistakes, add new features, or change things without causing
more problems. It's like having a well-kept garden – we can see what needs attention and
make changes without damaging the whole thing. This saves time and money in the long
run because we don't have to spend as much time fixing things or dealing with unexpected
issues. Let's compare an example of calculating the monthly sales of each branch with and
without clean coding principles for maintainability as per our scenario.
23 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 28– Without Clean Code (Author created)
Scalability
Scalability means that clean code is like building with Lego blocks – we can add more
blocks as our project grows without everything falling apart. This makes it easy to add new
features or make changes without making a mess. When code is well-structured and
organized, developers can add new pieces without making the existing ones messy or
complicated. It's like building a bigger house on a strong foundation, everything stays
sturdy and in place as the project gets bigger. This makes it easier for developers to work
on the code and keep it running smoothly as the project grows.
For a sales data analysis system like the one for Sampath Food City, scalability ensures that
the system can handle increasing amounts of sales data from multiple branches and more
complex analyses without performance degradation.
• This modular design is more scalable because we can easily extend the
functionality. For instance, if we need to calculate the average price for different
categories of products, we can reuse the calculate_total_and_count function without
modifying the core logic of calculate_average_price.
Reduced Errors
Reduced errors mean that clean code is like clear instructions. it's easy to understand what
needs to be done, which reduces the chance of making mistakes. When the code is clear
and easy to follow, developers are less likely to get confused or make errors while working
on it. This leads to higher-quality code with fewer bugs in the end product. It's like
following a recipe with clear steps – when everything is laid out clearly, it's harder to mess
up the final dish.
25 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
• In the clean code example, data: List[Tuple[str, float]] clearly defines the expected
input type, reducing the risk of passing incorrect data types.
• The clean code checks if the price is of the correct type (int or float) before adding
it to the total, and it skips and logs a message for invalid entries.
• The logic is more explicit and easier to understand, reducing the likelihood of
introducing errors when modifying or extending the code.
• The clean code ensures that if no valid prices are found, the function returns 0.0
instead of causing a division by zero error.
26 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 35 – With Clean Code (Author created)
Enhanced Collaboration
Writing clean code is like having a well-organized toolbox. We can quickly find the right
tool for the job, which speeds up the work. When the code is clean and easy to understand,
developers spend less time trying to figure out what's going on and more time actually
working on it. This means projects get finished faster, and the software can be released
sooner. We can get to our destination much faster when there are no obstacles in the way.
Adaptability
Clean code is like a flexible building. It can easily be reshaped or expanded to fit new
requirements or technology. By following best practices and design principles, developers
can adjust or add to the existing code without worrying about breaking it. This ensures that
the software stays flexible and can adapt to changing needs over time. It's like having a
house that can be remodeled without tearing down walls – the structure remains strong, and
we can make changes confidently as needed. This adaptability is crucial for keeping the
software responsive to evolving technology and user requirements.
28 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 39 – With Clean Code (Author created)
Improved Debugging
Clean code is like a well-organized library. Everything is labeled clearly and easy to find.
With descriptive names, clear structure, and helpful comments, developers can quickly
understand what each part of the code is doing. This makes it much easier to find and fix
problems when they arise. It's like having a map to guide us through a maze – we can
quickly pinpoint where things went wrong and trace the path to the solution. This speed up
the debugging process and helps developers resolve issues more efficiently, keeping the
software running smoothly.
29 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 41 – With Clean Code (Author created)
Design patterns
Design patterns are like ready-made templates for solving common problems in software
design. They're not specific pieces of code we can copy and paste, but more like general
ideas or concepts. When we follow a pattern, we're basically using a blueprint to create a
solution that fits our program's needs. While patterns are often compared to step-by-step
recipes (like cooking we talk earlier), they're more like outlines that give us the main idea
and let us figure out the details ourself. And even if we use the same pattern in different
programs, the actual code we write might be different each time.
30 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Creational design patterns
Creational design patterns are all about making it easier to create objects in software. They
help hide the complicated stuff about how objects are made, so we can focus on using them
instead. These patterns handle things like making sure there's only one instance of a class,
separating how an object is created from how it's used, and giving we ways to create objects
without knowing exactly what kind they are. There are different types of creational patterns,
like Singleton, Factory Method, Abstract Factory, Builder, and Prototype, each with its own
way of tackling these problems.
1. Singleton
The Singleton pattern ensures that only one instance of a class exists throughout the entire
application, regardless of how many times we try to create objects of that class. It achieves
this by controlling the creation process through a special method or property in the class.
This method checks if an instance of the class already exists; if it does, it returns that
instance, otherwise, it creates a new instance and returns it. This pattern is handy in
situations where having multiple instances of a class could lead to problems, like managing
resources or maintaining a global state. By limiting instances to one, Singleton provides a
straightforward way to access this single instance from anywhere in the application,
ensuring consistency and centralized control.
31 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
In this example, the Singleton class is used to calculate the square of a number. The
get_instance() method ensures that only one instance of Singleton is created, and the
calculate_square() method calculates the square of the number set using the set_number()
method.
We have an abstract class Animal representing the product, with concrete subclasses Dog
and Cat. We also have an abstract class AnimalFactory representing the creator, with
concrete subclasses DogFactory and CatFactory. Each factory creates a specific type of
animal. This way, we can create instances of different animals without needing to know
their specific classes, promoting flexibility and loose coupling.
32 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
3. Abstract Factory Pattern
The Abstract Factory pattern is like a blueprint for creating groups of related objects
without having to know exactly what they are. It's like having a menu where we can choose
different options, but we don't need to know how each option is made. There are two main
parts: the blueprint (abstract factory) and the actual making (concrete factories). The
blueprint says how to make each object, while the concrete factories are the ones actually
making the objects. People using this pattern can just ask for what they need without
worrying about the details of how it's made. It's handy for things like creating different
parts of a computer program that all need to work together smoothly, making it easier to
change or add new parts later on.
33 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 45 – Abstract Factory pattern (Author created)
We have abstract product classes Fruit and Vegetable, along with their concrete
implementations for different types of fruits and vegetables. We also have an abstract
factory SectionFactory with methods for creating fruits and vegetables, and concrete
factory classes ProduceFactory and DairyFactory that implement these methods to create
products from the produce section and dairy section of the supermarket, respectively.
Finally, the client code demonstrates how to use the factories to create products from
different sections of the supermarket and display their names.
4. Builder Pattern
The Builder pattern is like having a blueprint for building something complicated. It helps
in creating complex objects by breaking down the construction process into smaller,
manageable steps. Instead of building everything all at once, we build the object piece by
piece, following a specific order. This pattern is handy when we need to create different
versions of the same object, as it keeps the construction process organized and separate
from the final result.
34 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 46 – Builder pattern (Author created)
35 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
This code demonstrates the Builder pattern by creating two types of pizzas (Margherita and
Pepperoni) using different builders. Each builder implements the construction process for
its respective pizza type, and the director class orchestrates the process to create the final
pizzas. Finally, the pizzas are displayed to show their attributes.
5. Prototype Pattern
The Prototype pattern is like making a copy of something we already have, rather than
starting from nothing. It helps in creating new things by copying an existing one, which
can be quicker and easier, especially when the thing we're making is complex. Instead of
building everything from scratch each time, we can use a prototype as a template and make
copies of it when we need something similar. This way, we save time and effort, and we
also keep things consistent because all the copies are based on the same original.
We have a prototype class CarPrototype with a clone method that serves as the interface
for cloning. We then have a concrete prototype class Car representing a car object with
attributes for brand and model. The clone method in the Car class creates a deep copy of
the car object. Finally, in the client code, we create new car objects by cloning the prototype
and display their details.
36 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Structural Design Patterns
Structural design patterns are a type of design blueprint that help organize and connect
objects in software systems. They focus on how objects work together to create larger
structures and relationships. These patterns make it easier to understand and modify the
system by addressing concerns like how objects are composed, how interfaces are defined,
and how classes inherit from each other. Essentially, they provide solutions for building
software in a way that is flexible, reusable, and easy to scale. Think of them as templates
that guide developers in designing clear relationships between different parts of the system,
ensuring that changes in one part don't cause problems elsewhere.
1. Adapter pattern
The Adapter pattern helps different things that don't speak the same language to work
together smoothly. It's like a translator between two people who speak different languages,
making sure they understand each other. For example, imagine a music player that only
knows how to play one type of music file, but we want it to play a different type. The
Adapter pattern acts like a translator, allowing the music player to understand and play the
new type of file without any confusion.
37 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 49 – Adapter pattern (Author created)
This code defines three classes: MP3Player, WAVPlayer, and AudioAdapter. The
AudioAdapter class acts as a bridge between MP3Player and WAVPlayer, allowing
them to play audio files of different formats using a unified play method. We can create an
instance of MP3Player, create an adapter with it, and then use the adapter to play MP3
files.
2. Bridge pattern
The Bridge pattern is a way of organizing code so that different parts can change
independently. It's like building a bridge between two separate things so they can work
together without being too closely connected. For example, imagine we have a program
that can draw shapes, but we want it to be able to draw shapes in different colors. The
Bridge pattern would help us keep the drawing part separate from the coloring part, so we
can change one without affecting the other. It's a helpful way to make our code more
flexible and easier to work with, especially when we expect things to change often.
38 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 50 – Bridge pattern (Author created)
We have an abstraction Shape, which represents a shape that can be drawn with a color.
We also have concrete implementations RedColor and BlueColor, which represent
different colors. The refined abstractions Circle and Square extend the Shape class and
implement the draw method to draw themselves in the specified color. The Bridge pattern
allows us to change the color of a shape without modifying its class by passing different
color objects to the shapes.
3. Composite pattern
The Composite pattern is like organizing objects into a tree, where each object can either
be a single item or a group of items. It helps in treating individual items and groups of items
in the same way, making it easier to work with complex structures. For example, in a file
system, a folder can contain both files and other folders. The Composite pattern allows us
to perform operations like listing, searching, or deleting items, whether they are individual
files or folders with more items inside. It's like having a unified way to handle both
individual elements and collections of elements in a structured manner.
39 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 51 – Composite pattern (Author created)
We have a Product interface with a name and price. The Department class represents
composite components that can contain other products or departments. Departments can
add, remove, and operate on their children, whether they are Product objects or other
Department objects. Finally, we create a composite structure with nested departments and
products and call the operation method to display the entire inventory.
4. Decorator pattern
The Decorator pattern is like adding layers of wrapping paper around a gift. It lets us add
extra features to an object without changing its core structure. Imagine we have a plain
cake, and we want to decorate it with frosting, sprinkles, and icing. With the Decorator
pattern, we can add each decoration separately without changing the cake itself. Similarly,
in programming, we can add new features or behaviors to objects without changing their
original code. This pattern is useful when we want to customize or extend the functionality
of objects dynamically, like adding new options to a menu without altering the existing
items.
40 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 52 – Decorator pattern (Author created)
41 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Behavioral design patterns are a group of design patterns that help objects in a program to
talk to each other and work together better. They're like rules that guide how different parts
of a program should communicate to get things done efficiently. These patterns make it
easier for developers to manage how objects interact and share tasks, making the program
more flexible and easier to maintain. They provide solutions to common problems in
communication and teamwork between objects, giving developers a roadmap for building
software that works well together.
Observer pattern
The Observer pattern is like a messaging system where one object sends out messages, and
other objects listen for those messages. Imagine we have a group chat, and whenever
someone sends a message, everyone else in the group gets notified. That's how the Observer
pattern works. In software, it's used when one part of the program needs to tell other parts
about changes it's going through. For example, if we're building a weather app, the
Observer pattern would let us update the temperature display whenever the weather
changes without having to constantly check for updates. It's a handy way to keep different
parts of a program in sync without them having to know too much about each other.
42 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
The Subject class represents the supermarket, and the Observer class represents different
roles in the supermarket, such as cashiers, stock managers, and customers. When a new
product is added to the inventory (notify method is called), all observers are notified
(update method is called), and they react accordingly. For instance, the cashier rings up
the product, the stock manager restocks it, and the customer buys it.
Strategy pattern
The Strategy pattern lets us pick different ways of doing something while our program runs.
It works like having different tools in a toolbox, and we can choose which one to use
depending on what we need. So instead of having just one way to do things, we can switch
between different methods easily, making our code more flexible and easier to change later
on.
The ShoppingCart class represents the context, and it takes a DiscountStrategy object as
a parameter. Depending on the type of customer (regular or premium), we can pass different
discount strategies to the ShoppingCart object. The RegularCustomerDiscount and
PremiumCustomerDiscount classes represent different strategies for calculating
discounts.
43 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Activity 2
The Single Responsibility Principle (SRP) is a key idea in software design that says each
part of a program should do just one thing. This means breaking down a big, complex
system into smaller, simpler pieces, each handling a specific task. For example, in a system
that processes data, we might have one part for getting the data, another for processing it,
and a third for sending it out. By following SRP, developers make their code easier to
understand, maintain, and test. When each part of the code has a clear job, updating or
changing one part won’t mess up the others. This makes the whole system more stable and
flexible.
The Open/Closed Principle (OCP) is a guideline in software design that says software
components should be open for adding new features but closed to changing existing code.
This means we can add new functions without altering what already exists. By designing
parts of the system to be modular and easy to extend, developers can add new features later
without breaking the existing code or causing new bugs. To achieve OCP, developers often
use techniques like inheritance, composition, and dependency injection, which help create
flexible and reusable code. Following OCP makes software more adaptable and easier to
maintain and scale, since new features can be added smoothly without messing up the
current system.
The Interface Segregation Principle (ISP) is a key concept in software design that suggests
creating small, specific interfaces tailored to the needs of different clients. Instead of having
one large interface with many methods, ISP recommends breaking it down into smaller,
more focused interfaces. This way, each client only interacts with the methods it actually
44 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
needs. Following ISP helps avoid forcing clients to deal with unnecessary methods,
reducing complexity and making the system more modular and flexible. By designing
interfaces that only include what a client needs, the code becomes more intuitive, easier to
understand, and simpler to maintain. This approach minimizes the risk of unintended side
effects, leading to a more reliable and maintainable codebase.
The Dependency Inversion Principle (DIP) is a crucial idea in software design that helps
decouple modules by using abstractions. DIP suggests that high-level modules shouldn't
depend on low-level modules. Instead, both should depend on abstractions or interfaces.
By coding to interfaces rather than specific implementations, developers can make their
systems more flexible and easier to extend or change. DIP also promotes techniques like
dependency injection to manage dependencies and keep modules loosely connected.
Following DIP leads to software that is easier to understand, maintain, and adapt, resulting
in more robust and versatile systems.
Structural design patterns help us use an existing class's interface in a new way, add new
responsibilities on the fly, and organize objects into tree structures to show part whole
relationships. Behavioural design patterns are great for systems where one event needs to
notify many objects, creating a one-to-many relationship. Strategy patterns keep all related
algorithms together, making them easy to swap out. Command patterns let us log and
manage different requests, allowing clients to handle various requests easily. The Model-
View-Controller (MVC) architecture splits an application into three parts.
• Model (data)
• View (user interface)
• and Controller (logic)
Using design patterns helps build scalable, maintainable, and flexible systems by offering
reusable solutions to common software design problems. Some common design patterns
are Factory Method, Abstract Factory, Adapter, Decorator, Composite, Command, and
MVC.
Creational design patterns, like Singleton, ensure there's only one instance of a class and
provide a global access point. The Factory Method pattern lets subclasses decide which
type of object to create. Abstract Factory patterns create families of related objects without
specifying their exact classes. The Repository pattern provides a way to access domain
objects using a collection-like interface, acting as a mediator between the domain and data
layers.
Dependency Injection (DI) allows a class to get the objects it depends on from outside,
making the class easier to test and configure.
45 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
2.1.3 Design Pattern and Data Structures
Choosing the right design patterns based on specific problems is key to developing
adaptable, scalable, and maintainable software. Design patterns offer reusable solutions and
promote best practices, helping developers create more robust, maintainable, and scalable
applications.
When designing a program, choosing the right design patterns is important and depends on
several factors. These include understanding the problem We're trying to solve, as well as
considering scalability, maintainability, flexibility, user interface design, data access,
integration with external libraries or services, performance, and future expansion.
Understanding the problem domain is crucial for selecting design patterns that address
specific needs. For scalability and maintainability, patterns like Abstract Factory, Factory
Method, and Singleton are beneficial. To ensure flexibility and adaptability, Dependency
Injection, Strategy, and Observer patterns are effective. For user interface design, the
Model-View-Controller (MVC) pattern helps separate concerns and enhance
maintainability. Managing data access and storage can be efficiently handled using the
Repository Pattern. Integration with external services can be simplified with Adapter or
Facade patterns. Additionally, the Flyweight pattern is useful for optimizing memory usage
in performance-critical applications.
For developing the sales data analysis application for Sampath Food City, choosing the
right data structures is essential for making the system efficient, easy to maintain, and able
to handle growth.
When we're building the sales analysis tool for Sampath Food City, it's super important to
pick the right ways to organize our data.
• Lists
• Dictionaries (Hash Maps)
• Queues
• Tree Structures
46 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
First off, when we talk about the stuff, we're keeping track of like sales, products, and
branches, we'll use lists. It's like making a list of all the things we've sold, all the products
we have, and all the branches we operate.
Now, when we want to quickly find something specific in our lists, we'll use something
called dictionaries, which are like special lists that let us find things super fast by using IDs.
So, if we want to find a branch or a product, we can do it really quickly with dictionaries.
In the part where we're storing all the sales records, we'll keep them together in one big list.
And to make it easy to find specific branches or products related to those sales, we'll use
dictionaries again. They'll help us quickly find details about branches or products based on
their IDs.
Now, when we're crunching the numbers to understand stuff like total sales for each branch
or product, or maybe we want to see how sales change over time, we'll use dictionaries to
store all that data. They're great for quickly organizing and looking up information. And if
we need to store some temporary results or individual sale amounts, we'll use lists.
Sometimes, we want to make sure we're not counting the same thing twice, like the same
product in different categories. For that, we'll use sets, which make sure we only count
unique things.
Lastly, when we're handling commands from the user, like what analysis to run, we'll use
queues to keep everything organized. It's like forming a line, so we process each request in
the order it came in, making sure things don't get mixed up.
If we need to analyse data that has a hierarchical structure, like sales by product categories,
we can use Tree Structures for the same.
By using these simple ways to organize our data, we'll be able to build a tool that's not only
easy to use but also efficient at understanding and analysing sales data for Sampath Food
City.
To design a suitable testing regime for the sales data analysis application, we need to ensure
comprehensive coverage of all components and functionalities.
1. Unit Testing
Unit testing is like checking each small part of the code to make sure it works correctly on
its own. We write tests for each function or method to see if they do what they're supposed
to do. In Python, we have tools like pytest to help us do this.
For example, in our sales data analysis program, we would test each part that deals with
getting or changing data, making sure it does its job correctly. We'd also test the analysis
part to make sure it gives us the right results when we give it some sample data. This way,
we catch mistakes early and make sure everything works smoothly in the end.
47 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Examples:
• Test each method in the SaleRepository class to ensure correct data
retrieval and manipulation.
• Test analysis strategies to verify that they produce accurate results for
sample input data.
2. Integration Testing
Integration testing is about checking how different parts of the application work together.
We want to make sure that when we put all these pieces together, they still do what they're
supposed to do. We test them to see if they achieve specific tasks when they're working
together.
Just like unit testing, we use tools to help us do this efficiently. These tools are similar to
the ones we use for unit testing. By doing integration testing, we ensure that our application
runs smoothly as a whole, with all its different parts working together seamlessly.
Examples:
• Test the interaction between the CLI layer and the analysis layer to ensure
that user inputs are processed correctly.
• Test how the data repository interacts with the analysis layer to provide
data for analysis.
3. End-to-End Testing
End-to-end testing is like giving the entire application a real life run through, from
beginning to end. We want to see how everything works together just like it would for a
user. To do this, we automate interactions with the application, mimicking what a user
would do.
We perform a series of actions, like clicking buttons or entering data, and then check if the
results match what we expected. For web-based applications, we might use tools like
Selenium, while for command-line applications, we could create our own scripts. End-to-
end testing helps us catch any issues that might arise when all parts of the application are
put together, ensuring it behaves as expected for users in real world scenarios.
Examples:
• Automate user interactions with the CLI to simulate different analysis
requests and verify the correctness of the results.
• Simulate data ingestion and analysis processes to ensure that data flows
smoothly through the system.
4. Regression Testing
Regression testing is all about making sure that when we make changes to the code, we
don't accidentally introduce new problems or mess up things that were working fine before.
The idea is to re-run all the tests that passed before, after we've made changes, to make sure
everything still works as expected.
48 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
We use the same tools we use for unit testing and integration testing to do this. By regularly
running regression tests, we can catch any unexpected issues early on and keep our
application running smoothly.
Examples:
• After implementing a new feature or fixing a bug, re-run all existing test
cases to ensure that the changes did not introduce any regressions.
• Use version control systems like Git to track changes and run automated
tests in continuous integration (CI) pipelines.
5. Performance Testing
Performance testing is about checking how well our application performs and how much it
can handle when lots of people are using it at the same time. We want to know things like
how fast it responds to user actions, how much computer resources it uses, and how it
behaves when lots of users are using it.
To do this, we use tools called load testing tools, like Apache JMeter, or we can create our
own scripts. These tools help us simulate lots of users using the application at once, so we
can see how it handles the load. By doing performance testing, we make sure our
application can handle whatever users throw at it without slowing down or crashing.
Examples:
• Simulate a large number of concurrent users interacting with the
application to identify performance bottlenecks and optimize code and
infrastructure accordingly.
• Measure response times for different analysis requests to ensure that the
application remains responsive even with increasing data volume.
6. Usability Testing
Usability testing is all about checking how easy and enjoyable it is for users to use our
application. We want to know if the user interface is clear and if the overall experience is
good. To do this, we can ask real users for feedback or use special tools made for usability
testing.
These tools, like UserTesting or UserZoom, help us see how real users interact with our
application and where they might have trouble. Alternatively, we can simply ask users for
their thoughts and feedback directly. By doing usability testing, we make sure our
application is user-friendly and enjoyable to use, which is super important for keeping users
happy.
Examples:
• Conduct surveys or interviews with users to gather feedback on the CLI
interface and identify any difficulties or confusion.
• Iterate on the CLI design based on user feedback to make it more intuitive
and user-friendly.
49 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Automated Testing
Automated testing is about making our testing process faster and more efficient by letting
computers do the work for us. We want to automate tasks that we'd otherwise have to do
manually over and over again. This helps us test more things, more often, and get feedback
on our code faster. To do this, we write scripts or use special testing frameworks that can
run our tests automatically.
We can then integrate these tests into our development process using continuous integration
(CI) tools like Jenkins, Travis CI, or GitLab CI. These tools automatically run our tests
whenever we make changes to our code, giving us quick feedback on whether everything
still works as expected. By automating our testing, we can catch bugs earlier and ensure
our code is always in good shape.
Examples:
• Set up CI pipelines that automatically run unit tests, integration tests, and
end-to-end tests whenever code changes are pushed to the repository.
• Use test automation frameworks to automate repetitive tasks, such as data
setup for testing or simulating user interactions.
The purpose of test data management is to make sure our tests use data that's reliable and
realistic. We want our tests to run with data that's consistent and looks like what the real
application would use. To do this, we can use mock data or special tools that generate test
data for us.
Mocking libraries like MagicMock for Python help us create fake data that resembles real
data. Alternatively, we can write our own scripts to generate test data that matches the
characteristics of the data our application would use. By using consistent and realistic test
data, we can ensure that our tests accurately reflect how our application would behave in
real-world scenarios.
Examples:
• Create mock sales data for unit tests to simulate different scenarios, such as
sales from multiple branches and products with varying prices and
quantities.
• Use test data generation tools to create large datasets for performance
testing, ensuring that tests are conducted with realistic data volumes.
50 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
2.3 Refine the Design and Include Multiple Design Patterns
Refining the design of the sales data analysis application for Sampath Food City and
incorporating multiple design patterns will enhance its structure, maintainability, and
scalability.
1. Repository Pattern
The Repository pattern is like having a dedicated manager for handling all the data stuff in
our application. It keeps the data-related tasks separate from the other parts of the
application.
So, instead of each part of the application managing its own data, we have this central
manager that takes care of storing and getting data for us. This makes it easier to keep track
of where the data is coming from and going to. Plus, it makes testing our application much
simpler because we only have one place to check for data-related issues.
Example: In our scenario, the SaleRepository class encapsulates methods for adding sales
records and querying sales data by branch or product.
2. Strategy Pattern
The Strategy pattern is like having different ways to solve a problem, and we can pick the
best one for the situation. Imagine we have different strategies for analyzing sales data: one
for monthly totals, one for product prices, and so on. Each strategy is like a different
approach to the same problem.
With the Strategy pattern, we encapsulate each strategy separately, making them easy to
swap in and out as needed. So, depending on what kind of analysis we want to do, we can
choose the right strategy at runtime. This flexibility allows us to adapt our analysis methods
without changing the main code.
3. Singleton Pattern
The Singleton pattern is like having a special rule that says there can only be one of
something. In our case, it's about having just one instance of a certain class in our whole
application. This can be really handy when we want to make sure that everyone in our
program is using the same version of something important, like a data repository.
By using the Singleton pattern, we ensure that no matter where in the program someone
asks for this thing, they always get the same one. It's like having a single point of contact
that everyone can rely on, making coordination between different parts of the program
easier.
Example: The SaleRepository class can be implemented as a singleton to ensure that there's
only one instance of the repository throughout the application, maintaining consistency in
data access.
51 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
4. Command Pattern
The Command pattern is like putting requests into neat little packages that we can handle
easily. Instead of directly doing something when a request comes in, we wrap that request
into an object. This object contains all the information needed to perform the request, like
what action to take and any data required.
By doing this, we can do lots of cool stuff with these requests, like putting them in queues
to be handled later, adding undo functionality to reverse actions, or logging them for
tracking purposes. It's like having a well-organized system for managing requests, making
it easier to handle and process them in different ways.
5. Facade Pattern
The Facade pattern is like having a front desk at a big building. It provides a simple and
friendly interface for people coming in, hiding all the complex stuff that's going on behind
the scenes. In our software, it's similar.
We create a facade, which is like a front desk, that hides all the complicated details of a
subsystem. So, when other parts of the program want to use that subsystem, they don't have
to deal with all the complexity directly. They just talk to the facade, and it takes care of
everything for them. This makes it much easier to use the subsystem without worrying
about how it actually works internally.
52 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Activity 3
To develop the sales data analysis system for Sampath Food City, the system can be broken
down into several key components. Each component will handle specific aspects of the
functionality required.
Responsibilities
• Handle recording and storing of sales data for each branch.
• Provide methods to retrieve and analyze sales data on a monthly basis.
Data Structures
• sales_data: Dictionary of Lists
Methods
• record_sale(branch, date, total_sales, items_sold)
• get_monthly_sales(branch, month, year)
Responsibilities
• Handle recording and storing of price data for each product.
• Provide methods to analyze price trends.
Data Structures
53 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Methods
• record_price(product, price)
• get_price_trend(product)
Responsibilities
• Analyze and store weekly sales data for the entire supermarket network.
• Provide methods to retrieve weekly sales data.
Data Structures
• weekly_sales: List of Dictionaries
Methods
• analyze_weekly_sales()
• get_weekly_sales(week_number)
Responsibilities
• Track and analyze the popularity of each product based on sales data.
• Provide methods to retrieve product preference data.
Data Structures
• product_preference: Dictionary
Methods
• record_product_preference(items_sold)
• get_product_preference()
54 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
5. Sales Distribution Analyzer
Responsibilities
• Analyze the distribution of total sales amounts across all transactions.
• Provide methods to retrieve sales distribution data.
Data Structures
• sales_distribution: List of Tuples
Methods
• record_sale_distribution(branch, date, total_sales)
• get_sales_distribution()
Responsibilities
• Provide a user interface for interacting with the system.
• Allow users to input data and request analyses through command-line commands.
Methods
• display_menu()
• handle_user_input()
55 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Rough implementation outline incorporating all these components
56 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 61 – Rough implementation outline of the application (Author created)
57 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Main interface & Sample inputs
This design organizes the application into clear, manageable components, making the
system more maintainable and scalable. Each component has a defined responsibility and
interacts with others through well-defined interfaces. The command-line interface provides
a way for users to interact with the system and perform various analyses based on the
collected sales data.
58 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
To export data to different formats like TXT, CSV, ROW, and HTML, We can add methods
in our CLI class to handle the export functionality for each format.
59 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
60 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
61 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 65 – Python code of the developed application (Author created)
62 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
3.2 Effectiveness of Development Principles and Patterns in our
Application Design
When dealing with large scale data processing, we follow certain principles and patterns to
ensure our code is high quality, justifiable, and scalable. These include principles like the
Liskov Substitution Principle (LSP), Interface Segregation Principle (ISP), Dependency
Inversion Principle (DIP), Open/Close Principle (OCP), and Single Responsibility
Principle (SRP), along with various design patterns.
SRP helps by breaking down our code into smaller, more understandable parts, each with
its own specific job. OCP allows us to easily add new processing methods without having
to change existing code. LSP encourages us to design our code so that different strategies
can be easily implemented and adapted. ISP helps us keep our code clean by tailoring
interfaces to meet specific needs, avoiding unnecessary complexity.
DIP makes testing and maintenance easier by reducing how much different parts of our
code rely on each other. Design patterns like the Builder pattern make it easier to create
and customize reports, while the Observer pattern allows for real-time adjustments during
data processing. The Strategy Pattern promotes code reuse and flexibility by letting us
easily switch between different processing methods.
By applying these principles and patterns together, we see significant benefits in terms of
better maintainability, flexibility, and scalability. Designing our code with a modular
approach in mind makes it simpler to make changes and updates in the future. Overall,
using SOLID principles and design patterns results in a cleaner, more effective codebase
for processing large data sets, which sets us up for success as our program grows and
evolves.
To make code easier to maintain and less prone to unexpected issues, the Single
Responsibility Principle (SRP) suggests that each class should have only one job. The
Open/Close Principle (OCP) helps maintain code stability by allowing us to add new
features without changing existing code. Liskov's Substitution Principle (LSP) ensures
smooth substitution and implementation of strategies by providing a consistent interface
for different data processing methods.
The Interface Segregation Principle (ISP) encourages a clean interface design and avoids
unnecessary dependencies by splitting interfaces into separate tasks. Dependency injection,
a realization of the Dependency Inversion Principle (DIP), allows higher-level modules to
depend on abstractions rather than specific implementations. This separation makes unit
testing with mock implementations easier and simplifies strategy replacement.
These principles have a significant impact on how software programs are designed and
function. They help ensure code is more modular, easier to maintain, and less likely to
cause unexpected problems.
63 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
The Impact of Clean Code Techniques on Software Development
We have already discussed about these previously. Applying clean code strategies is crucial
for enhancing readability, maintainability, and overall quality, especially in projects
dealing with large datasets. Principles such as DRY (Don't Repeat Yourself), concise
functions, robust error handling, thorough testing, efficient code, modularization,
consistent formatting, and clear naming significantly contribute to achieving these goals.
Breaking down the code into separate modules promotes maintainability, while clear
naming clarifies the purpose of each module. Consistent formatting, including proper
indentation and spacing, is maintained throughout the codebase. Thoughtful comments
provide additional context without cluttering the code. Using meaningful names instead of
hardcoded values improves code manageability and reduces errors, following the DRY
principle to minimize redundancy and isolate reusable elements.
To enhance readability and simplify testing, smaller, task-specific functions or methods are
preferred. Robust error-handling systems provide clear feedback and address potential
issues effectively. Comprehensive testing at multiple levels helps identify issues early,
improving program reliability. Adhering to clean code best practices significantly improves
understanding of the codebase, making it easier for developers to work with it.
To improve functionality, the program leverages specific design patterns. The strategy
pattern stands out for its ability to encapsulate algorithms and dynamically select them
based on client code requirements. This proves particularly useful for tasks like monthly
sales analysis and price analysis, where different strategies may be employed.
Although not explicitly implemented, the Singleton design pattern could be beneficial for
classes needing to maintain a single instance, such as Data Processing Context or Data
Reporting Context. By ensuring the existence of only one instance for each context class,
unnecessary instantiations are avoided, leading to more efficient resource utilization.
The Factory Method pattern offers enhanced flexibility by dynamically producing various
report types, which could facilitate future program development to accommodate additional
report types seamlessly.
The Observer design pattern is effective when system components need to respond to
changes, as often seen in logging or alerting systems. Integrating this pattern could be
valuable for notifications or logging during data processing, enabling system components
to react appropriately to alterations in data or system state.
64 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Activity 4
Automated testing means using tools and scripts to check if software works well. Instead
of people running tests and looking at results, computers do it automatically. This saves
time and effort, especially for tests that need to be done over and over again or take a lot of
resources.
Unit Testing
This check individual parts of the software, like functions or classes, to make sure they
work correctly on their own. For example, if we have a function that adds two numbers, we
test it to ensure it adds them up correctly. Tools like JUnit or pytest can run these tests
automatically.
Integration Testing
This tests how different parts of the software work together. For instance, if our program
has a front-end and a back-end, integration testing checks if they communicate properly.
Tools like Selenium or Postman can simulate user actions or API requests to automate these
tests.
Regression Testing
This makes sure that recent changes to the software haven't broken anything that was
working before. It automatically reruns test cases to catch any issues caused by new
updates. Tools like Selenium WebDriver or Apache JMeter can help with this.
Functional Testing
This checks if the software does what it's supposed to do based on its specifications. For
example, if we have a login page, functional testing verifies that users can log in
successfully. Tools like Cucumber or Robot Framework can run tests written in plain
language to automate this process.
Performance Testing
This tests how fast and stable the software is under different conditions, like heavy user
loads. Tools like Apache JMeter or Gatling simulate many users accessing the software at
once to measure its performance.
65 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Security Testing
This looks for security issues in the software, like vulnerabilities that hackers could exploit.
Automated tools like OWASP ZAP or Burp Suite scan the software for common security
problems automatically, such as injection attacks or cross-site scripting.
This automates the process of building, testing, and deploying software changes. Tools like
Jenkins or GitLab CI/CD can run automated tests whenever new code is added, ensuring
that the software remains stable and deployable.
PyUnit - It’s like a tool that comes with Python, and it helps us make and run tests. These
tests check if different parts of our code work well by themselves.
Nose2 - This is another tool for testing our code. It's nice because it can do many kinds of
tests, like checking if small parts of our code work, if different parts of our code work
together, and if our whole program works as expected. People like it because it's easy to
use and has many helpful features.
Doctest - Doctest is a simple way to test our code. We can write tests right inside the
explanations we put in our code. It's handy because we can make sure our code works
correctly while also making sure our explanations are right.
Robot Framework - This is a tool for testing our code in a special way. Instead of writing
tests in a hard-to-read format, we write them in a way that's easy for people to understand.
It's good for testing things like websites and making sure they work the way we want.
Behave - Behave is a tool for testing our code by writing down how we expect it to work.
It's like writing a story that describes what our code should do. This can help us make sure
our code does what we want it to do, from the perspective of someone using it.
Selenium - Selenium is like a helper for testing websites. It can pretend to be a person using
a web browser and check if a website works well. It's useful for making sure websites look
and act the way they're supposed to.
Splinter - Splinter is another helper for testing websites, but it's easier to use than Selenium.
It helps us do things on websites, like clicking buttons and filling out forms, so we can test
if they work correctly.
PyTest - PyTest is a tool for testing our code, and it's liked by many developers because
it's easy to understand and use. It helps us write different kinds of tests, like checking small
parts of our code, seeing if different parts work together, and making sure our program
works as expected.
66 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
4.2 Implement automatic testing of the developed application.
This involves selecting a tool or framework that allows us to define and execute test cases
for our application. We can choose from popular options like PyTest, Nose2, or unittest,
depending on our project requirements and familiarity with the framework.
67 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Refactor and Maintain Tests
Regularly refactor and maintain our test suite to keep it aligned with changes in our
application. This includes updating test cases to reflect changes in functionality, improving
test coverage, and removing obsolete tests.
Developer-produced automatic testing tools are those created and maintained by the
development team or organization responsible for building the software or application.
These tools are typically tailored to the specific needs and requirements of the project, and
they are often integrated seamlessly into the development process.
Vendor-provided automatic testing tools are developed and offered by third party vendors
who specialize in creating testing solutions for various software development projects.
These tools are commercially available and may offer a range of features and functionalities
to suit different types of applications and systems.
Customization
Tools made by developers are often tailored precisely to meet the needs and workflows of
the development team and the project. They're crafted to tackle the specific challenges and
requirements of the software being created. In contrast, tools provided by vendors are
typically more standardized and may not offer as much flexibility in terms of
customization.
Integration
Tools created by developers are usually smoothly integrated into the current development
setup and tools. They're designed to work closely with other tools like version control,
continuous integration servers, and issue tracking systems. On the other hand, vendor-
provided tools may need extra setup or integration work to fit well into the development
process.
Tools made by developers need the development team to handle support and maintenance
tasks. This means the team manages updates, fixes bugs, and provides support, often with
limited resources available to team members only. On the contrary, vendor-provided tools
come with dedicated support and maintenance services offered by the vendor. This includes
regular updates, technical assistance, and access to documentation and training materials.
68 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Cost
Tools made by developers might be cheaper at the start because they're built internally,
meaning there are no fees for using them. But there could be ongoing costs for keeping
them up and running. On the other hand, tools from vendors usually come with fees for
licensing or subscriptions, but they might have more features and support options available.
Developer-made tools are created to fit exactly what the development team and project
need. They have features and functions that are customized to match the unique
requirements of the software being built. On the flip side, vendor-provided tools have a
wider range of features to attract more users, but they might not fit the project's needs as
closely.
1. Unit Testing
Benefits
Unit testing helps find bugs in small parts of the software. It makes sure each piece works
correctly on its own. For example, in our app, we can check if adding a sale works without
any mistakes.
Drawbacks
Writing these tests can take a lot of time, especially for complex software. Also, they might
not cover every possible situation, leaving some parts untested.
2. Integration Testing
Benefits
Integration testing checks if different parts of the software work together. It finds problems
when they connect. For example, in our app, we can see if adding a sale and updating prices
work together without any issues.
Drawbacks
Setting up these tests can be hard, especially for big systems. Also, they might need access
to other things like databases, which can be tricky.
69 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
3. Regression Testing
Benefits
Regression testing makes sure changes to the software don't break anything that was
working before. It helps keep the software stable over time. For example, in our app, we
can check if changing how we add sales doesn't break anything else in the system.
Drawbacks
Doing these tests can take a long time, especially for big systems. Also, they might need to
be done a lot, which takes up a lot of resources.
4. Functional Testing
Benefits
Functional testing checks if the software does what it's supposed to do. It makes sure it
works like it should from the user's point of view. For example, in our app, we can see if
users can add sales and view reports like they're supposed to.
Drawbacks
These tests might miss some things users could do, leaving some parts untested. They can
also be hard to automate for parts of the software with complex behavior.
5.Performance Testing
Benefits
Performance testing checks how well the software works under different conditions, like
when a lot of people use it at once. It helps us find problems with speed and stability early
on. For example, in our app, we can check if it slows down when many sales happen at
once.
Drawbacks
Doing these tests needs special tools and setup, which can be hard. Also, it's not easy to
make performance issues happen the same way every time we test.
6. Security Testing
Benefits
Security testing looks for ways bad actors could break into the software and steal or mess
with data. It helps keep sensitive information safe and stops attacks before they happen.
For example, in our app, we can check if people can't get into parts they're not supposed to
access.
70 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Drawbacks
Finding these security problems needs special knowledge and tools, which not everyone
has. Also, it can take a long time, especially for complex systems.
Regression testing helps avoid repeating tests and saves time by focusing on important
areas. Performance testing tries to mimic how real users behave to see if the software can
handle it.
Using good practices in test automation, like running tests at the same time, and regularly
checking and changing the testing plan, helps save time and keep tests reliable. Cloud-
based testing lets us test in different setups and grow when needed. Checking the software
regularly helps find new problems and keeping an eye on it all the time makes sure it works
well.
Let’s employ Selected Testing Tools to Conduct Automatic Testing on our Developed
Application
In this test file, we import the SalesDataManager class and create a fixture sales_manager
to instantiate the SalesDataManager object for each test case. We then define two test
functions: test_record_sale to test the record_sale method and test_get_monthly_sales
to test the get_monthly_sales method. Within each test function, we perform assertions to
verify that the methods behave as expected. Finally, we run the tests using pytest.
71 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1
Figure 67 – Unit test code (Author created)
This code defines two test functions: test_record_sale and test_get_monthly_sales. Each
function tests a specific method of the SalesDataManager class by performing operations
and making assertions to ensure that the methods behave as expected. The pytest.fixture
decorator is used to create a fixture sales_manager that initializes a SalesDataManager
object for each test function. Finally, the tests can be run using pytest.
References
softwaretestinghelp [online]Available
at:https://www.softwaretestinghelp.com/polymorphism-in-java/ [Accessed 14.04.2024].
73 | P a g e
T M Dayarathna Unit 20 - Applied Programming and Design Principles
Assignment no 1