0% found this document useful (0 votes)
31 views92 pages

Cos 202 Note

The document provides an overview of UML Class Diagrams, detailing their components, benefits, and how they aid in understanding object-oriented system design. It also covers inheritance in programming, including types of inheritance and the concept of polymorphism, encapsulation, and access modifiers in Python. Additionally, it explains the significance of constructors and the use of 'self' in class methods.

Uploaded by

ooafonrinwo
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)
31 views92 pages

Cos 202 Note

The document provides an overview of UML Class Diagrams, detailing their components, benefits, and how they aid in understanding object-oriented system design. It also covers inheritance in programming, including types of inheritance and the concept of polymorphism, encapsulation, and access modifiers in Python. Additionally, it explains the significance of constructors and the use of 'self' in class methods.

Uploaded by

ooafonrinwo
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

UML Class Diagram

Understanding Object-Oriented
System Design

Dr. Olaniyan Julius


What is a UML Class Diagram?
A UML Class Diagram is a visual representation of a system’s
classes, attributes, methods, and relationships.

It is used to model the properties and behaviours of Objects

Helps in designing and understanding object- oriented


systems.

Used in software engineering to model real- world entities.


Benefits of UML Class Diagram
1. Visual Representation of Structure – Helps in understanding and
designing the relationships between classes in an object-oriented
system.
2. Improves Communication – Provides a clear and standardized
way for developers, designers, and stakeholders to discuss
system architecture.
3. Facilitates Code Implementation – Serves as a blueprint for
developers to write clean and structured code based on the
diagram.
4. Enhances System Maintainability – Makes it easier to modify,
update, or extend the system without breaking existing
functionality.
5. Supports Software Documentation – Acts as an essential part of
system documentation, helping new team members understand
the system quickly.
Components of a UML Class Diagram
• A standard class diagram is composed of three components,
as illustrated in Figure 30.1:
• Upper section: It is the class name section. It is a required
section of the class diagram.
• Middle section: It is the class's attributes section. It
provides the names and types of the fields (variables). This
section can be optional; it is only needed when describing
the objects from a class.
• Bottom section: It is the class's methods section. It is
displayed in list format, with each method taking up its line.
It contains the method's name, type, and parameters. This
method describes how a class interacts with data.
UML Class Syntax
ClassName
- attribute1: Type
- attribute2: Type
+ method1(): Return
+ method2(): Return
Example - Car Class

Car
- brand: String
- model: String
- year: int
+ startEngine()
+ stopEngine()
Class visibility or Access modifiers
Class access level depends on the visibility. Below
are the access levels with their corresponding
modifier symbols:
ü Public (+): Denotes public attributes or methods.
ü Private (-): Denotes private attributes or methods.
ü Protected (#): Denotes protected attributes or
methods.
ü Package (~): Denotes packages (collection of class
libraries).
ü Derived (/): Denotes a derived class.
ü Static (underline): Denotes a static class.
UML Class Relationships
Various relationships and links exist between
classes and objects. A class may relate to one or
more classes. The relationship types are
presented in UML using the following
UML Class Relationships
Association
• A structural connection between two classes. It
describes how objects of one class relate to objects
of another class. Associations can have multiplicities
(e.g., one-to-one, one-to-many, many-to-many) and
can be unidirectional (one class knows about the
other) or bidirectional (both classes know about
each other).
• Example: Association Between "Person" and "Car"
• Consider a scenario where a Person owns a Car. This is a one-to-
one association.
– "Student" and "Course" Relationship
» A Student enrolls in a Course.
» A Course has multiple Students.

• This is a many-to-many bidirectional association.


Inheritance
• Dog and Cat inherit from Animal:
• Animal ▲ Dog
• Animal ▲ Cat

• It represents a relationship between a


subclass and a superclass, where the subclass
inherits both properties and methods of the
superclass, often with additional properties
and/or methods unique to the subclass.
Realization
It is the relationship that exists between the blueprint class
and its instance (object) containing its respective
implementation-level details. The object is said to realize the
blueprint class. This relationship is indicated by a dotted line
with a closed (filled) arrowhead
Dependency
This relationship exists when one class depends on
another class to function.
Symbolized by a dotted line with an opened
arrowhead pointing towards the other class
It means that one class temporarily uses another
class but does not own it. E.g car uses fuel
temporarily but does not permanently own it.
(Car------>Fuel)
Aggregation
It is a special association between two classes
where the object of class A owns another
object of class B. That is, class B is part of
class A.
For example, Library & Books: A library has
books, but books can exist independently.
It is indicated as a solid line with an unfilled
diamond arrowhead pointing at the class that
represents the aggregation.
Composition
Composition is a special type of aggregation,
while in aggregation, objects belonging to class 1
a n d c l a s s 2 h a v e s e p a ra t e l i f e t i m e s . I n
composition, both objects might have identical
or nearly the same lifetimes.
House & Rooms: If the house is deleted, rooms
must be deleted too.
Composition is symbolised by a solid line with a
filled diamond arrowhead pointing at the class
representing the whole.
Cardinality (Multiplicity)
Cardinality provides details of the relationship
between classes or objects of the classes. It can be
one of these categories - "One to One," "One to
Many", or "Many to Many".
How to Draw a UML Class Diagram?
1. Identify Classes - Determine objects in your
system.
2. Define Attributes & Methods - Add attributes
and methods.
3. Set Relationships - Identify associations,
inheritance, etc.
4. Use UML Notation - Follow correct symbols.
5. Refine & Review - Keep it clear and
understandable.
UML Diagram Tools
✅ Draw.io - https://app.diagrams.net/
✅ Lucidchart - https://www.lucidchart.com/
✅ StarUML - Great for system design.
✅ Microsoft Visio - Professional diagramming.
Final Thoughts
• UML class diagrams help visualize object-
oriented systems.
• They define structure, attributes, methods,
and relationships.
• Understanding inheritance, composition, and
association is crucial.
• Start with simple diagrams, then refine as your
system grows.
THANK YOU
Programming with Inheritance

Understanding Object-Oriented
Programming

Dr. Olaniyan Julius


Inheritance in Programming
Definition: Inheritance allows a new class (subclass)
to derive from an existing class (superclass).

How It Works: The subclass inherits attributes and


behaviors from the superclass but can also have
unique additions.
Syntax Inheritance
# Define a superclass
class SuperClass:
# Attributes and methods of the superclass
pass

# Define a subclass that inherits from SuperClass


class SubClass(SuperClass):
# Attributes and methods of the subclass
pass
Example of Python inheritance
Classes of Inheritance
• 1. Single Inheritance
– A single child class inherits from one parent class.
# Parent Class
class Animal:
def __init__(self, name):
self.name = name

def make_sound(self):
return "Some generic animal sound"

# Child Class (inherits from Animal)


class Dog(Animal):
def make_sound(self): # Overriding the parent method
return "Bark!"

# Testing Single Inheritance


dog = Dog("Buddy")
print(f"{dog.name} says: {dog.make_sound()}")
Classes of Inheritance
• 2. Multiple Inheritance
• A child class can inherit from multiple parent classes.
# Parent Classes
class Walkable:
def walk(self):
return "I can walk!"

class Swimmable:
def swim(self):
return "I can swim!"

# Child Class (inherits from multiple parents)


class Frog(Walkable, Swimmable):
def croak(self):
return "Ribbit!"

# Testing Multiple Inheritance


frog = Frog()
print(frog.walk()) # From Walkable class
print(frog.swim()) # From Swimmable class
print(frog.croak()) # Frog's own method
Classes of Inheritance
3. Multilevel Inheritance
• A chain of inheritance where a class inherits from another derived class.
# Base Class
class Animal:
def __init__(self, name):
self.name = name

def make_sound(self):
return "Some animal sound"

# Intermediate Derived Class


class Mammal(Animal):
def has_fur(self):
return "I have fur"

# Further Derived Class


class Cat(Mammal):
def make_sound(self): # Overriding
return "Meow!"

# Testing Multilevel Inheritance


cat = Cat("Whiskers")
print(cat.name) # Inherited from Animal
print(cat.make_sound()) # Overridden method
print(cat.has_fur()) # Inherited from Mammal
Classes of Inheritance
4. Hierarchical Inheritance
• Multiple child classes inherit from the same parent.
# Parent Class
class Animal:
def __init__(self, name):
self.name = name

def make_sound(self):
return "Some animal sound"

# Child Classes
class Dog(Animal):
def make_sound(self):
return "Bark!"

class Bird(Animal):
def make_sound(self):
return "Chirp!"

# Testing Hierarchical Inheritance


dog = Dog("Rex")
bird = Bird("Tweety")

print(f"{dog.name} says: {dog.make_sound()}")


print(f"{bird.name} says: {bird.make_sound()}")
Classes of Inheritance
Polymorphism in OOP
• Definition: Polymorphism allows an object of a class to
behave like an object of its subclass.

• Concept: A single function can work with different types,


enabling flexibility.

• Python Implementation: A subclass inherits methods from


the parent class. Methods can be re-implemented to match
subclass uniqueness.

• Example: A function func(obj) can take objects of different


classes (e.g., Tomato, Apple) and call their respective type()
and colour() methods, producing different outputs.
Polymorphism in OOP
Code Explanation
• Classes & Methods
• Tomato and Apple classes each define type() and colour().
• Represent different objects but share method names.
• Function func(obj)
• Takes an object as input.
• Calls type() and colour() methods on the object.
• Object Creation & Function Call
• obj_t = Tomato(), obj_a = Apple() → Instances created.
• func(obj_t), func(obj_a) → Same function works for both objects.
• Demonstration of Polymorphism
• func() works with different objects as long as they have type() and
colour().
• One function, multiple object types = Polymorphism!
Types of Polymorphism in Python
Static Polymorphism (Method Overloading)
§Python does not support method overloading like Java or C++.
•However, it can be simulated using default arguments or variable-length
arguments (*args).

•The same method add() works with different numbers of parameters.


Method Overloading
using variable-length arguments (*args).

class Calculator:
def add(self, *args):
return sum(args) # Adds all given numbers

# Testing variable-length arguments


calc = Calculator()
print(calc.add(2, 3)) # Two arguments
print(calc.add(1, 2, 3, 4)) # Four arguments
print(calc.add(10)) # One argument
print(calc.add()) # No argument (defaults to 0)
Dynamic Polymorphism
A subclass overrides a method from the parent class. The method in the
subclass is executed instead of the one in the parent class.
Abstract Classes
An abstract class allows the creation of methods that have common names
and perform similar tasks within child classes. Methods that have been
declared but without any detailed implementations are known as abstract
methods.

An abstract class is identified by having a single or multiple abstract


methods. It is useful for designing large functional units. It is also useful
in providing a common interface to implement different components.

With the abstract base class, an application interface (API) can be defined
for a related subclass. This will assist in ensuring that a large code base is
organized and managed well. Also, in case a different programmer or
software company gets involved in providing detailed implementation,
having an API will make things easy.

An abstract class is not built-in in Python. However, ABC is a Python


library that allows the creation of abstract classes through the abstract
base classes (ABC). The method is decorated as abstract with concrete
classes as subclasses. The method is identified with the keyword
@abstractmethod.
Abstract Classes Demo
Subclassing
• Subclassing directly from the base is an alternative
way of implementing abstract base classes. This
option avoids explicit declaration of class as an
abstract base class.
Subclassing Code Implementation
ENCAPSULATION
• Wrapping data and the methods into a single
unit (a class) and restricting direct access to
some of the data.

• Why is Encapsulation useful?


• ✅ Protects data from accidental or unauthorized
changes
• ✅ Controls how data is accessed/modified
• ✅ Makes code modular, easier to maintain, and more
secure
How it's done in Python
• Using private/protected members (_var, __var)
• Using getters and setters to control access
class BankAccount:
def __init__(self):
self.__balance = 0 # private variable

def deposit(self, amount):


if amount > 0:
self.__balance += amount

def withdraw(self, amount):


if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Invalid withdrawal!")

def get_balance(self):
return self.__balance
Access Modifiers
• In Python OOP, access modifiers are used to
control the visibility of class members
(variables and methods).
• Python has three main access levels, but they
are more of a convention than strict rules like
in some other languages like java, c++, etc.
• Public (Default)
• Protected (Single underscore _)
• Private (Double underscore __)
Access Modifiers
• Public (Default)
• Syntax: Just name it normally, like self.name.
• Access: Can be accessed anywhere, inside or
outside the class.
• Example:
class Person:
def __init__(self):
self.name = "Julius" # Public
Access Modifiers
• Protected (Single underscore _)
• Syntax: Prefix with a single underscore, like
self._age.
• Access: Can be accessed from within the class
and its subclasses (not enforced, just a hint to
programmers).
• Example:
class Person:
def __init__(self):
self._age = 30 # Protected
Access Modifiers
• Private (Double underscore __)
• Syntax: Prefix with two underscores, like
self.__salary.
• Access: Only accessible within the class. Python
"name-mangles" it to _ClassName__variable,
making it harder (but not impossible) to access
from outside.
• Example:
class Person:
def __init__(self):
self.__salary = 5000 # Private
Access Modifiers Summary Table
Constructor
• A constructor is a special method called
__init__() in Python.

• It runs automatically when you create a new


object from a class and is used to initialize the
object's properties.
Basic Syntax of a Constructor
class ClassName:
def __init__(self, parameters):
# initialize properties here
self.property = value
Step-by-Step Example
Step 1: Create the class
class Person:
def __init__(self, name, age): # Constructor
with 2 parameters
self.name = name # Assign the value to the
object
self.age = age
Step-by-Step Example
Step 2: Create an object
p1 = Person("Alice", 25) # This automatically
calls __init__()
Step 3: Access the object’s attributes
print(p1.name) # Output: Alice
print(p1.age) # Output: 25
Default Constructor (No parameters)
class Animal:
def __init__(self):
print("An animal has been created!")

a1 = Animal() # Output: An animal has been


created!
Constructors - Summary
• __init__() is the constructor in Python.
• It is called automatically when an object is
created.
• It’s used to initialize values.
• self refers to the current object.
self
• self is just a convention—
• a name for the first parameter of instance
methods in a class.
• It refers to the specific object (instance) of the
class that is calling the method.
• Think of self as "this object" in other
programming languages like Java or C++.
Why do we need self?
• When you create an object from a class, self lets each object keep
track of its own data.
• Without self, Python wouldn't know which object's data you're
referring to.
class Dog:
def __init__(self, name):
self.name = name # "self.name" is this specific dog's name
def bark(self):
print(f"{self.name} says Woof!")

Then you can do:


d1 = Dog("Bingo")
d2 = Dog("Buddy")

d1.bark() # Output: Bingo says Woof!


d2.bark() # Output: Buddy says Woof!

Without self, both dogs would share the same name—there'd be no way to tell them
apart
cls
• Used with class methods, which are declared
with the @classmethod decorator.
• cls lets you access or modify class variables.
class Car:
wheels = 4 # Class variable

@classmethod
def change_wheels(cls, num):
cls.wheels = num

Car.change_wheels(6)
print(Car.wheels) # Output: 6
Class using both self and cls
class Car:
# Class variable (shared across all instances)
total_cars = 0

def __init__(self, model):


self.model = model # Instance variable
Car.total_cars += 1 # Increment class variable

def show_model(self):
# Instance method using self
print(f"Car Model: {self.model}")

@classmethod
def show_total_cars(cls):
# Class method using cls
print(f"Total cars created: {cls.total_cars}")
Class using both self and cls
Usage:
car1 = Car("Tesla")
car2 = Car("BMW")

car1.show_model() # Output: Car Model: Tesla


car2.show_model() # Output: Car Model: BMW

Car.show_total_cars() # Output: Total cars created: 2

• self.model is different for each object (car1, car2).


• cls.total_cars keeps track of how many cars have been created —
shared across all instances.
Name Mangling
• Even though private members (those with
__double_underscores) are intended to be
hidden from outside the class, Python still
allows you to access them using a trick called
name mangling.
class Person:
def __init__(self):
self.__secret = "I love Python“
def reveal_secret(self):
return self.__secret
Name Mangling
• Normal Access (inside the class):
p = Person()
print(p.reveal_secret()) # Output: I love Python

• Trying to access directly (will fail):


print(p.__secret) # Error: AttributeError

• Name Mangling:
print(p._Person__secret) # Output: I love Python

So while private members can be accessed, doing so from


outside breaks the "privacy convention"—use it only if you
really have to (e.g., debugging or testing).
Getters and Setters
• Getters and Setters in Python are used to
access and modify private variables safely.

• Even though we can technically access them


with name mangling, using getters/setters is
the clean and recommended way especially
when we want to add logic when reading or
updating values.
Getters and Setters
class Person:
def __init__(self):
self.__age = 0 # Private variable

# Getter method
def get_age(self):
return self.__age

# Setter method
def set_age(self, age):
if age >= 0:
self.__age = age
else:
print("Age can't be negative.")
Getters and Setters
Using it:
p = Person()
p.set_age(25) # Set the age
print(p.get_age()) # Get the age => 25

p.set_age(-5) # Invalid input => prints


warning
Getters and Setters- Decorators:
Python has a cleaner way using decorators:
@property and @<name>.setter
class Person:
def __init__(self):
self.__age = 0

@property
def age(self): # acts like a getter
return self.__age

@age.setter
def age(self, value): # acts like a setter
if value >= 0:
self.__age = value
else:
print("Age can't be negative.")
Getters and Setters
Now you can use it like this:
p = Person()
p.age = 30 # Setter in action
print(p.age) # Getter in action => 30
p.age = -10 # Invalid => prints warning

• Why Use Getters and Setters?


– To control access to private data
– To add validation (like checking for negative age)
– To log or trigger actions when values change
Static Methods
• A static method belongs to the class, not the
instance (object).
It does not take self or cls as its first argument.
• Use it when the method doesn't need to
access or modify instance/class data — just
utility logic related to the class.
Example with Static Method
class MathUtils:
@staticmethod
def add(a, b):
return a + b
@staticmethod
def multiply(a, b):
return a * b
print(MathUtils.add(3, 5)) # Output: 8
print(MathUtils.multiply(4, 6)) # Output: 24

Notice: You don't need to create an object! No self is


needed. Just call it directly from the class.
Static Classes
• In Python, there’s no official "static class"
keyword like in C# or Java.

• But you can achieve the same idea using a


class with:

• Only @staticmethods
• No self or object instances
• That makes the class act like a utility class or
namespace.
Static-Class-Like Example
class StringUtils:
@staticmethod
def to_upper(text):
return text.upper()
@staticmethod
def is_palindrome(text):
cleaned = text.replace(" ", "").lower()
return cleaned == cleaned[::-1]

Use it:
print(StringUtils.to_upper("julius")) # Output: JULIUS
print(StringUtils.is_palindrome("madam")) # Output: True
THANK YOU
PROGRAM ORGANISATION
USING MODULES AND PACKAGES

By
Dr. Julius Olaniyan
Module

• A module is a single Python file (.py) that


contains code — functions, classes, or
variables — that can be reused in other
Python files.
Module - Example
• # file: calculator.py
def add(x, y):
return x + y

def subtract(x, y):


return x - y
Module - Example
• You can use the calculator.py in another
Python file:

import calculator
print(calculator.add(5, 3)) # Output: 8
print(calculator.subtract(5, 3)) # Output: 2
Package
• A package is a folder that contains multiple
modules and a special file named __init__.py
(can be empty). The __init__.py file tells
Python this directory is a package.
• Structure:
• math_tools/
• │
• ├── __init__.py
• ├── basic.py
• └── advanced.py
Modules Usage in another file
• basic.py from math_tools import basic,
def add(x, y): advanced
return x + y
print(basic.add(2, 3)) #
• advanced.py Output: 5
def power(x, y): print(advanced.power(2, 3))
return x ** y # Output: 8
Why Use Modules and Packages?

• �Reusability – Write once, use many times.


• �Clean Code – Keeps files short and readable.
• �Modularity – Each file/folder handles a specific
functionality.
• �Easier Testing – Test individual components separately.
Creating and Using Modules &
Packages – Step by Step
• STEP 1: Create a Simple Module
• Create a file called greetings.py:
# greetings.py
def say_hello(name):
return f"Hello, {name}!"
– Use it in main.py:
# main.py
import greetings
print(greetings.say_hello("Julius")) # Output: Hello,
Julius!
Creating and Using Modules &
Packages – Step by Step
• STEP 2: Create a Package
• Create a folder: my_package/
• Inside my_package/, add:
– my_package/
– ├── __init__.py
– ├── greetings.py
– └── math_ops.py
Creating and Using Modules &
Packages – Step by Step
Modules main.py
• greetings.py from my_package import greetings, math_ops
def say_hi(name):
return f"Hi, {name}!" print(greetings.say_hi("Ada"))# Output: Hi, Ada!
print(math_ops.multiply(2, 5)) # Output: 10
• math_ops.py
def multiply(x, y):
return x * y
STEP 3: Using __init__.py to Control Imports
• Edit __init__.py in my_package/
from .greetings import say_hi
from .math_ops import multiply

Now in main.py:
from my_package import say_hi, multiply

print(say_hi("Julius")) # Output: Hi, Julius!


print(multiply(3, 4)) # Output: 12
Advanced: Relative vs Absolute
Imports
Absolute Import:
from my_package.greetings import say_hi

Relative Import (inside a package):


from .greetings import say_hi # Dot means current package

Best Practices
✅ Name modules and packages with lowercase letters and underscores.
✅ Keep modules focused on one topic (e.g., auth.py, utils.py, database.py).
✅ Avoid circular imports (A imports B, B imports A).
✅ Use __init__.py to expose only useful functions.
API
• What is an API?
– API stands for Application Programming Interface.
• Definition:
– An API is a set of rules and protocols that allows
one software application to communicate with
another.
API ANALOGY
• Think of an API like a waiter in a restaurant.

ØYou (user) ask the waiter (API) for food.


ØThe waiter takes the order to the kitchen (server).
ØThe kitchen prepares the food (data).
ØThe waiter brings it back to you (response).
✅ Why Use APIs?

• �Access services (e.g., weather, maps,


Twitter)
• ♻ ️Reuse functionality from other apps
• � Secure data access with authentication
• ⚡ Speed up development (no need to
reinvent the wheel)
Types of APIs
RESTful API (Most Common Type)

• REST = Representational State Transfer


• � Characteristics:
– Uses HTTP methods:
• GET: Retrieve data
• POST: Create new data
• PUT: Update existing data
• DELETE: Delete data
– Returns data in JSON or XML
– Stateless (no client context stored on the server)
Making API Requests
• Step 1: Install requests
• pip install requests
• Step 2: Make a GET Request
• import requests

• response = requests.get("https://api.github.com")
• print(response.status_code) # 200 means OK
• print(response.json()) # Returns JSON data
Get User Data from GitHub API
import requests

url = "https://api.github.com/users/octocat"
response = requests.get(url)
data = response.json()

print(f"Name: {data['name']}")
print(f"Public Repos: {data['public_repos']}")
Authentication with APIs
• Some APIs require API keys (like passwords).
headers = {
"Authorization": "Bearer YOUR_API_KEY"
}

response = requests.get("https://api.example.com/data",
headers=headers)
Building Your Own API
• pip install flask
# simple_api.py
from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/hello', methods=['GET'])
def hello():
return jsonify({"message": "Hello, Julius!"})

if __name__ == '__main__':
app.run()

• Then visit http://127.0.0.1:5000/hello in your browser.


Testing APIs (Postman or Browser)
• Postman/Apidog is a free tool to send API
requests easily.
• You can also use curl in terminal:
• curl https://api.github.com/users/octocat
THANK YOU

You might also like