0% found this document useful (0 votes)
2 views63 pages

SE DesignPattern StudyNotes Part2

This document is a comprehensive study guide for the Software Engineering & Design Patterns Lab course at Metropolitan University Sylhet, covering essential topics such as Requirements Engineering, UML, System Architecture, and Project Planning. It includes detailed sections on functional and non-functional requirements, user stories, use cases, and various UML diagrams, as well as architectural patterns like MVC and microservices. Additionally, it provides a Work Breakdown Structure (WBS) for project management and outlines key design patterns for software development.

Uploaded by

TR Tasin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views63 pages

SE DesignPattern StudyNotes Part2

This document is a comprehensive study guide for the Software Engineering & Design Patterns Lab course at Metropolitan University Sylhet, covering essential topics such as Requirements Engineering, UML, System Architecture, and Project Planning. It includes detailed sections on functional and non-functional requirements, user stories, use cases, and various UML diagrams, as well as architectural patterns like MVC and microservices. Additionally, it provides a Work Breakdown Structure (WBS) for project management and outlines key design patterns for software development.

Uploaded by

TR Tasin
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Software Engineering &


Design Patterns Lab
Comprehensive Study Notes — Part 2

Course: CSE 0613 3332 | Credit Hours: 1.5


Metropolitan University Sylhet | Academic Year: 2023

This document, together with Part 1 (Lecture 01), covers the complete syllabus of CSE 0613 3332.
Master both parts and you are fully prepared for the lab final exam.

What This Book Covers


Part Topics Pages
Part A Requirements Engineering & UML — Quick Reference ~10 pages
Part B System Architecture, WBS, Gantt Charts, Agile Planning ~20 pages
Part C SQA, Testing Strategies, Unit & Integration Testing ~20 pages
Part D Reliability & Performance Metrics ~12 pages

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

PART A

Requirements Engineering & UML


Quick Reference & Exam Summary

NOTE Part A is a concise summary. Design Patterns are fully covered in Part 1. This section
ensures you have all concepts in one place for exam revision.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

📋 Requirements Engineering

A.1 What are Requirements?


Requirements describe what a software system should do (functional) and the qualities it must have
(non-functional). Capturing requirements correctly is the most critical step — errors here are the most
expensive to fix later.

Functional Requirements (FR)


Describe specific behaviour or functions the system must perform — what the system does.
• User can register with email and password.
• Admin can view all orders from the dashboard.
• System sends an email confirmation after successful payment.
• Students can submit assignments before the deadline.

Non-Functional Requirements (NFR)


Describe quality attributes of the system — how well the system performs its functions.

NFR Type Definition Example


Performance Speed & responsiveness Page loads in under 2 seconds.
Security Data protection & access control Passwords must be hashed with
bcrypt.
Scalability Handle growth in users/data System supports 10,000
concurrent users.
Reliability Uptime & fault tolerance 99.9% uptime guaranteed per
month.
Usability Ease of use New users complete onboarding in
under 5 minutes.
Maintainability Ease of change Code coverage must stay above
80%.

A.2 User Stories


User Story A short, plain-language description of a feature from the perspective of
an end user. Format: As a [role], I want [goal] so that [reason].

• As a student, I want to submit my assignment online so that I do not need to be physically


present.
• As an admin, I want to see all registered users so that I can manage accounts efficiently.
• As a buyer, I want to filter products by price so that I can find items within my budget.
CSE 0613 3332 | Academic Year 2023
Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

A.3 Use Cases


A Use Case describes a sequence of interactions between a user (Actor) and the system to accomplish
a goal. It is more detailed than a user story.

Component Meaning Example


Actor Who interacts with the system Student, Admin, Payment Gateway
Use Case What goal is achieved Submit Assignment, Process
Payment
Precondition What must be true before User must be logged in
Main Flow The normal successful steps 1. User clicks Submit 2. System
validates...
Alternative Flow What if something goes wrong If file too large, show error message
Postcondition System state after success Assignment recorded in database

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

📐 UML Diagrams — Quick Reference

UML (Unified Modeling Language) is the standard visual language for modelling software systems. The
course covers 7 key diagram types.

A.4 Use Case Diagram


Purpose Shows what the system does from the user's perspective — actors and
the use cases they interact with.

• Actors are drawn as stick figures (humans) or boxes (external systems).


• Use Cases are drawn as ovals inside a system boundary rectangle.
• «include» — a use case always includes another (e.g., Login always includes Validate
Credentials).
• «extend» — a use case sometimes extends another (e.g., Apply Discount optionally extends
Checkout).

A.5 Class Diagram


Purpose Shows the static structure of the system — classes, attributes, methods,
and relationships.

Relationship Symbol Meaning Example


Association Objects are linked Student — Course
Aggregation Whole has parts (parts can exist University has Departments
independently)
Composition Whole owns parts (parts cannot exist alone) House has Rooms
Inheritance Child extends parent Dog extends Animal
Dependency One class uses another OrderService uses
PaymentGateway

A.6 Sequence Diagram


Purpose Shows how objects interact in a specific scenario over time — the order
of messages.

• Objects are shown as vertical lifelines (boxes at top, dashed lines going down).
• Messages are horizontal arrows between lifelines.
• Activation boxes (thin rectangles) show when an object is active.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

• Return messages are shown as dashed arrows.


TIP Sequence diagrams are the most common exam question for UML — focus on reading and
drawing them correctly.

A.7 Activity Diagram


Purpose Shows the flow of control or data in a process — like a flowchart but for
OOP systems.

• Start state: filled black circle. End state: bull's-eye circle.


• Activities: rounded rectangles. Decisions: diamond shapes.
• Swim lanes: columns showing which actor performs which activity.

A.8 State Diagram


Purpose Shows all possible states an object can be in and the transitions between
states.

• States: rounded rectangles (e.g., Order: Pending → Processing → Shipped → Delivered).


• Transitions: arrows with event labels (e.g., payment_confirmed triggers → Processing).
• Guard conditions in brackets: [stock > 0].

A.9 Component & Deployment Diagrams


Diagram What It Shows Exam Focus
Component High-level software components and their Identify components and
interfaces — e.g., Frontend, Backend, provided/required
Database. interfaces.
Deployment How software is deployed on physical hardware Map software artifacts to
— servers, devices, containers. nodes (servers, devices).

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🧩 Design Patterns — Quick Reference Table

Full coverage of Singleton and all 23 GoF patterns is in Part 1. Use this table for rapid revision.

Creational Patterns
Pattern Intent Key Trick
Singleton One instance only; global access. Private constructor + static
getInstance().
Factory Method Subclass decides which object to Abstract creator method
create. overridden in subclasses.
Abstract Factory Create families of related objects. Factory of factories.
Builder Construct complex objects step by Separate construction from
step. representation.
Prototype Clone an existing object. Implement clone() method.

Structural Patterns
Pattern Intent Key Trick
Adapter Make incompatible interfaces work Wrapper class translates calls.
together.
Decorator Add behaviour dynamically without Wrap object in another object.
subclassing.
Facade Simplify a complex subsystem. One entry-point class hides
complexity.
Proxy Control access to an object. Surrogate object intercepts calls.
Composite Tree structures for part-whole Leaf and Composite share same
hierarchies. interface.
Bridge Separate abstraction from Two independent inheritance
implementation. hierarchies.
Flyweight Share many fine-grained objects to Shared immutable state (intrinsic)
save memory. vs unique state (extrinsic).

Behavioral Patterns
Pattern Intent Key Trick
Observer Notify dependents when state Subject keeps a list of observers
changes. and calls notify().

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Strategy Swap algorithms at runtime. Encapsulate each algorithm in its


own class.
Command Encapsulate requests as objects. Enables undo/redo and queuing.
Template Method Define skeleton; subclasses fill in Abstract base class with hook
steps. methods.
Iterator Traverse a collection uniformly. next() and hasNext() methods.
State Object behaviour changes with state. Delegate to current state object.
Chain of Pass request along a chain of Each handler decides to handle or
Responsibility handlers. forward.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

PART B
System Architecture & Project Planning
WBS • Gantt Charts • Agile Planning

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🏗️ System Architecture

Definition Software architecture is the high-level structure of a software system —


the set of major components (subsystems), their responsibilities, and how
they communicate with each other.

B.1 Why Architecture Matters


• Guides all subsequent design and implementation decisions.
• Makes it easier for teams to work on different parts in parallel.
• Enables non-functional requirements (performance, scalability, security) to be addressed early.
• Makes the system easier to maintain and evolve over time.

B.2 Common Architectural Patterns


An architectural pattern is a reusable solution to a commonly occurring design problem at the system
level.

1. Layered Architecture (N-Tier)


Organises the system into horizontal layers, each with a specific responsibility. Higher layers depend
on lower layers; no reverse dependency is allowed.

Layer Responsibility Technology Example


Presentation User interface — what the user sees and React, HTML/CSS
interacts with.
Business Logic Core application rules and workflows. Python (Flask/Django), Java
(Spring)
Data Access Database queries and data retrieval. SQLAlchemy, Hibernate,
JDBC
Database Persistent storage of data. PostgreSQL, MongoDB,
MySQL

TIP Layered architecture is the most commonly used pattern for web applications — expect
exam questions on it.

2. MVC — Model-View-Controller
Separates an application into three interconnected components, isolating the data (Model) from the
user interface (View) through a Controller.

Component Role Example

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Model Manages data, business rules, and database User class with save() method
interaction.
View Renders data for the user — the UI. HTML template / React
component
Controller Handles user input, updates Model, selects [Link]() method
View.

# MVC Example — Simple Python (Flask-style pseudocode)

# MODEL: manages data


class User:
def __init__(self, name, email):
[Link] = name
[Link] = email

def save(self):
# saves to database
pass

# VIEW: renders data (template)


# user_profile.html
# <h1>{{ [Link] }}</h1>
# <p>{{ [Link] }}</p>

# CONTROLLER: handles request


def user_profile(user_id):
user = [Link](user_id) # ask Model
return render('user_profile.html', user=user) # pass to View

3. Client-Server Architecture
The system is split into two roles: a Client that requests services, and a Server that provides them.
Communication happens over a network (typically HTTP/HTTPS).

• Client: web browser, mobile app, desktop app.


• Server: API server (REST/GraphQL), database server, file server.
• The client and server can be on different machines; they communicate via well-defined
protocols.

4. Microservices Architecture
The application is built as a collection of small, independent services, each responsible for a specific
business capability and deployable on its own.

Aspect Monolith (Traditional) Microservices


Deployment All in one deployable unit. Each service deployed
independently.
Scalability Scale entire app even for one Scale only the service that needs it.
bottleneck.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Failure One bug can crash everything. One service fails; others keep
running.
Technology Single tech stack. Each service can use different
languages/DBs.
Complexity Simpler to start. More complex infrastructure
needed.

IMPORTANT For your university project, use Layered Architecture (MVC). Microservices are
overkill for small systems and introduce unnecessary complexity.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

📊 Work Breakdown Structure (WBS)

Definition A hierarchical decomposition of the total scope of a project into smaller,


manageable components called work packages.

B.3 Why Use a WBS?


• Makes the project scope visible — nothing is hidden.
• Helps estimate time and cost more accurately.
• Defines clear responsibility for each piece of work.
• Acts as the backbone for the Gantt Chart and sprint planning.

B.4 Levels of a WBS


Level Name Example
Level 0 Project Name (root) Online Learning Platform
Level 1 Major Deliverables Frontend, Backend, Database, Testing,
Deployment
Level 2 Sub-deliverables Frontend → UI Design, Page Development,
Integration
Level 3 Work Packages Page Development → Login Page,
Dashboard, Course Page

B.5 Example WBS — University Web Project

1. Online Student Portal


1.1 Requirements & Planning
▸ 1.1.1 Gather functional requirements
▸ 1.1.2 Create user stories and use cases
▸ 1.1.3 Draw UML diagrams
1.2 Frontend Development
▸ 1.2.1 Design wireframes
▸ 1.2.2 Implement Login & Registration pages
▸ 1.2.3 Implement Dashboard
▸ 1.2.4 Implement Course Listing & Detail pages
1.3 Backend Development
▸ 1.3.1 Set up REST API (Flask/Django)
▸ 1.3.2 Implement Authentication (JWT)
▸ 1.3.3 Implement Course Management API
▸ 1.3.4 Implement Submission API
1.4 Database

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

▸ 1.4.1 Design Entity-Relationship (ER) diagram


▸ 1.4.2 Create schema and seed data
1.5 Testing & QA
▸ 1.5.1 Write unit tests
▸ 1.5.2 Integration testing
▸ 1.5.3 User acceptance testing (UAT)
1.6 Deployment
▸ 1.6.1 Configure server (DigitalOcean / Heroku)
▸ 1.6.2 CI/CD pipeline setup
▸ 1.6.3 Final demo and presentation

TIP In the exam, you may be asked to draw or complete a WBS for a given project. Always start
with the project name at Level 0 and decompose downward. Every item should be a noun
(deliverable), not a verb (action).

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

📅 Gantt Charts

Definition A Gantt Chart is a horizontal bar chart that visualises a project schedule
— showing tasks, their durations, and the start/end dates on a timeline.

B.6 Reading a Gantt Chart


Element Meaning
Rows Each row represents one task or work package.
Horizontal axis Timeline (days, weeks, or months).
Bars Duration of each task — start date to end date.
Milestones Diamond symbols marking key completion points.
Dependencies Arrows showing which tasks must finish before another starts.
Critical Path The longest chain of dependent tasks — any delay here delays the
whole project.

B.7 Gantt Chart — Example Schedule

Task Week 1 Week 2 Week 3 Week 4 Week 5 Week 6


Requirements ████████ ████████
& UML
Database ████████ ████████
Design
Backend API ████████ ████████

Frontend ████████ ████████


Development
Testing & QA ████████ ████████

Deployment ████████
& Demo

B.8 Key Concepts


• Dependency: Backend API cannot start until Database Design is complete.
• Parallel tasks: Frontend Development and Backend API overlap — different team members
work simultaneously.
• Milestone: 'Backend API Complete' before Frontend Integration begins.
• Critical Path: Requirements → Database → Backend → Frontend → Testing → Deployment.
Any delay in this chain delays the final delivery.
CSE 0613 3332 | Academic Year 2023
Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

NOTE For exam: know how to read a Gantt Chart (identify critical path, dependencies, task
duration) and explain what each element represents.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🔄 Agile Planning

B.9 What is Agile?


Agile is an iterative, incremental approach to software development. Instead of planning everything
upfront (as in Waterfall), Agile delivers software in short cycles called iterations or sprints, adapting to
change at each step.

The Agile Manifesto (2001) — 4 Core Values

We VALUE more... ...than


Individuals and interactions Processes and tools
Working software Comprehensive documentation
Customer collaboration Contract negotiation
Responding to change Following a plan

B.10 Scrum Framework


Scrum is the most popular Agile framework. It organises development into fixed-length Sprints (typically
2 weeks) and defines clear roles and ceremonies.

Scrum Roles
Role Responsibility
Product Owner Defines what to build; maintains and prioritises the Product Backlog.
Scrum Master Facilitates Scrum process; removes obstacles (impediments) for the team.
Development Team Self-organising team that builds the product; typically 3–9 people.

Scrum Artifacts
Artifact Description
Product Backlog Master list of all features, bug fixes, and work items ordered by priority.
Sprint Backlog Subset of Product Backlog committed to in the current Sprint.
Increment The working, tested software delivered at the end of each Sprint.

Scrum Ceremonies
Ceremony When Purpose

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Sprint Planning Start of each Team selects items from Product Backlog and plans
Sprint the Sprint.
Daily Standup Every day (15 Quick sync: What did I do? What will I do? Any
min max) blockers?
Sprint Review End of Sprint Demo working software to stakeholders; gather
feedback.
Sprint Retrospective After Sprint Reflect: What went well? What to improve? Actions
Review for next Sprint.

B.11 Agile vs Waterfall — Comparison


Aspect Waterfall Agile
Planning All upfront before any coding. Continuous; adapts each sprint.
Delivery One big release at the end. Working software every 2 weeks.
Change handling Expensive and disruptive. Expected and welcomed.
Customer involvement At start (requirements) and end Continuous throughout the project.
(delivery).
Risk High — problems discovered late. Low — problems surface early.
Documentation Heavy documentation before Just enough documentation.
coding.
Best suited for Fixed requirements, compliance- Evolving requirements, startups.
heavy.

NOTE The course uses Agile (Scrum) for the team project. Exam may ask you to compare
Waterfall vs Agile or describe Scrum ceremonies — know both well.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

PART C
Software Quality Assurance & Testing
SQA • Unit Testing • Integration Testing • Test Cases

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🔍 Software Quality Assurance (SQA)

SQA Definition A systematic process of ensuring that the software development process
and the final product meet defined quality standards. SQA is proactive —
it prevents defects from entering the system.

C.1 Quality vs Testing


Aspect Quality Assurance (QA) Testing (QC)
Focus Process — how we build the Product — checking if the software
software. works.
Goal Prevent defects. Find defects.
When Throughout the entire SDLC. At specific stages after code is
written.
Who Everyone on the team. Testers (and developers for unit
tests).
Examples Code reviews, pair programming, Unit tests, integration tests, system
standards. tests.

C.2 Software Quality Attributes (ISO 25010)


Quality Attribute Definition Bad Example
Functional Correctness System does what it is specified to do. Login accepts wrong
passwords.
Performance Efficiency Speed, resource usage, capacity. Page takes 10 seconds to
load.
Usability How easy it is to use. No error message when
login fails.
Reliability Ability to perform without failure over System crashes every 2
time. hours.
Security Protection from unauthorized access. SQL injection vulnerability in
login form.
Maintainability Ease of modification and extension. One 2,000-line function that
does everything.
Portability Ability to run on different environments. App only works on
Windows, not Linux.

C.3 Verification vs Validation

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Verification Validation
Question "Are we building the product right?" "Are we building the right product?"
Focus Process & specifications — does it End user needs — does it solve the
match the design? actual problem?
Method Code reviews, inspections, User acceptance testing, demos.
walkthroughs.
Example Does the login module match the Do users actually want to log in this
spec? way?

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🧪 Software Testing Strategies

Testing is the systematic process of executing a program to find defects. A good testing strategy uses
multiple levels and approaches.

C.4 The Testing Pyramid


Level Type Cost Speed Quantity
Top End-to-End / UI Tests High Slow Few
Middle Integration Tests Medium Medium Some
Bottom Unit Tests Low Fast Many

TIP The testing pyramid tells us to write MANY fast unit tests, SOME integration tests, and only
FEW end-to-end tests. This gives maximum coverage at minimum cost.

C.5 Black-Box vs White-Box Testing


Approach Description Tester Knows Technique
Black-Box Test based on specification Inputs & expected Equivalence
— no knowledge of outputs only. partitioning, boundary
internals. value analysis.
White-Box Test based on internal code Full source code. Branch coverage, path
structure. coverage, statement
coverage.
Grey-Box Partial knowledge of Some internal details. Integration testing, API
internals. testing.

C.6 Test Coverage


Test coverage measures how much of your code is exercised by your tests.

Coverage Type What It Measures Target


Statement Coverage Every line of code executed at least once. ≥ 70%
Branch Coverage Every if/else branch taken at least once. ≥ 80%
Function Coverage Every function called at least once. 100% recommended
Path Coverage Every possible execution path. Often impractical —
use selectively.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

⚙️ Unit Testing in Python

Unit Test A test that verifies the behaviour of a single, isolated unit of code —
typically a single function or method — in isolation from the rest of the
system.

C.7 Python Unit Testing with unittest


Python's built-in unittest module follows the xUnit testing framework style.

# [Link] — the code we want to test


class Calculator:

def add(self, a, b):


return a + b

def subtract(self, a, b):


return a - b

def multiply(self, a, b):


return a * b

def divide(self, a, b):


if b == 0:
raise ValueError('Cannot divide by zero')
return a / b

# test_calculator.py — unit tests


import unittest
from calculator import Calculator

class TestCalculator([Link]):

def setUp(self):
# setUp runs BEFORE every test method
[Link] = Calculator()

def test_add_two_positive_numbers(self):
result = [Link](3, 5)
[Link](result, 8) # assert 3+5 == 8

def test_add_negative_numbers(self):
result = [Link](-2, -3)
[Link](result, -5)

def test_subtract(self):
result = [Link](10, 4)
[Link](result, 6)

def test_multiply(self):
result = [Link](3, 4)
[Link](result, 12)

def test_divide_normal(self):
result = [Link](10, 2)
[Link](result, 5.0)

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

def test_divide_by_zero_raises_error(self):
# This test checks that an exception IS raised
with [Link](ValueError):
[Link](10, 0)

if __name__ == '__main__':
[Link]()

# Run tests: python -m unittest test_calculator.py


# Output: ......
# Ran 6 tests in 0.001s OK

C.8 Common Assertion Methods


Method What It Checks Example
assertEqual(a, b) a equals b assertEqual(add(2,3), 5)
assertNotEqual(a, b) a does not equal b assertNotEqual(result, 0)
assertTrue(x) x is truthy assertTrue(user.is_active)
assertFalse(x) x is falsy assertFalse(user.is_banned)
assertIsNone(x) x is None assertIsNone(find_user(999))
assertIsNotNone(x) x is not None assertIsNotNone(db_connection)
assertRaises(Exc, fn) calling fn raises exception assertRaises(ValueError, divide,
1, 0)
assertIn(a, b) a is in collection b assertIn('admin', roles)

C.9 Test-Driven Development (TDD)


TDD is a development practice where you write the test BEFORE you write the code. The cycle is: Red
→ Green → Refactor.

Step Action Status


1. Red Write a failing test for a new feature. Test fails — code doesn't exist yet.
2. Green Write the minimum code to make the test pass. Test passes — code may be messy.
3. Clean up the code without breaking the test. Test still passes — code is clean.
Refactor

# TDD Example — implementing a stack

# Step 1: RED — write failing test first


class TestStack([Link]):
def test_push_and_pop(self):
stack = Stack()
[Link](10)
[Link]([Link](), 10) # FAILS — Stack doesn't exist

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet
# Step 2: GREEN — write minimum code
class Stack:
def __init__(self):
self._items = []
def push(self, item):
self._items.append(item)
def pop(self):
return self._items.pop()

# Step 3: REFACTOR — add is_empty(), peek() etc. cleanly

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🔗 Integration Testing

Integration Test Tests that verify that multiple units (modules, classes, or services) work
correctly together. It checks the interactions and data flow between
components.

C.10 Unit Test vs Integration Test


Aspect Unit Test Integration Test
Scope Single function/class in isolation. Two or more components working
together.
Speed Very fast (milliseconds). Slower (real DB, real API calls).
Dependencies All dependencies are Real dependencies used.
mocked/stubbed.
Purpose Does my function logic work? Do my components work together?
Example Does calculate_discount() return Does the checkout flow (API →
10%? Service → DB) work?

C.11 Integration Testing Example — User Registration


This example tests the full flow: API endpoint → Service layer → Database.

# user_service.py
class UserService:

def __init__(self, db):


[Link] = db # database connection injected

def register(self, username, email):


if not username or not email:
raise ValueError('Username and email are required')
if '@' not in email:
raise ValueError('Invalid email format')
user = {'username': username, 'email': email}
[Link]('users', user)
return user

def find_user(self, email):


return [Link]('users', {'email': email})

# test_user_integration.py
import unittest
from user_service import UserService

# We use a simple in-memory 'fake' database for integration tests


class FakeDatabase:
def __init__(self):
[Link] = {}

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet
def insert(self, table, record):
[Link](table, []).append(record)

def query(self, table, filter_dict):


records = [Link](table, [])
return [r for r in records
if all([Link](k) == v for k, v in filter_dict.items())]

class TestUserRegistrationIntegration([Link]):

def setUp(self):
[Link] = FakeDatabase()
[Link] = UserService([Link]) # inject the fake DB

def test_register_saves_user_to_database(self):
[Link]('alice', 'alice@[Link]')
results = [Link].find_user('alice@[Link]')
[Link](len(results), 1)
[Link](results[0]['username'], 'alice')

def test_duplicate_query_returns_nothing_for_wrong_email(self):
[Link]('bob', 'bob@[Link]')
results = [Link].find_user('wrong@[Link]')
[Link](len(results), 0)

def test_invalid_email_raises_error(self):
with [Link](ValueError):
[Link]('charlie', 'not-an-email')

C.12 Writing Formal Test Cases


A test case is a documented specification of inputs, execution conditions, testing procedure, and
expected results for a single scenario.

Field Description Example


Test Case ID Unique identifier. TC-LOGIN-001
Title Short descriptive name. Valid login with correct credentials
Preconditions What must be true before the test. User 'alice' exists in the database.
Test Steps Exact steps to perform the test. 1. Navigate to /login 2. Enter valid
email 3. Enter valid password 4.
Click Submit
Test Data The specific inputs used. Email: alice@[Link], Password:
Secure123!
Expected Result What should happen if the feature User is redirected to /dashboard.
works.
Actual Result What actually happened (filled after User redirected to /dashboard. ✓
test).
Status Pass / Fail / Blocked. Pass

Boundary Value Analysis (BVA)

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Most bugs occur at the edges (boundaries) of input ranges. BVA says to test the minimum, maximum,
and just-outside-boundary values.

# Example: password must be 8–20 characters


#
# Test these boundary values:
# 7 chars → INVALID (just below minimum)
# 8 chars → VALID (minimum boundary)
# 9 chars → VALID (just above minimum)
# 20 chars → VALID (maximum boundary)
# 21 chars → INVALID (just above maximum)

def test_password_boundary_values(self):
[Link](is_valid_password('abc123!')) # 7 chars — invalid
[Link](is_valid_password('abc123!!')) # 8 chars — valid
[Link](is_valid_password('abc123abc')) # 9 chars — valid
[Link](is_valid_password('a' * 20)) # 20 chars — valid
[Link](is_valid_password('a' * 21)) # 21 chars — invalid

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

PART D
Software Reliability & Performance Metrics
Latency • Throughput • Scalability • Maintainability

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

🔒 Software Reliability

Reliability The probability that a software system will perform its required functions
under stated conditions for a specified period of time without failure.

D.1 Key Reliability Metrics


Metric Full Name Definition Formula
MTTF Mean Time to Average time a system runs MTTF = Total Operating
Failure before its first failure. Time ÷ Number of Failures
MTBF Mean Time Average time between two MTBF = MTTF + MTTR
Between Failures consecutive failures (for
repairable systems).
MTTR Mean Time to Average time to detect, MTTR = Total Downtime ÷
Repair diagnose, and fix a failure. Number of Repairs
Availability % of time system is Higher is better (target 99.9% = Availability = MTBF ÷
operational. ~8.7 hrs downtime/year). (MTBF + MTTR)

D.2 The 'Nines' of Availability


Availability Level Downtime Per Year Typical Use Case
99% (2 nines) ~87.6 hours Internal tools, non-critical apps.
99.9% (3 nines) ~8.76 hours Most web apps and SaaS products.
99.99% (4 nines) ~52.6 minutes E-commerce, banking systems.
99.999% (5 nines) ~5.26 minutes Telecom, emergency services,
hospitals.

# Python example: calculate reliability metrics

failures = [10, 20, 15, 12] # hours each run lasted before failure
repair_times = [2, 1, 3, 2] # hours each repair took

mttf = sum(failures) / len(failures)


mttr = sum(repair_times) / len(repair_times)
mtbf = mttf + mttr
avail = (mtbf / (mtbf + mttr)) * 100

print(f'MTTF: {mttf:.1f} hours') # average time before failure


print(f'MTTR: {mttr:.1f} hours') # average repair time
print(f'MTBF: {mtbf:.1f} hours') # average time between failures
print(f'Availability: {avail:.2f}%') # % of time system is up

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

⚡ Performance Metrics

Performance metrics quantify how well a system responds under various load conditions.
Understanding these is essential for building systems that work in the real world.

D.3 Latency
Latency The time delay between a request being made and the first response
being received. Also called response time.

Latency Type Definition Measurement


Network Latency Time for data to travel between client Typically measured in
and server. milliseconds (ms).
Processing Latency Time for the server to process the CPU time, DB query time,
request. business logic time.
Total Response Time Network + Processing + Rendering. Perceived latency by the end
user.

# Python: measure function execution time (processing latency)


import time

def slow_operation():
[Link](0.05) # simulates a 50ms database call
return 'result'

def measure_latency(func, runs=10):


times = []
for _ in range(runs):
start = time.perf_counter()
func()
end = time.perf_counter()
[Link]((end - start) * 1000) # convert to ms

avg = sum(times) / len(times)


p95 = sorted(times)[int(len(times) * 0.95)] # 95th percentile

print(f'Average Latency : {avg:.2f} ms')


print(f'P95 Latency : {p95:.2f} ms')
return avg

measure_latency(slow_operation)
# Output:
# Average Latency : 50.12 ms
# P95 Latency : 51.04 ms

TIP Always measure P95 (95th percentile) latency, not just the average. The average hides
outliers — P95 tells you what 95% of your users experience.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

D.4 Throughput
Throughput The number of requests (or transactions) a system can process per unit
of time. Measured in requests/second (RPS), transactions/second (TPS),
or MB/second.

# Python: simple throughput measurement


import time

def process_request(item):
# simulates work (e.g., calculating a value)
result = item * item
return result

def measure_throughput(num_requests=1000):
start = time.perf_counter()

for i in range(num_requests):
process_request(i)

duration = time.perf_counter() - start


throughput = num_requests / duration

print(f'Processed : {num_requests} requests')


print(f'Duration : {duration:.3f} seconds')
print(f'Throughput: {throughput:.0f} requests/second')

measure_throughput(1000)
# Output:
# Processed : 1000 requests
# Duration : 0.002 seconds
# Throughput: ~500,000 requests/second

Relationship Explanation
Latency ↑ and Throughput ↓ High latency means each request takes longer, so fewer can be
processed per second.
Throughput ↑ with parallelism Handling multiple requests simultaneously (threads, async)
increases throughput without reducing per-request latency.
Bottleneck effect If the database is slow, both latency and throughput suffer
regardless of how fast the API is.

D.5 Scalability
Scalability The ability of a system to handle increased load (users, data, requests)
by adding resources, while maintaining acceptable performance.

Vertical Scaling (Scale Up)


Add more power (CPU, RAM, faster disk) to the existing server.
Pros Cons

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Simple — no code changes needed. Has a physical limit — you can only make one
server so powerful.
Less infrastructure complexity. Expensive at high specs.
Good for databases. Single point of failure — if that server goes down,
everything is down.

Horizontal Scaling (Scale Out)


Add more servers and distribute the load between them using a load balancer.
Pros Cons
No theoretical limit — add as many servers as Requires a load balancer.
needed.
Fault tolerant — if one server dies, others serve Application must be stateless (or use shared
the traffic. session store).
Cost effective with commodity hardware. More complex infrastructure and deployment.

# Scalability concept illustrated in Python


# Simulating how more 'workers' increase throughput (horizontal scale)

import time
from [Link] import ThreadPoolExecutor

def handle_request(request_id):
[Link](0.01) # each request takes 10ms
return f'Response {request_id}'

def run_with_workers(num_workers, num_requests=100):


start = time.perf_counter()
with ThreadPoolExecutor(max_workers=num_workers) as executor:
list([Link](handle_request, range(num_requests)))
duration = time.perf_counter() - start
print(f'{num_workers:2d} workers: {duration:.2f}s '
f'({num_requests/duration:.0f} req/s)')

run_with_workers(1, 100) # 1 worker: ~1.0s, 100 req/s


run_with_workers(4, 100) # 4 workers: ~0.25s, 400 req/s
run_with_workers(10, 100) # 10 workers:~0.10s, 1000 req/s

D.6 Maintainability
Maintainability The ease with which a software system can be modified — corrected,
improved, or adapted — after delivery. High maintainability reduces the
cost of future changes.

ISO 25010 Sub-Characteristics of Maintainability


Sub-Characteristic Definition Example of Improvement

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

Modularity System composed of independent Use classes and separate


components. modules instead of one giant file.
Reusability Components can be used in other Write generic utility functions,
contexts. not one-off scripts.
Analysability Ease of diagnosing problems and Good comments, clear naming,
understanding impact of changes. logging, error messages.
Modifiability Ease of implementing changes Follow Open/Closed Principle —
without introducing defects. extend don't modify.
Testability Ease of creating tests to verify the Small functions with one
system works. responsibility (Single
Responsibility Principle).

Code Metrics for Maintainability


Metric Definition Good Target
Cyclomatic Complexity Number of independent paths ≤ 10 per function.
through the code. Higher = harder to
test and understand.
Code Coverage % of code exercised by tests. ≥ 80%
Lines of Code (LOC) Size of a single function/class. Functions < 30 lines; classes
< 300 lines.
Coupling How many other modules a module Low coupling — ideally < 5
depends on. direct dependencies.
Cohesion How focused a module's High cohesion — each class
responsibilities are. does one thing well.

# Maintainability example: Low cohesion vs High cohesion

# ❌ BAD — one class does too many unrelated things (low cohesion)
class AppManager:
def save_user(self, user): pass # database concern
def send_email(self, msg): pass # email concern
def calculate_tax(self, amount): pass # finance concern
def render_html(self, template): pass # UI concern

# ✅ GOOD — each class has a single, clear responsibility (high cohesion)


class UserRepository:
def save(self, user): pass
def find_by_email(self, email): pass

class EmailService:
def send(self, recipient, subject, body): pass

class TaxCalculator:
def calculate(self, amount, rate): return amount * rate

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

📝 Appendix
Complete python code
"""
╔══════════════════════════════════════════════════════════════════╗
║ Software Engineering & Design Patterns Lab ║
║ CSE 0613 3332 — Metropolitan University Sylhet ║
║ All Runnable Code Examples (Single File) ║
║ ║
║ HOW TO RUN: ║
║ python se_design_patterns_all_examples.py ║
╚══════════════════════════════════════════════════════════════════╝
"""

import unittest
import time
from [Link] import ThreadPoolExecutor
from abc import ABC, abstractmethod
import copy

# ──────────────────────────────────────────────────────────────────
# HELPER: Section printer
# ──────────────────────────────────────────────────────────────────
def section(title):
print("\n" + "═" * 60)
print(f" {title}")
print("═" * 60)

# ══════════════════════════════════════════════════════════════════
# PART 1 — DESIGN PATTERNS (Python Implementations)
# ══════════════════════════════════════════════════════════════════

section("PART 1: DESIGN PATTERNS")

# ──────────────────────────────────────────────────────────────────
# 1.1 SINGLETON PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Ensure a class has only ONE instance and provide a
# global point of access to it.
#
# Real-world use: Database connection, Logger, Config Manager

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# ──────────────────────────────────────────────────────────────────

print("\n--- 1.1 Singleton Pattern ---")

class DatabaseConnection:
# Step 1: class-level variable to hold the single instance
_instance = None

# Step 2: __new__ controls object creation in Python


def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
print("[DB] Connection established (created for the first
time).")
return cls._instance

def query(self, sql):


print(f"[DB] Executing: {sql}")

# --- Client Code ---


db1 = DatabaseConnection()
db2 = DatabaseConnection()
db3 = DatabaseConnection()

print(f"db1 is db2? {db1 is db2}") # True — same object


print(f"db2 is db3? {db2 is db3}") # True — same object

[Link]("SELECT * FROM users")


[Link]("SELECT * FROM courses")

# ──────────────────────────────────────────────────────────────────
# 1.2 FACTORY METHOD PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Define an interface for creating an object, but let
# subclasses decide which class to instantiate.
#
# Real-world use: Different notification types (Email, SMS, Push)
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.2 Factory Method Pattern ---")

# Abstract product

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

class Notification(ABC):
@abstractmethod
def send(self, message):
pass

# Concrete products
class EmailNotification(Notification):
def send(self, message):
print(f"[EMAIL] Sending: {message}")

class SMSNotification(Notification):
def send(self, message):
print(f"[SMS] Sending: {message}")

class PushNotification(Notification):
def send(self, message):
print(f"[PUSH] Sending: {message}")

# Factory — decides which object to create


class NotificationFactory:
@staticmethod
def create(notification_type):
if notification_type == "email":
return EmailNotification()
elif notification_type == "sms":
return SMSNotification()
elif notification_type == "push":
return PushNotification()
else:
raise ValueError(f"Unknown type: {notification_type}")

# --- Client Code ---


# Client doesn't need to know WHICH class is instantiated
for ntype in ["email", "sms", "push"]:
notif = [Link](ntype)
[Link]("Your order has been shipped!")

# ──────────────────────────────────────────────────────────────────
# 1.3 OBSERVER PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Define a one-to-many dependency so that when one object
# changes state, all dependents are notified automatically.

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

#
# Real-world use: Event systems, UI frameworks, stock price alerts
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.3 Observer Pattern ---")

class Subject:
"""The object being watched (also called 'Publisher')"""
def __init__(self):
self._observers = []
self._state = None

def attach(self, observer):


self._observers.append(observer)

def detach(self, observer):


self._observers.remove(observer)

def notify(self):
for observer in self._observers:
[Link](self._state)

def set_state(self, state):


print(f"\n[Subject] State changed to: {state}")
self._state = state
[Link]() # automatically notify all observers

class Observer(ABC):
@abstractmethod
def update(self, state):
pass

class EmailAlert(Observer):
def update(self, state):
print(f" [EmailAlert] Received update: {state}")

class Dashboard(Observer):
def update(self, state):
print(f" [Dashboard] Refreshing UI with: {state}")

class Logger(Observer):
def update(self, state):
print(f" [Logger] Logging state: {state}")

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# --- Client Code ---


stock = Subject()
[Link](EmailAlert())
[Link](Dashboard())
[Link](Logger())

stock.set_state("PRICE: 150 BDT")


stock.set_state("PRICE: 165 BDT")

# ──────────────────────────────────────────────────────────────────
# 1.4 STRATEGY PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Define a family of algorithms, encapsulate each one,
# and make them interchangeable at RUNTIME.
#
# Real-world use: Sorting algorithms, payment methods, compression
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.4 Strategy Pattern ---")

class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount):
pass

class BkashPayment(PaymentStrategy):
def pay(self, amount):
print(f"[bKash] Paying {amount} BDT via bKash.")

class CardPayment(PaymentStrategy):
def pay(self, amount):
print(f"[Card] Paying {amount} BDT via Credit/Debit Card.")

class CashPayment(PaymentStrategy):
def pay(self, amount):
print(f"[Cash] Paying {amount} BDT in Cash on Delivery.")

class ShoppingCart:
def __init__(self):
self._items = []

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

self._payment_strategy = None

def add_item(self, name, price):


self._items.append((name, price))

def set_payment_strategy(self, strategy):


# Switch strategy at runtime — no if/else needed here
self._payment_strategy = strategy

def checkout(self):
total = sum(price for _, price in self._items)
print(f"\n[Cart] Total: {total} BDT")
self._payment_strategy.pay(total)

# --- Client Code ---


cart = ShoppingCart()
cart.add_item("Python Book", 500)
cart.add_item("USB Cable", 150)

cart.set_payment_strategy(BkashPayment())
[Link]()

cart.set_payment_strategy(CardPayment()) # switch at runtime


[Link]()

# ──────────────────────────────────────────────────────────────────
# 1.5 DECORATOR PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Attach additional responsibilities to an object
# DYNAMICALLY — a flexible alternative to subclassing.
#
# Real-world use: Adding toppings to coffee, middleware layers,
# file compression + encryption
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.5 Decorator Pattern ---")

class Coffee(ABC):
@abstractmethod
def cost(self):
pass

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

@abstractmethod
def description(self):
pass

class SimpleCoffee(Coffee):
def cost(self):
return 30

def description(self):
return "Simple Coffee"

# Base decorator
class CoffeeDecorator(Coffee):
def __init__(self, coffee):
self._coffee = coffee # wraps the original object

def cost(self):
return self._coffee.cost()

def description(self):
return self._coffee.description()

# Concrete decorators — each adds something


class MilkDecorator(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 10

def description(self):
return self._coffee.description() + " + Milk"

class SugarDecorator(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 5

def description(self):
return self._coffee.description() + " + Sugar"

class VanillaDecorator(CoffeeDecorator):
def cost(self):
return self._coffee.cost() + 20

def description(self):
return self._coffee.description() + " + Vanilla"

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# --- Client Code ---


coffee = SimpleCoffee()
print(f"{[Link]()} => {[Link]()} BDT")

coffee = MilkDecorator(coffee)
print(f"{[Link]()} => {[Link]()} BDT")

coffee = SugarDecorator(coffee)
print(f"{[Link]()} => {[Link]()} BDT")

coffee = VanillaDecorator(coffee)
print(f"{[Link]()} => {[Link]()} BDT")

# ──────────────────────────────────────────────────────────────────
# 1.6 COMMAND PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Encapsulate a request as an object — enables undo/redo,
# queuing, and logging of requests.
#
# Real-world use: Undo/Redo in text editors, remote controls,
# task queues
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.6 Command Pattern ---")

# Receiver — the actual object that does the work


class TextEditor:
def __init__(self):
self._text = ""

def write(self, text):


self._text += text
print(f"[Editor] Text is now: '{self._text}'")

def delete(self, num_chars):


self._text = self._text[:-num_chars]
print(f"[Editor] Text is now: '{self._text}'")

def get_text(self):
return self._text

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# Command interface
class Command(ABC):
@abstractmethod
def execute(self):
pass

@abstractmethod
def undo(self):
pass

# Concrete commands
class WriteCommand(Command):
def __init__(self, editor, text):
self._editor = editor
self._text = text

def execute(self):
self._editor.write(self._text)

def undo(self):
self._editor.delete(len(self._text))

# Invoker — holds and executes commands, manages undo history


class CommandHistory:
def __init__(self):
self._history = []

def execute(self, command):


[Link]()
self._history.append(command)

def undo(self):
if self._history:
command = self._history.pop()
[Link]()
print("[Undo applied]")
else:
print("[Nothing to undo]")

# --- Client Code ---


editor = TextEditor()
history = CommandHistory()

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

[Link](WriteCommand(editor, "Hello "))


[Link](WriteCommand(editor, "World"))
[Link](WriteCommand(editor, "!!!"))
[Link]() # undo "!!!"
[Link]() # undo "World"

# ──────────────────────────────────────────────────────────────────
# 1.7 ADAPTER PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Convert the interface of a class into another interface
# that clients expect — make incompatible interfaces work.
#
# Real-world use: Third-party API integration, legacy code,
# plug adapters (UK → BD socket)
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.7 Adapter Pattern ---")

# Existing system (the interface your code expects)


class BDPaymentGateway:
def pay_in_bdt(self, amount_bdt):
print(f"[BD Gateway] Processing {amount_bdt} BDT")

# Third-party system you want to use (incompatible interface)


class InternationalStripeAPI:
def charge_in_usd(self, amount_usd):
print(f"[Stripe API] Charging ${amount_usd:.2f} USD")

# Adapter — wraps Stripe to look like BDPaymentGateway


class StripeAdapter(BDPaymentGateway):
USD_RATE = 110 # 1 USD = 110 BDT

def __init__(self):
self._stripe = InternationalStripeAPI()

def pay_in_bdt(self, amount_bdt):


amount_usd = amount_bdt / self.USD_RATE
self._stripe.charge_in_usd(amount_usd) # translates the call

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# --- Client Code ---


# Client only knows about BDPaymentGateway interface
def checkout(gateway, amount):
gateway.pay_in_bdt(amount)

local = BDPaymentGateway()
stripe = StripeAdapter()

checkout(local, 500) # local gateway


checkout(stripe, 1100) # Stripe via Adapter — client code unchanged!

# ──────────────────────────────────────────────────────────────────
# 1.8 FACADE PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Provide a simplified, unified interface to a
# complex subsystem.
#
# Real-world use: Home theater system, compiler frontend,
# e-commerce order flow
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.8 Facade Pattern ---")

# Complex subsystem classes


class InventorySystem:
def check_stock(self, product):
print(f" [Inventory] Checking stock for '{product}'... OK")
return True

class PaymentSystem:
def process(self, amount):
print(f" [Payment] Processing payment of {amount} BDT... OK")
return True

class ShippingSystem:
def schedule(self, product, address):
print(f" [Shipping] Scheduling delivery of '{product}' to
{address}... OK")

class EmailSystem:
def send_confirmation(self, email):
print(f" [Email] Confirmation sent to {email}")

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# Facade — one simple method hides all the complexity


class OrderFacade:
def __init__(self):
self._inventory = InventorySystem()
self._payment = PaymentSystem()
self._shipping = ShippingSystem()
self._email = EmailSystem()

def place_order(self, product, amount, address, email):


print(f"\n[Order] Placing order for '{product}'...")
if not self._inventory.check_stock(product):
print(" [Order] Failed — out of stock.")
return
if not self._payment.process(amount):
print(" [Order] Failed — payment declined.")
return
self._shipping.schedule(product, address)
self._email.send_confirmation(email)
print("[Order] Successfully placed!")

# --- Client Code ---


# Client calls ONE method — doesn't know about Inventory/Payment/etc.
facade = OrderFacade()
facade.place_order("Python Textbook", 800, "Sylhet, Bangladesh",
"student@[Link]")

# ──────────────────────────────────────────────────────────────────
# 1.9 TEMPLATE METHOD PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Define the SKELETON of an algorithm in a base class,
# deferring some steps to subclasses.
#
# Real-world use: Data parsing pipelines, report generation,
# game AI (plan → execute → evaluate)
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.9 Template Method Pattern ---")

class DataReport(ABC):
# Template method — defines the fixed algorithm skeleton
def generate(self):

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

self.fetch_data() # step 1 (fixed)


self.process_data() # step 2 (subclass decides)
self.format_output() # step 3 (subclass decides)
[Link]() # step 4 (fixed)

def fetch_data(self):
print(" [Template] Fetching data from database...")

@abstractmethod
def process_data(self):
pass

@abstractmethod
def format_output(self):
pass

def save(self):
print(" [Template] Saving report to disk...")

class PDFReport(DataReport):
def process_data(self):
print(" [PDF] Calculating totals and statistics...")

def format_output(self):
print(" [PDF] Rendering charts and tables for PDF...")

class CSVReport(DataReport):
def process_data(self):
print(" [CSV] Cleaning and normalizing data...")

def format_output(self):
print(" [CSV] Converting data to comma-separated format...")

# --- Client Code ---


print("\nGenerating PDF Report:")
PDFReport().generate()

print("\nGenerating CSV Report:")


CSVReport().generate()

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# ──────────────────────────────────────────────────────────────────
# 1.10 ITERATOR PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Provide a uniform way to traverse elements of a
# collection WITHOUT exposing its underlying structure.
#
# Note: Python has this built-in via __iter__ and __next__
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.10 Iterator Pattern ---")

class StudentCollection:
def __init__(self):
self._students = []

def add(self, name):


self._students.append(name)

# Makes the collection iterable (Python Iterator Protocol)


def __iter__(self):
self._index = 0
return self

def __next__(self):
if self._index < len(self._students):
student = self._students[self._index]
self._index += 1
return student
raise StopIteration # tells Python the iteration is done

# --- Client Code ---


roll_list = StudentCollection()
roll_list.add("Alice")
roll_list.add("Bob")
roll_list.add("Charlie")

for student in roll_list: # uses __iter__ and __next__ automatically


print(f" Student: {student}")

# ──────────────────────────────────────────────────────────────────
# 1.11 STATE PATTERN
# ──────────────────────────────────────────────────────────────────

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# Intent: Allow an object to alter its behaviour when its


# internal state changes — appears to change its class.
#
# Real-world use: Order status, traffic lights, vending machines
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.11 State Pattern ---")

class OrderState(ABC):
@abstractmethod
def next_state(self, order):
pass

@abstractmethod
def describe(self):
pass

class PendingState(OrderState):
def describe(self):
return "PENDING"

def next_state(self, order):


print(" [State] Payment confirmed → Moving to PROCESSING")
order.set_state(ProcessingState())

class ProcessingState(OrderState):
def describe(self):
return "PROCESSING"

def next_state(self, order):


print(" [State] Package ready → Moving to SHIPPED")
order.set_state(ShippedState())

class ShippedState(OrderState):
def describe(self):
return "SHIPPED"

def next_state(self, order):


print(" [State] Delivered → Moving to DELIVERED")
order.set_state(DeliveredState())

class DeliveredState(OrderState):
def describe(self):
return "DELIVERED"

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

def next_state(self, order):


print(" [State] Order already delivered. No further transitions.")

class Order:
def __init__(self):
self._state = PendingState()

def set_state(self, state):


self._state = state

def advance(self):
print(f"\n Current state: {self._state.describe()}")
self._state.next_state(self)

# --- Client Code ---


order = Order()
[Link]() # PENDING → PROCESSING
[Link]() # PROCESSING → SHIPPED
[Link]() # SHIPPED → DELIVERED
[Link]() # Already delivered

# ──────────────────────────────────────────────────────────────────
# 1.12 PROXY PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Provide a surrogate object that controls access to
# another object (add access control, caching, logging).
#
# Real-world use: CDN caching, access control, lazy loading
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.12 Proxy Pattern ---")

class FileReader(ABC):
@abstractmethod
def read(self, filename):
pass

class RealFileReader(FileReader):
def read(self, filename):
print(f" [RealFileReader] Reading '{filename}' from disk...")

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

return f"Contents of {filename}"

class CachingProxyFileReader(FileReader):
"""Proxy that caches file contents — avoids re-reading from disk."""
def __init__(self):
self._real_reader = RealFileReader()
self._cache = {}

def read(self, filename):


if filename in self._cache:
print(f" [Proxy Cache] Returning cached '{filename}' (no disk
read!)")
return self._cache[filename]
content = self._real_reader.read(filename)
self._cache[filename] = content
return content

# --- Client Code ---


reader = CachingProxyFileReader()
print([Link]("[Link]")) # reads from disk
print([Link]("[Link]")) # served from cache
print([Link]("[Link]")) # reads from disk (new file)
print([Link]("[Link]")) # cache again

# ──────────────────────────────────────────────────────────────────
# 1.13 COMPOSITE PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Compose objects into TREE structures to represent
# part-whole hierarchies.
#
# Real-world use: File system (folders + files), UI component tree,
# org charts
# ──────────────────────────────────────────────────────────────────

print("\n--- 1.13 Composite Pattern ---")

class FileSystemItem(ABC):
def __init__(self, name):
[Link] = name

@abstractmethod
def get_size(self):

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

pass

@abstractmethod
def display(self, indent=0):
pass

class File(FileSystemItem):
def __init__(self, name, size):
super().__init__(name)
self._size = size

def get_size(self):
return self._size

def display(self, indent=0):


print(" " * indent + f" {[Link]} ({self._size} KB)")

class Folder(FileSystemItem):
def __init__(self, name):
super().__init__(name)
self._children = []

def add(self, item):


self._children.append(item)

def get_size(self):
return sum(child.get_size() for child in self._children)

def display(self, indent=0):


print(" " * indent + f" {[Link]}/ ({self.get_size()} KB)")
for child in self._children:
[Link](indent + 1)

# --- Client Code ---


root = Folder("root")
docs = Folder("documents")
pics = Folder("pictures")

[Link](File("[Link]", 120))
[Link](File("[Link]", 850))
[Link](File("[Link]", 300))
[Link](File("[Link]", 450))

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

[Link](docs)
[Link](pics)
[Link](File("[Link]", 5))

[Link]()
print(f"Total size: {root.get_size()} KB")

# ══════════════════════════════════════════════════════════════════
# PART 2 — UNIT TESTING (unittest)
# ══════════════════════════════════════════════════════════════════

section("PART 2: UNIT TESTING WITH unittest")

# ──────────────────────────────────────────────────────────────────
# The class we want to test — Calculator
# ──────────────────────────────────────────────────────────────────

class Calculator:
def add(self, a, b):
return a + b

def subtract(self, a, b):


return a - b

def multiply(self, a, b):


return a * b

def divide(self, a, b):


if b == 0:
raise ValueError("Cannot divide by zero")
return a / b

def is_even(self, n):


return n % 2 == 0

# ──────────────────────────────────────────────────────────────────
# Unit Tests for Calculator
# ──────────────────────────────────────────────────────────────────

class TestCalculator([Link]):

def setUp(self):

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# setUp() runs BEFORE every single test method


[Link] = Calculator()
print(f"\n [setUp] Calculator ready for: {self._testMethodName}")

# --- add() tests ---


def test_add_two_positives(self):
[Link]([Link](3, 5), 8)

def test_add_negative_numbers(self):
[Link]([Link](-2, -3), -5)

def test_add_zero(self):
[Link]([Link](10, 0), 10)

# --- subtract() tests ---


def test_subtract_normal(self):
[Link]([Link](10, 4), 6)

def test_subtract_result_negative(self):
[Link]([Link](3, 10), -7)

# --- multiply() tests ---


def test_multiply_two_positives(self):
[Link]([Link](3, 4), 12)

def test_multiply_by_zero(self):
[Link]([Link](99, 0), 0)

# --- divide() tests ---


def test_divide_normal(self):
[Link]([Link](10, 2), 5.0)

def test_divide_by_zero_raises_error(self):
# assertRaises checks that a specific exception IS raised
with [Link](ValueError):
[Link](10, 0)

# --- is_even() tests ---


def test_is_even_true(self):
[Link]([Link].is_even(4))

def test_is_even_false(self):
[Link]([Link].is_even(7))

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

# --- Boundary Value Analysis example ---


# Password must be 8 to 20 characters
def test_boundary_values_for_password_length(self):
def is_valid_password_length(pwd):
return 8 <= len(pwd) <= 20

[Link](is_valid_password_length("abc123!")) # 7 chars
— invalid
[Link](is_valid_password_length("abc123!!")) # 8 chars
— valid (min boundary)
[Link](is_valid_password_length("abc123abc")) # 9 chars
— valid
[Link](is_valid_password_length("a" * 20)) # 20 chars
— valid (max boundary)
[Link](is_valid_password_length("a" * 21)) # 21 chars
— invalid

# ══════════════════════════════════════════════════════════════════
# PART 3 — INTEGRATION TESTING
# ══════════════════════════════════════════════════════════════════

section("PART 3: INTEGRATION TESTING")

# ──────────────────────────────────────────────────────────────────
# UserService: depends on a database
# ──────────────────────────────────────────────────────────────────

class UserService:
def __init__(self, db):
[Link] = db # database is injected (Dependency Injection)

def register(self, username, email):


if not username or not email:
raise ValueError("Username and email are required")
if "@" not in email:
raise ValueError("Invalid email format")
user = {"username": username, "email": email}
[Link]("users", user)
return user

def find_user(self, email):


return [Link]("users", {"email": email})

def count_users(self):

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

return [Link]("users")

# ──────────────────────────────────────────────────────────────────
# FakeDatabase: in-memory substitute (no real DB needed for tests)
# ──────────────────────────────────────────────────────────────────

class FakeDatabase:
def __init__(self):
[Link] = {}

def insert(self, table, record):


[Link](table, []).append(record)

def query(self, table, filter_dict):


records = [Link](table, [])
return [r for r in records
if all([Link](k) == v for k, v in filter_dict.items())]

def count(self, table):


return len([Link](table, []))

# ──────────────────────────────────────────────────────────────────
# Integration Tests for UserService + FakeDatabase working together
# ──────────────────────────────────────────────────────────────────

class TestUserServiceIntegration([Link]):

def setUp(self):
# Fresh database for every test — tests are independent
[Link] = FakeDatabase()
[Link] = UserService([Link])

def test_register_stores_user_in_database(self):
[Link]("alice", "alice@[Link]")
results = [Link].find_user("alice@[Link]")
[Link](len(results), 1)
[Link](results[0]["username"], "alice")

def test_register_multiple_users(self):
[Link]("alice", "alice@[Link]")
[Link]("bob", "bob@[Link]")
[Link]([Link].count_users(), 2)

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

def test_find_user_wrong_email_returns_empty(self):
[Link]("bob", "bob@[Link]")
result = [Link].find_user("wrong@[Link]")
[Link](result, [])

def test_register_invalid_email_raises_error(self):
with [Link](ValueError):
[Link]("charlie", "not-an-email")

def test_register_empty_username_raises_error(self):
with [Link](ValueError):
[Link]("", "test@[Link]")

def test_register_empty_email_raises_error(self):
with [Link](ValueError):
[Link]("dave", "")

# ══════════════════════════════════════════════════════════════════
# PART 4 — RELIABILITY & PERFORMANCE METRICS
# ══════════════════════════════════════════════════════════════════

section("PART 4: RELIABILITY & PERFORMANCE METRICS")

# ──────────────────────────────────────────────────────────────────
# 4.1 Reliability Metrics: MTTF, MTTR, MTBF, Availability
# ──────────────────────────────────────────────────────────────────

print("\n--- 4.1 Reliability Metrics ---")

def calculate_reliability(failure_times, repair_times):


"""
failure_times : list of hours each system ran before failing
repair_times : list of hours each repair took
"""
mttf = sum(failure_times) / len(failure_times)
mttr = sum(repair_times) / len(repair_times)
mtbf = mttf + mttr
availability = (mtbf / (mtbf + mttr)) * 100

print(f" MTTF (Mean Time to Failure) : {mttf:.1f} hours")


print(f" MTTR (Mean Time to Repair) : {mttr:.1f} hours")
print(f" MTBF (Mean Time Btw Failures): {mtbf:.1f} hours")

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

print(f" Availability : {availability:.2f}%")

if availability >= 99.99:


print(" Rating: 4-nines — Enterprise grade ")
elif availability >= 99.9:
print(" Rating: 3-nines — Production grade ")
elif availability >= 99.0:
print(" Rating: 2-nines — Acceptable ")
else:
print(" Rating: Below 99% — Needs improvement ")

return mttf, mttr, mtbf, availability

failure_times = [50, 45, 60, 55, 48] # hours before each failure
repair_times = [2, 1, 3, 2, 2] # hours each repair took

calculate_reliability(failure_times, repair_times)

# ──────────────────────────────────────────────────────────────────
# 4.2 Latency Measurement
# ──────────────────────────────────────────────────────────────────

print("\n--- 4.2 Latency Measurement ---")

def simulate_db_query():
"""Simulates a database query taking ~50ms"""
[Link](0.05)
return {"id": 1, "name": "Alice"}

def measure_latency(func, runs=10):


times = []
for _ in range(runs):
start = time.perf_counter()
func()
end = time.perf_counter()
[Link]((end - start) * 1000) # convert to ms

times_sorted = sorted(times)
avg = sum(times) / len(times)
minimum = times_sorted[0]
maximum = times_sorted[-1]

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

p95_idx = int(len(times) * 0.95)


p95 = times_sorted[min(p95_idx, len(times)-1)]

print(f" Runs : {runs}")


print(f" Average : {avg:.2f} ms")
print(f" Min : {minimum:.2f} ms")
print(f" Max : {maximum:.2f} ms")
print(f" P95 : {p95:.2f} ms ← 95% of requests finish by this
time")

if avg < 100:


print(" Status: Excellent response time ")
elif avg < 500:
print(" Status: Acceptable response time ")
else:
print(" Status: Too slow — optimisation needed ")

measure_latency(simulate_db_query, runs=10)

# ──────────────────────────────────────────────────────────────────
# 4.3 Throughput Measurement
# ──────────────────────────────────────────────────────────────────

print("\n--- 4.3 Throughput Measurement ---")

def process_request(item):
"""Simulates processing one request"""
result = item * item # simple computation
return result

def measure_throughput(num_requests=1000):
start = time.perf_counter()

for i in range(num_requests):
process_request(i)

duration = time.perf_counter() - start


throughput = num_requests / duration

print(f" Requests processed : {num_requests}")


print(f" Duration : {duration:.4f} seconds")
print(f" Throughput : {throughput:,.0f} requests/second")

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

measure_throughput(5000)

# ──────────────────────────────────────────────────────────────────
# 4.4 Scalability — Horizontal Scaling with Thread Workers
# ──────────────────────────────────────────────────────────────────

print("\n--- 4.4 Scalability (Horizontal Scaling) ---")

def handle_request(request_id):
"""Each request takes 10ms (simulates a web request)"""
[Link](0.01)
return f"Response-{request_id}"

def benchmark_workers(num_workers, num_requests=20):


start = time.perf_counter()
with ThreadPoolExecutor(max_workers=num_workers) as executor:
list([Link](handle_request, range(num_requests)))
duration = time.perf_counter() - start
throughput = num_requests / duration
print(f" Workers: {num_workers:2d} | "
f"Duration: {duration:.2f}s | "
f"Throughput: {throughput:.0f} req/s")

print(" Simulating horizontal scaling (more workers = more throughput):")


benchmark_workers(1, num_requests=20)
benchmark_workers(4, num_requests=20)
benchmark_workers(10, num_requests=20)
print(" → Adding more workers increases throughput (horizontal scale-out)")

# ──────────────────────────────────────────────────────────────────
# 4.5 Maintainability — Low vs High Cohesion
# ──────────────────────────────────────────────────────────────────

print("\n--- 4.5 Maintainability: Cohesion & SRP ---")

# BAD: Low Cohesion — one class does too many unrelated things
class BadAppManager:
"""Violates Single Responsibility Principle (SRP)"""
def save_user(self, user):
print(" Saving user to DB...") # database concern

def send_email(self, msg):

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

print(" Sending email...") # email concern

def calculate_tax(self, amount):


return amount * 0.15 # finance concern

def render_html(self, template):


return f"<html>{template}</html>" # UI concern

# GOOD: High Cohesion — each class has ONE clear responsibility


class UserRepository:
"""Only handles database operations for users"""
def save(self, user):
print(f" [UserRepository] Saving user: {user}")

def find_by_email(self, email):


return {"email": email, "name": "Found User"}

class EmailService:
"""Only handles email sending"""
def send(self, recipient, subject, body):
print(f" [EmailService] Email → {recipient}: {subject}")

class TaxCalculator:
"""Only handles tax calculations"""
def calculate(self, amount, rate=0.15):
tax = amount * rate
print(f" [TaxCalculator] Tax on {amount} = {tax:.2f}")
return tax

# --- Demo ---


print("\n BAD approach (one class does everything):")
bad = BadAppManager()
bad.save_user("Alice")
bad.send_email("Hello")
bad.calculate_tax(1000)

print("\n GOOD approach (each class has one job — SRP):")


repo = UserRepository()
mail = EmailService()
tax = TaxCalculator()

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

[Link]("Alice")
[Link]("alice@[Link]", "Welcome!", "Thanks for registering.")
[Link](1000)

# ══════════════════════════════════════════════════════════════════
# RUN ALL UNIT TESTS
# ══════════════════════════════════════════════════════════════════

section("RUNNING ALL UNIT TESTS")

print("\nRunning TestCalculator & TestUserServiceIntegration...\n")

loader = [Link]()
suite = [Link]()
[Link]([Link](TestCalculator))
[Link]([Link](TestUserServiceIntegration))

runner = [Link](verbosity=2)
result = [Link](suite)

print("\n" + "═" * 60)


if [Link]():
print(f" ALL {[Link]} TESTS PASSED")
else:
print(f" {len([Link])} FAILED, {len([Link])}
ERRORS")
print("═" * 60)

print("""
╔══════════════════════════════════════════════════════════════════╗
║ END OF FILE ║
║ ║
║ Patterns covered: ║
║ Singleton, Factory Method, Observer, Strategy, Decorator, ║
║ Command, Adapter, Facade, Template Method, Iterator, ║
║ State, Proxy, Composite ║
║ ║
║ Testing covered: ║
║ Unit Testing (unittest), Integration Testing, BVA ║
║ ║
║ Metrics covered: ║
║ MTTF/MTTR/MTBF/Availability, Latency (P95), ║
║ Throughput, Scalability, Maintainability (SRP) ║

CSE 0613 3332 | Academic Year 2023


Software Engineering & Design Patterns Lab | Comprehensive Study Notes — Part 2 Metropolitan University Sylhet

╚══════════════════════════════════════════════════════════════════╝
""")

Good luck! Study smart, code clean, test everything.


Questions? Post in the course forum or email before the lab final.

CSE 0613 3332 | Academic Year 2023

You might also like