SE DesignPattern StudyNotes Part2
SE DesignPattern StudyNotes Part2
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.
PART A
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.
📋 Requirements Engineering
UML (Unified Modeling Language) is the standard visual language for modelling software systems. The
course covers 7 key diagram types.
• 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.
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().
PART B
System Architecture & Project Planning
WBS • Gantt Charts • Agile Planning
🏗️ System Architecture
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.
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.
def save(self):
# saves to database
pass
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).
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.
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.
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).
📅 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.
Deployment ████████
& Demo
NOTE For exam: know how to read a Gantt Chart (identify critical path, dependencies, task
duration) and explain what each element represents.
🔄 Agile Planning
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
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.
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.
PART C
Software Quality Assurance & Testing
SQA • Unit Testing • Integration Testing • Test Cases
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.
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?
Testing is the systematic process of executing a program to find defects. A good testing strategy uses
multiple levels and approaches.
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.
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.
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)
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]()
🔗 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.
# user_service.py
class UserService:
# test_user_integration.py
import unittest
from user_service import UserService
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')
Most bugs occur at the edges (boundaries) of input ranges. BVA says to test the minimum, maximum,
and just-outside-boundary values.
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
PART D
Software Reliability & Performance Metrics
Latency • Throughput • Scalability • Maintainability
🔒 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.
failures = [10, 20, 15, 12] # hours each run lasted before failure
repair_times = [2, 1, 3, 2] # hours each repair took
⚡ 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.
def slow_operation():
[Link](0.05) # simulates a 50ms database call
return 'result'
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.
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.
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)
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.
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.
import time
from [Link] import ThreadPoolExecutor
def handle_request(request_id):
[Link](0.01) # each request takes 10ms
return f'Response {request_id}'
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.
# ❌ 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
class EmailService:
def send(self, recipient, subject, body): pass
class TaxCalculator:
def calculate(self, amount, rate): return amount * rate
📝 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)
# ══════════════════════════════════════════════════════════════════
# ──────────────────────────────────────────────────────────────────
# 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
# ──────────────────────────────────────────────────────────────────
class DatabaseConnection:
# Step 1: class-level variable to hold the single instance
_instance = None
# ──────────────────────────────────────────────────────────────────
# 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)
# ──────────────────────────────────────────────────────────────────
# Abstract product
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}")
# ──────────────────────────────────────────────────────────────────
# 1.3 OBSERVER PATTERN
# ──────────────────────────────────────────────────────────────────
# Intent: Define a one-to-many dependency so that when one object
# changes state, all dependents are notified automatically.
#
# Real-world use: Event systems, UI frameworks, stock price alerts
# ──────────────────────────────────────────────────────────────────
class Subject:
"""The object being watched (also called 'Publisher')"""
def __init__(self):
self._observers = []
self._state = None
def notify(self):
for observer in self._observers:
[Link](self._state)
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}")
# ──────────────────────────────────────────────────────────────────
# 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
# ──────────────────────────────────────────────────────────────────
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 = []
self._payment_strategy = None
def checkout(self):
total = sum(price for _, price in self._items)
print(f"\n[Cart] Total: {total} BDT")
self._payment_strategy.pay(total)
cart.set_payment_strategy(BkashPayment())
[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
# ──────────────────────────────────────────────────────────────────
class Coffee(ABC):
@abstractmethod
def cost(self):
pass
@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()
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"
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
# ──────────────────────────────────────────────────────────────────
def get_text(self):
return self._text
# 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))
def undo(self):
if self._history:
command = self._history.pop()
[Link]()
print("[Undo applied]")
else:
print("[Nothing to undo]")
# ──────────────────────────────────────────────────────────────────
# 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)
# ──────────────────────────────────────────────────────────────────
def __init__(self):
self._stripe = InternationalStripeAPI()
local = BDPaymentGateway()
stripe = StripeAdapter()
# ──────────────────────────────────────────────────────────────────
# 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
# ──────────────────────────────────────────────────────────────────
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}")
# ──────────────────────────────────────────────────────────────────
# 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)
# ──────────────────────────────────────────────────────────────────
class DataReport(ABC):
# Template method — defines the fixed algorithm skeleton
def generate(self):
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...")
# ──────────────────────────────────────────────────────────────────
# 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__
# ──────────────────────────────────────────────────────────────────
class StudentCollection:
def __init__(self):
self._students = []
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
# ──────────────────────────────────────────────────────────────────
# 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"
class ProcessingState(OrderState):
def describe(self):
return "PROCESSING"
class ShippedState(OrderState):
def describe(self):
return "SHIPPED"
class DeliveredState(OrderState):
def describe(self):
return "DELIVERED"
class Order:
def __init__(self):
self._state = PendingState()
def advance(self):
print(f"\n Current state: {self._state.describe()}")
self._state.next_state(self)
# ──────────────────────────────────────────────────────────────────
# 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
# ──────────────────────────────────────────────────────────────────
class FileReader(ABC):
@abstractmethod
def read(self, filename):
pass
class RealFileReader(FileReader):
def read(self, filename):
print(f" [RealFileReader] Reading '{filename}' from disk...")
class CachingProxyFileReader(FileReader):
"""Proxy that caches file contents — avoids re-reading from disk."""
def __init__(self):
self._real_reader = RealFileReader()
self._cache = {}
# ──────────────────────────────────────────────────────────────────
# 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
# ──────────────────────────────────────────────────────────────────
class FileSystemItem(ABC):
def __init__(self, name):
[Link] = name
@abstractmethod
def get_size(self):
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
class Folder(FileSystemItem):
def __init__(self, name):
super().__init__(name)
self._children = []
def get_size(self):
return sum(child.get_size() for child in self._children)
[Link](File("[Link]", 120))
[Link](File("[Link]", 850))
[Link](File("[Link]", 300))
[Link](File("[Link]", 450))
[Link](docs)
[Link](pics)
[Link](File("[Link]", 5))
[Link]()
print(f"Total size: {root.get_size()} KB")
# ══════════════════════════════════════════════════════════════════
# PART 2 — UNIT TESTING (unittest)
# ══════════════════════════════════════════════════════════════════
# ──────────────────────────────────────────────────────────────────
# The class we want to test — Calculator
# ──────────────────────────────────────────────────────────────────
class Calculator:
def add(self, a, b):
return a + b
# ──────────────────────────────────────────────────────────────────
# Unit Tests for Calculator
# ──────────────────────────────────────────────────────────────────
class TestCalculator([Link]):
def setUp(self):
def test_add_negative_numbers(self):
[Link]([Link](-2, -3), -5)
def test_add_zero(self):
[Link]([Link](10, 0), 10)
def test_subtract_result_negative(self):
[Link]([Link](3, 10), -7)
def test_multiply_by_zero(self):
[Link]([Link](99, 0), 0)
def test_divide_by_zero_raises_error(self):
# assertRaises checks that a specific exception IS raised
with [Link](ValueError):
[Link](10, 0)
def test_is_even_false(self):
[Link]([Link].is_even(7))
[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
# ══════════════════════════════════════════════════════════════════
# ──────────────────────────────────────────────────────────────────
# UserService: depends on a database
# ──────────────────────────────────────────────────────────────────
class UserService:
def __init__(self, db):
[Link] = db # database is injected (Dependency Injection)
def count_users(self):
return [Link]("users")
# ──────────────────────────────────────────────────────────────────
# FakeDatabase: in-memory substitute (no real DB needed for tests)
# ──────────────────────────────────────────────────────────────────
class FakeDatabase:
def __init__(self):
[Link] = {}
# ──────────────────────────────────────────────────────────────────
# 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)
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
# ══════════════════════════════════════════════════════════════════
# ──────────────────────────────────────────────────────────────────
# 4.1 Reliability Metrics: 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
# ──────────────────────────────────────────────────────────────────
def simulate_db_query():
"""Simulates a database query taking ~50ms"""
[Link](0.05)
return {"id": 1, "name": "Alice"}
times_sorted = sorted(times)
avg = sum(times) / len(times)
minimum = times_sorted[0]
maximum = times_sorted[-1]
measure_latency(simulate_db_query, runs=10)
# ──────────────────────────────────────────────────────────────────
# 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)
measure_throughput(5000)
# ──────────────────────────────────────────────────────────────────
# 4.4 Scalability — Horizontal Scaling with Thread Workers
# ──────────────────────────────────────────────────────────────────
def handle_request(request_id):
"""Each request takes 10ms (simulates a web request)"""
[Link](0.01)
return f"Response-{request_id}"
# ──────────────────────────────────────────────────────────────────
# 4.5 Maintainability — Low vs High Cohesion
# ──────────────────────────────────────────────────────────────────
# 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
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
[Link]("Alice")
[Link]("alice@[Link]", "Welcome!", "Thanks for registering.")
[Link](1000)
# ══════════════════════════════════════════════════════════════════
# RUN ALL UNIT TESTS
# ══════════════════════════════════════════════════════════════════
loader = [Link]()
suite = [Link]()
[Link]([Link](TestCalculator))
[Link]([Link](TestUserServiceIntegration))
runner = [Link](verbosity=2)
result = [Link](suite)
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) ║
╚══════════════════════════════════════════════════════════════════╝
""")