Cover
Start nu gratis Objects [Autosaved].pptx
Summary
# Objectgeoriënteerd programmeren: concepten en objecten
Dit deel introduceert de fundamentele concepten van objectgeoriënteerd programmeren (OOP), waarbij de nadruk ligt op objecten, hun eigenschappen (staat) en hun mogelijkheden (gedrag), en de relatie tussen objecten en klassen.
### 1.1 Introductie tot objectgeoriënteerd programmeren
Objectgeoriënteerd programmeren (OOP) is een programmeerparadigma dat draait om het gebruik van objecten. Een object wordt beschouwd als een entiteit uit de werkelijkheid die uniek identificeerbaar is. Voorbeelden van objecten zijn een student, een bureau, een cirkel, een knop of zelfs een lening. Elk object bezit een unieke identiteit, een bepaalde staat en gedragingen.
* **Identiteit:** Elk object is uniek, zelfs als het dezelfde staat heeft als een ander object.
* **Staat (State):** De staat van een object wordt bepaald door een verzameling datavelden (ook wel eigenschappen genoemd) en hun huidige waarden.
* **Gedrag (Behavior):** Het gedrag van een object wordt gedefinieerd door een set methoden die acties uitvoeren.
> **Tip:** Gedrag van een object kan worden gezien als 'dingen die het object kan doen' of 'hoe het object dingen kan veranderen'. In OOP wordt dit gedrag vastgelegd binnen een klasse.
### 1.2 Klassen
Een klasse fungeert als een blauwdruk of sjabloon voor het creëren van objecten. Het definieert de structuur en het gedrag dat alle objecten van die klasse zullen delen.
Een Python-klasse gebruikt variabelen om datavelden op te slaan en definieert methoden om acties uit te voeren. Daarnaast bevat een klasse een speciale methode, de "initializer" (of constructor), die wordt aangeroepen wanneer een nieuw object wordt gecreëerd. De initializer is primair bedoeld voor het initialiseren van de datavelden van het object.
De basisstructuur van een klasse in Python ziet er als volgt uit:
```python
class ClassName:
# Initializer methode (construeert het object)
def __init__(self, parameters):
# Initialiseer datavelden
pass
# Andere methoden (gedrag)
def method_name(self, parameters):
# Voer acties uit
pass
```
#### 1.2.1 Objecten als instanties van een klasse
Wanneer een klasse is gedefinieerd, kunnen er objecten (instanties) van die klasse worden aangemaakt. Dit proces wordt objectconstructie genoemd.
**Syntaxis voor objectconstructie:**
```python
object_naam = ClassName(argumenten)
```
Het construeren van een object omvat doorgaans twee stappen:
1. Het creëren van een object in het geheugen dat behoort tot de klasse.
2. Het aanroepen van de `__init__` methode van de klasse om het object te initialiseren met de meegegeven argumenten.
Tijdens dit proces wordt de speciale parameter `self` in de `__init__` methode automatisch ingesteld om te verwijzen naar het zojuist gecreëerde object.
#### 1.2.2 De `self` parameter
Alle methoden binnen een klasse, inclusief de initializer (`__init__`), nemen de parameter `self` als eerste argument. Deze parameter is cruciaal; het verwijst naar het object zelf dat de methode aanroept of wordt geconstrueerd.
* `self` wordt gebruikt om toegang te krijgen tot de datavelden (instance variables) van het specifieke object.
* Hoewel de naam `self` door conventie wordt gebruikt, kan er technisch gezien een andere naam gekozen worden. Echter, het aanhouden van `self` is sterk aanbevolen voor de leesbaarheid en conformiteit met Python-standaarden.
* Bij het aanroepen van een methode via een object, hoeft `self` niet expliciet meegegeven te worden; Python regelt dit automatisch.
**Voorbeeld:**
```python
class Circle:
def __init__(self, radius):
self.radius = radius # self.radius is een datafield van het object
def get_area(self):
return 3.14159 * self.radius \ast\ast 2 # self.radius wordt hier ook gebruikt
c = Circle(5) # Bij het aanroepen van Circle(5) wordt __init__(self, 5) aangeroepen
area = c.get_area() # Bij het aanroepen van c.get_area() wordt get_area(self) aangeroepen
```
### 1.3 Toegang tot leden van objecten
Nadat een object is gecreëerd, kunnen de datavelden en methoden (ook wel leden genoemd) ervan worden benaderd met behulp van de puntoperator (`.`). Dit staat ook bekend als de "object member access operator".
**Syntaxis:**
* Voor datavelden: `object_naam.data_veld`
* Voor methoden: `object_naam.methode_naam(argumenten)`
**Voorbeeld:**
Stel we hebben een klasse `Circle` met een dataveld `radius` en een methode `getArea()`:
```python
# Stel dat Circle is geïmporteerd
# from Circle import Circle
c = Circle(5) # Creëert een Circle object met radius 5
# Toegang tot een dataveld (indien publiek)
print(c.radius) # Output: 5
# Aanpassen van een dataveld (indien toegestaan)
c.radius = 10
# Aanroepen van een methode
print(c.getArea()) # Roept de getArea methode aan op het object c
```
> **Tip:** Het direct aanpassen van datavelden van buitenaf is niet altijd wenselijk en kan worden beperkt door middel van inkapseling.
### 1.4 UML voor klassen en objecten
Unified Modeling Language (UML) biedt een gestandaardiseerde grafische notatie voor het modelleren van software. Voor klassen en objecten wordt UML als volgt gebruikt:
* **Klasse-diagrammen:** Een klasse wordt weergegeven in een rechthoek die is opgedeeld in drie secties:
1. Klassenaam (bovenaan)
2. Datavelden (in het midden)
3. Methoden (onderaan)
Datavelden worden vaak voorafgegaan door een `-` teken om aan te geven dat ze privé zijn, en methoden door een `+` teken voor publieke methoden.
* **Object-diagrammen:** Een object wordt weergegeven als een rechthoek met de naam van het object gevolgd door een dubbele punt en de naam van de klasse (bijvoorbeeld `cirkel1: Circle`). Onder deze naam worden de specifieke waarden van de datavelden van dat object vermeld.
**Voorbeeld van UML:**
```
+-------------------+
| Circle |
+-------------------+
| - radius: float |
+-------------------+
| + __init__(radius: float)|
| + getArea() |
+-------------------+
```
En voor objecten:
```
+-------------------+
| cirkel1: Circle |
+-------------------+
| radius = 5 |
+-------------------+
```
### 1.5 Immutable versus mutable objecten
Een belangrijk onderscheid in OOP is tussen onveranderlijke (immutable) en veranderlijke (mutable) objecten:
* **Immutable objecten:** De staat van een immutable object kan na creatie niet meer worden gewijzigd. Elke poging om de staat te wijzigen, resulteert in de creatie van een nieuw object. Goede voorbeelden hiervan zijn integers, floats en strings in Python.
* **Mutable objecten:** De staat van een mutable object kan na creatie wel worden gewijzigd. Wijzigingen worden direct op het bestaande object toegepast. Voorbeelden hiervan zijn lijsten en dictionaries in Python.
> **Tip:** Het begrijpen van dit onderscheid is cruciaal voor het correct omgaan met objecten en het voorkomen van onverwachte bijwerkingen in uw code.
### 1.6 Dataveld-inkapseling
Dataveld-inkapseling (data field encapsulation) is een kernprincipe van OOP dat gericht is op het beschermen van de data van een object en het vereenvoudigen van onderhoud. Dit wordt bereikt door directe toegang tot datavelden te beperken en deze te beschermen tegen corruptie.
In Python wordt inkapseling vaak geïmplementeerd door "private" datavelden te definiëren. Dit wordt aangegeven door de naam van het dataveld te laten voorafgaan door twee underscores (`__`).
**Voorbeeld:**
```python
class CircleWithPrivateRadius:
def __init__(self, radius):
self.__radius = radius # Private data field
def getRadius(self):
return self.__radius
# Poging tot directe toegang faalt:
# c = CircleWithPrivateRadius(5)
# print(c.__radius) # Dit zal een AttributeError geven
# Toegang via een publieke methode:
c = CircleWithPrivateRadius(5)
print(c.getRadius()) # Output: 5
```
**Design Guide:**
* Als een klasse bedoeld is voor gebruik door andere programma's, is het raadzaam om datavelden privé te maken om de integriteit van de data te waarborgen en het onderhoud te vergemakkelijken.
* Als een klasse uitsluitend intern door uw eigen programma wordt gebruikt, is het inkapselen van datavelden mogelijk minder kritisch.
### 1.7 Klasse-abstractie en inkapseling
* **Klasse-abstractie (Class Abstraction):** Dit principe houdt in dat de implementatiedetails van een klasse worden gescheiden van het gebruik ervan. De ontwikkelaar van de klasse verstrekt een beschrijving van hoe de klasse kan worden gebruikt (het "contract"), zonder de interne werking bloot te geven. De gebruiker van de klasse hoeft niet te weten hoe de klasse is geïmplementeerd; de details zijn "verpakt" en verborgen.
* **Inkapseling (Encapsulation):** Dit verwijst naar het bundelen van data (datavelden) en de methoden die op die data opereren binnen één eenheid (de klasse). Het zorgt ervoor dat de interne staat van een object beschermd is en dat het object alleen via zijn publieke interface (methoden) kan worden gemanipuleerd.
Samen zorgen abstractie en inkapseling ervoor dat klassen worden gebruikt als "black boxes" waarvoor gebruikers alleen de publieke interface hoeven te kennen, wat leidt tot beter onderhoudbare en herbruikbare software.
### 1.8 Procedureel versus objectgeoriënteerd programmeren
Het fundamentele verschil tussen procedureel en objectgeoriënteerd programmeren ligt in de organisatie van code en data:
* **Procedureel programmeren:** Data en operaties op data zijn gescheiden. Data wordt naar methoden gestuurd om bewerkingen uit te voeren. Dit kan leiden tot problemen bij de ontwikkeling en het onderhoud van grotere systemen.
* **Objectgeoriënteerd programmeren (OOP):** Data en de operaties die daarop betrekking hebben, worden samengebundeld in objecten. OOP-programma's weerspiegelen de echte wereld waarin objecten zowel attributen als activiteiten hebben. Dit leidt tot:
* Verbeterde software-herbruikbaarheid.
* Eenvoudigere ontwikkeling en onderhoud.
* Een betere afspiegeling van complexe problemen.
Een Python-programma kan worden gezien als een verzameling van samenwerkende objecten. Het denken in termen van objecten is een kernvaardigheid bij het programmeren in Python.
---
# Definiëren en construeren van klassen en objecten
Dit onderwerp introduceert de fundamentele concepten van objectgeoriënteerd programmeren: klassen en objecten, inclusief het definiëren van klassen met datavelden en methoden, het construeren van objecten met constructors en initializers, en het benaderen van objectleden.
### 2.1 Objecten en klassen
Objectgeoriënteerd programmeren (OOP) is een programmeerparadigma dat gebruikmaakt van objecten. Een object vertegenwoordigt een tastbare entiteit in de echte wereld die uniek identificeerbaar is, zoals een student, een computer of een lening.
Elk object bezit twee kerncomponenten:
* **Identiteit:** Elk object is uniek, zelfs als de staat ervan identiek is aan die van een ander object.
* **Staat:** De staat van een object wordt bepaald door een verzameling datavelden (ook wel properties genoemd) en hun huidige waarden.
* **Gedrag:** Het gedrag van een object wordt gedefinieerd door een set methoden die acties op het object kunnen uitvoeren of het kunnen wijzigen.
Een **klasse** fungeert als een blauwdruk of sjabloon voor het creëren van objecten. De klasse definieert de datavelden en methoden die alle objecten van die klasse zullen hebben.
#### 2.1.1 Klassen definiëren met datavelden en methoden
Een klasse wordt in Python gedefinieerd met behulp van het `class` sleutelwoord, gevolgd door de klassenaam. Binnen de klasse worden variabelen gebruikt om de datavelden op te slaan en worden functies gedefinieerd als methoden om acties uit te voeren.
* **Datavelden:** Dit zijn variabelen die de toestand van een object opslaan.
* **Methoden:** Dit zijn functies die binnen een klasse zijn gedefinieerd en operaties uitvoeren op de data van het object. Methoden die bij een specifiek object horen, worden ook wel **instance methods** genoemd.
Een speciale methode binnen een klasse is de **initializer**. Deze methode, die conventioneel `__init__` heet, wordt automatisch aangeroepen wanneer een nieuw object van de klasse wordt gecreëerd. De initializer is verantwoordelijk voor het initialiseren van de datavelden van het object.
Een typische klassedefinitie ziet er als volgt uit:
```python
class ClassName:
# Initializer (constructor)
def __init__(self, parameters):
# Initialiseren van datavelden
self.data_field1 = value1
self.data_field2 = value2
# ...
# Andere methoden
def method1(self, parameters):
# Code voor methode 1
pass
def method2(self, parameters):
# Code voor methode 2
pass
```
#### 2.1.2 Objecten construeren met constructors en initializers
Nadat een klasse is gedefinieerd, kunnen er objecten (instanties) van die klasse worden aangemaakt met behulp van de **constructor**. De syntaxis voor het construeren van een object is `ClassName(arguments)`.
Het proces van het construeren van een object omvat doorgaans twee stappen:
1. Er wordt geheugenruimte toegewezen voor het nieuwe object.
2. De **initializer** (`__init__` methode) van de klasse wordt aangeroepen om het object te initialiseren en zijn datavelden in te stellen.
De `__init__` methode heeft altijd een eerste parameter die conventioneel `self` wordt genoemd. Deze `self` parameter verwijst naar het object dat zojuist is aangemaakt en geeft toegang tot de datavelden en andere methoden van dat specifieke object.
**Voorbeeld van objectconstructie:**
Stel we hebben een `Circle` klasse gedefinieerd:
```python
class Circle:
def __init__(self, radius):
self.radius = radius # Initialiseren van het dataveld 'radius'
def getArea(self):
return 3.14159 * self.radius \* self.radius # Berekening van het oppervlak
```
Om een `Circle` object te construeren, doen we het volgende:
```python
# Creëert een Circle object met radius 5
c1 = Circle(5)
```
Hierbij gebeurt het volgende:
1. Er wordt een `Circle` object aangemaakt in het geheugen.
2. De `__init__` methode wordt aangeroepen met `self` verwijzend naar het nieuwe object en `radius` met de waarde `5`.
3. Binnen `__init__` wordt het dataveld `self.radius` van het object ingesteld op `5`.
#### 2.1.3 Objectleden benaderen met de puntoperator
Nadat een object is gecreëerd, kunnen de datavelden en methoden van het object worden benaderd met behulp van de **puntoperator** (`.`). Dit staat ook bekend als de "object member access operator".
De syntaxis is `object_naam.lid_naam`, waarbij `lid_naam` een dataveld of een methode van het object is.
**Voorbeeld:**
Stel we hebben het `Circle` object `c1` met `radius = 5`:
* Om de waarde van het dataveld `radius` te benaderen:
`c1.radius` zal de waarde `5` opleveren.
* Om de methode `getArea` aan te roepen:
`c1.getArea()` zal het oppervlak van de cirkel berekenen en retourneren.
Het is ook mogelijk om datavelden direct aan te passen na creatie van het object:
```python
c1.radius = 10 # De radius van de cirkel wordt aangepast naar 10
```
#### 2.1.4 Het `self` parameter
De `self` parameter in methoden van een klasse is cruciaal. Het vertegenwoordigt het object zelf dat de methode aanroept. Binnen een methode kan `self` gebruikt worden om toegang te krijgen tot de datavelden en andere methoden die specifiek zijn voor dat object. Hoewel de naam `self` een conventie is, is het gebruik ervan sterk aanbevolen voor leesbaarheid.
> **Tip:** De `self` parameter wordt automatisch door Python ingevuld wanneer een methode wordt aangeroepen via een object. Je hoeft `self` dus niet expliciet mee te geven bij het aanroepen van een methode.
### 2.2 UML grafische notatie voor klassen en objecten
Unified Modeling Language (UML) biedt een gestandaardiseerde manier om objectgeoriënteerde systemen te modelleren.
* **Klassen:** Een klasse wordt in UML weergegeven als een rechthoek, verdeeld in drie secties:
1. **Klassenaam:** Bovenaan, in het midden.
2. **Datavelden (Attributen):** In de middelste sectie, met hun namen en (optioneel) gegevenstypen. Een minteken (`-`) voor een dataveld geeft aan dat het privé is.
3. **Methoden (Operaties):** In de onderste sectie, met hun namen en (optioneel) parameters en retourtypen.
* **Objecten:** Een object wordt weergegeven als een rechthoek met twee secties. Bovenaan staat de objectnaam gevolgd door de klassenaam (bijvoorbeeld `objectnaam: Klassenaam`). De onderste sectie toont de waarden van de datavelden van dat specifieke object.
**Voorbeeld UML notatie:**
Voor de `Circle` klasse:
```
+-----------------+
| Circle |
+-----------------+
| - radius: float |
+-----------------+
| + getArea() |
| + getPerimeter()|
+-----------------+
```
En een object `c1` van de `Circle` klasse met `radius = 5`:
```
+-----------+
| c1: Circle|
+-----------+
| radius = 5|
+-----------+
```
### 2.3 Immuutable en mutable objecten
Objecten kunnen worden ingedeeld in twee categorieën op basis van hun veranderlijkheid:
* **Immutable objecten:** Objecten waarvan de staat niet kan worden gewijzigd nadat ze zijn gecreëerd. Als je een "wijziging" wilt aanbrengen, wordt er in feite een nieuw object met de gewijzigde staat gecreëerd. Voorbeelden van immutable typen in Python zijn integers, floats, strings en tuples.
* Bijvoorbeeld, het toekennen van een nieuwe waarde aan een string variabele creëert een nieuw string object in plaats van het originele object te wijzigen.
* **Mutable objecten:** Objecten waarvan de staat wel kan worden gewijzigd nadat ze zijn gecreëerd. Wijzigingen worden direct op het bestaande object toegepast. Voorbeelden van mutable typen in Python zijn lijsten, dictionaries en sets.
* Bijvoorbeeld, het toevoegen van een element aan een lijst wijzigt de lijst direct.
### 2.4 Data field encapsulatie
Encapsulatie is een kernprincipe van OOP dat het bundelen van data (datavelden) en de methoden die op die data werken, binnen één eenheid (de klasse) impliceert. **Data field encapsulatie** gaat een stap verder door de directe toegang tot datavelden van buitenaf te beperken of te verbergen.
Dit wordt gedaan om:
* **Data corruptie te voorkomen:** Door directe toegang te ontzeggen, worden onbedoelde of ongeldige wijzigingen van de data voorkomen.
* **Onderhoudbaarheid van de klasse te verbeteren:** De interne implementatie van de klasse kan worden gewijzigd zonder dat dit impact heeft op de code die de klasse gebruikt, zolang de publieke interface (methoden) gelijk blijft.
In Python kan data field encapsulatie worden geïmplementeerd door datavelden een naam te geven die begint met twee underscores (`__`). Dit markeert ze als "privé", wat aangeeft dat ze bedoeld zijn voor intern gebruik binnen de klasse. Python voert echter "naam-mangling" uit, waarbij `__private_field` intern wordt omgezet naar `_ClassName__private_field`. Dit maakt het nog steeds mogelijk om de velden te benaderen, zij het op een minder directe manier, wat bedoeld is om accidentele toegang te ontmoedigen.
Publieke methoden, vaak getters en setters genoemd, worden gebruikt om gecontroleerde toegang tot deze privé datavelden te bieden.
**Voorbeeld:**
```python
class CircleWithPrivateRadius:
def __init__(self, radius):
self.__radius = radius # Privé dataveld
def getRadius(self):
return self.__radius # Publieke getter methode
# Setters kunnen ook worden toegevoegd voor gecontroleerde wijzigingen
# def setRadius(self, new_radius):
# if new_radius > 0:
# self.__radius = new_radius
# else:
# print("Radius moet positief zijn.")
```
Als je probeert `c.__radius` direct te benaderen, krijg je een `AttributeError`. Je moet de `getRadius()` methode gebruiken.
> **Tip:** Encapsulatie is vooral belangrijk wanneer je klassen ontwerpt die door andere programmeurs of programma's zullen worden gebruikt. Voor interne klassen in je eigen project is het minder cruciaal.
### 2.5 Klasse abstractie en encapsulatie
* **Klasse abstractie:** Dit principe richt zich op het scheiden van de implementatiedetails van een klasse van het gebruik ervan. De ontwikkelaar van de klasse biedt een heldere beschrijving van wat de klasse doet en hoe deze kan worden gebruikt (de publieke interface). De gebruiker van de klasse hoeft de interne werking niet te kennen, alleen hoe de interface te gebruiken.
* **Encapsulatie:** Zoals eerder besproken, is dit het bundelen van data en methoden en het verbergen van implementatiedetails.
Samen zorgen klasse abstractie en encapsulatie ervoor dat klassen worden gebruikt als "black boxes". Clients (andere delen van het programma) interageren met de klasse via een gedefinieerd contract (de publieke methoden), zonder zich zorgen te hoeven maken over de interne complexiteit. Dit verhoogt de herbruikbaarheid en onderhoudbaarheid van de code aanzienlijk.
### 2.6 Het verschil tussen procedureel en objectgeoriënteerd programmeren
Het fundamentele verschil ligt in hoe data en functionaliteit worden georganiseerd:
* **Procedureel programmeren:** Data en de operaties (functies) die op die data worden uitgevoerd, zijn gescheiden. Data wordt aan functies doorgegeven om te worden verwerkt. Dit kan leiden tot uitdagingen bij het beheren van complexe systemen, omdat de data verspreid kan zijn en functies mogelijk onbedoelde wijzigingen aan data kunnen aanbrengen.
* **Objectgeoriënteerd programmeren (OOP):** Data (datavelden) en de operaties die op die data werken (methoden) worden samengebundeld in objecten. Dit model weerspiegelt de echte wereld, waarin entiteiten zowel attributen (data) als activiteiten (gedrag) hebben. OOP bevordert:
* **Herbruikbaarheid van code:** Klassen kunnen eenvoudig worden hergebruikt in verschillende delen van een applicatie of in andere projecten.
* **Onderhoudbaarheid:** Wijzigingen in de implementatie van een klasse hebben minder kans om andere delen van het programma te beïnvloeden, dankzij encapsulatie.
* **Schaalbaarheid:** Het organiseren van code in samenwerkende objecten maakt het eenvoudiger om grotere en complexere software systemen te ontwikkelen.
### 2.7 Gebruik van de `datetime` klasse
De Python `datetime` module biedt krachtige functionaliteit voor het werken met datums en tijden. De `datetime` klasse binnen deze module vertegenwoordigt een specifieke datum en tijd.
**Belangrijke aspecten van de `datetime` klasse:**
* **Datavelden:** Een `datetime` object bevat datavelden zoals `year`, `month`, `day`, `hour`, `minute`, `second`, en `microsecond`.
* **Constructie:**
* `datetime.now()`: Creëert een `datetime` object voor de huidige datum en tijd.
* `datetime(year, month, day, hour, minute, second, microsecond)`: Creëert een `datetime` object met gespecificeerde waarden.
* **Toegang tot informatie:** De individuele datavelden kunnen worden benaderd met de puntoperator, bijvoorbeeld `d.year`.
**Voorbeeld:**
```python
from datetime import datetime
# Huidige datum en tijd
now = datetime.now()
print(f"Huidige jaar: {now.year}")
print(f"Huidige maand: {now.month}")
print(f"Huidige dag: {now.day}")
print(f"Huidig uur: {now.hour}")
print(f"Huidige minuut: {now.minute}")
print(f"Huidige seconde: {now.second}")
# Specifiek datum en tijd object
specific_dt = datetime(2023, 10, 27, 10, 30, 0)
print(f"Specifiek datum en tijd: {specific_dt}")
```
---
# Gegevensbescherming en abstractie in objectgeoriënteerd programmeren
Dit gedeelte behandelt de principes van data field encapsulation en class abstraction, inclusief het gebruik van private data fields en UML-diagrammen om klassen te beschrijven.
### 3.1 Objecten en klassen in objectgeoriënteerd programmeren
Objectgeoriënteerd programmeren (OOP) is een programmeerparadigma dat gebruikmaakt van "objecten". Een object representeert een entiteit uit de echte wereld die uniek geïdentificeerd kan worden, zoals een student, een cirkel of een knop. Elk object heeft een unieke identiteit, een staat en gedrag.
* **Staat (State):** De staat van een object bestaat uit een set van data fields (ook wel eigenschappen genoemd) met hun huidige waarden.
* **Gedrag (Behavior):** Het gedrag van een object wordt gedefinieerd door een set van methoden.
Een **klasse** fungeert als een blauwdruk of sjabloon voor objecten. In Python gebruikt een klasse variabelen om data fields op te slaan en definieert methoden om acties uit te voeren. Een klasse bevat ook een speciale methode, de _initializer_ (vaak `__init__`), die wordt aangeroepen om een nieuw object te creëren en te initialiseren.
#### 3.1.1 Klassen definiëren met data fields en methoden
Een klasse wordt gedefinieerd met het `class` sleutelwoord, gevolgd door de klassenaam. Binnen de klasse worden data fields (variabelen) en methoden (functies) gedefinieerd.
```python
class ClassName:
# Initializer method
def __init__(self, ...):
# Initialize data fields
# Other methods
def method1(self, ...):
# Actions
pass
def method2(self, ...):
# Actions
pass
```
#### 3.1.2 Objecten construeren met een constructor
Nadat een klasse is gedefinieerd, kunnen objecten van die klasse worden gecreëerd met behulp van een constructor.
1. Er wordt een object in het geheugen gecreëerd voor de klasse.
2. De `__init__` methode van de klasse wordt aangeroepen om het object te initialiseren. De `self` parameter in de `__init__` methode verwijst automatisch naar het zojuist gecreëerde object.
De syntax voor het construeren van een object is: `ClassName(argumenten)`.
**Voorbeeld:**
Stel, we hebben een klasse `Circle`. Het construeren van een object kan er als volgt uitzien:
`c = Circle(5)`
Hierbij wordt een `Circle` object gecreëerd en de `__init__` methode wordt aangeroepen met `self` verwijzend naar het nieuwe object en `5` als argument voor de radius.
#### 3.1.3 Toegang tot leden van objecten met de puntoperator (.)
Nadat een object is gecreëerd, kunnen de data fields en methoden van het object worden benaderd met behulp van de puntoperator (`.`), ook wel de object member access operator genoemd.
**Voorbeeld:**
```python
from Circle import Circle # Aangenomen dat Circle in een apart bestand staat
c = Circle(5)
print(c.getPerimeter()) # Roept de getPerimeter methode aan
c.radius = 10 # Wijzigt de radius data field (indien toegankelijk)
print(c.getArea()) # Roept de getArea methode aan
```
#### 3.1.4 Het object zelf refereren met de `self` parameter
Alle methoden binnen een klasse hebben standaard de eerste parameter `self`. Deze parameter verwijst naar het object dat de methode aanroept. Hoewel de naam `self` conventioneel is, kan elke naam gebruikt worden. `self` wordt gebruikt om toegang te krijgen tot de instance variables (data fields) van het specifieke object.
> **Tip:** De `self` parameter wordt gebruikt binnen de implementatie van een methode, maar wordt niet expliciet meegegeven bij het aanroepen van de methode vanuit buiten de klasse.
### 3.2 UML-diagrammen voor klassenbeschrijving
UML (Unified Modeling Language) is een gestandaardiseerde grafische notatie om software te modelleren. Voor klassen wordt UML gebruikt om hun structuur te beschrijven, inclusief klassenamen, data fields, constructors en methoden.
Een UML-klassendiagram bestaat typisch uit drie secties:
1. **Klassenaam:** Bovenaan de doos.
2. **Data fields:** Met een minteken (`-`) voor private leden en een plusteken (`+`) voor publieke leden.
3. **Methoden:** Met een minteken (`-`) voor private leden en een plusteken (`+`) voor publieke leden. Constructors worden vaak apart of binnen de methoden-sectie vermeld.
**UML Notatie voor objecten:** Een object wordt in UML weergegeven met de objectnaam gevolgd door een dubbele punt en de klassenaam, bijvoorbeeld `cirkel1: Circle`. De specifieke waarden van de data fields van het object worden eronder vermeld.
### 3.3 Immuutable vs. Mutable objecten
Objecten kunnen worden onderverdeeld in twee categorieën op basis van hun aanpasbaarheid na creatie:
* **Immuutable (onveranderlijke) objecten:** Deze objecten kunnen niet worden gewijzigd nadat ze zijn gecreëerd. Elke poging om een immuut object te "wijzigen" resulteert in de creatie van een nieuw object met de gewenste wijzigingen. Voorbeelden in Python zijn integers, floats, strings en tuples.
* **Mutable (veranderlijke) objecten:** Deze objecten kunnen worden aangepast nadat ze zijn gecreëerd. Wijzigingen worden direct op het bestaande object doorgevoerd. Voorbeelden zijn lijsten, dictionaries en sets.
### 3.4 Data field encapsulatie voor gegevensbescherming
**Data field encapsulatie** is een principe waarbij de interne staat (data fields) van een object wordt verborgen voor directe toegang van buitenaf. Dit dient twee belangrijke doelen:
1. **Gegevensbescherming (Prevent data corruption):** Voorkomt dat de client (code die de klasse gebruikt) de data fields direct manipuleert op manieren die inconsistenties of fouten kunnen veroorzaken.
2. **Onderhoudbaarheid (Make class easy to maintain):** Door data fields te beschermen, kan de interne implementatie van de klasse worden gewijzigd zonder dat de code die de klasse gebruikt (de client) noodzakelijkerwijs aangepast hoeft te worden, zolang de publieke interface (methoden) hetzelfde blijft.
In Python worden private data fields conventioneel aangeduid met twee leidende underscores (bijvoorbeeld `__variabele`). Methoden om deze private data fields te benaderen of te wijzigen worden publieke "getter" en "setter" methoden genoemd.
**Voorbeeld (met private radius):**
```python
class CircleWithPrivateRadius:
def __init__(self, radius):
self.__radius = radius # Private data field
def getRadius(self):
return self.__radius # Publieke getter methode
def setRadius(self, radius):
if radius > 0: # Validatie
self.__radius = radius
else:
print("Radius moet positief zijn.")
# Poging tot directe toegang (zou een AttributeError geven):
# c = CircleWithPrivateRadius(5)
# print(c.__radius) # Dit werkt niet
# Gebruik van getter en setter:
c = CircleWithPrivateRadius(5)
print(c.getRadius()) # Output: 5
c.setRadius(10)
print(c.getRadius()) # Output: 10
c.setRadius(-2) # Output: Radius moet positief zijn.
```
> **Tip:** Als een klasse primair bedoeld is voor intern gebruik binnen een eigen programma, is het minder cruciaal om data fields te encapsuleren. Echter, voor klassen die bedoeld zijn voor gebruik door andere ontwikkelaars of programma's, is encapsulatie sterk aanbevolen.
### 3.5 Class abstraction en encapsulation
**Class abstraction** (klasse-abstractie) houdt in dat de implementatiedetails van een klasse worden gescheiden van het gebruik ervan. De ontwikkelaar van de klasse biedt een "contract" (de publieke interface: publieke methoden en constanten) dat beschrijft hoe de klasse gebruikt kan worden, zonder dat de gebruiker hoeft te weten hoe de klasse intern is geïmplementeerd.
**Encapsulation** (inkapseling) is het mechanisme waarmee deze implementatiedetails (data fields en interne methoden) worden verborgen en beschermd binnen de klasse. Dit creëert een "black box" voor de gebruiker van de klasse.
Samen maken abstractie en encapsulatie softwareontwikkeling makkelijker:
* **Herbruikbaarheid:** Klassen kunnen eenvoudig worden hergebruikt in verschillende projecten.
* **Onderhoudbaarheid:** Wijzigingen in de interne werking van een klasse hebben minder impact op de rest van het systeem.
* **Ontwikkelingssnelheid:** Ontwikkelaars kunnen klassen gebruiken op basis van hun publieke interface, zonder te verdwalen in complexe implementatiedetails.
**Voorbeeld (Loan klasse):**
Een `Loan` klasse kan abstractie en encapsulatie demonstreren. De gebruiker hoeft niet te weten hoe de renteberekening exact plaatsvindt, maar kan wel via publieke methoden (`getInterestRate()`, `getLoanAmount()`) de relevante informatie opvragen of via een constructor een lening object aanmaken. De data fields zoals `annualInterestRate`, `numberOfYears` en `loanAmount` zouden intern als private kunnen worden gemarkeerd (`-` in UML).
### 3.6 Verschillen tussen procedureel en objectgeoriënteerd programmeren
* **Procedureel programmeren:** Data en operaties op die data zijn vaak gescheiden. Programma's worden opgebouwd als een reeks instructies en functies. Data wordt naar functies gestuurd om verwerkt te worden.
* **Objectgeoriënteerd programmeren:** Data (fields) en operaties (methods) die op die data betrekking hebben, worden samengebracht in objecten. Dit model weerspiegelt vaak de echte wereld beter, waar entiteiten zowel attributen als activiteiten hebben.
De voordelen van OOP ten opzichte van procedureel programmeren omvatten:
* **Verbeterde softwareherbruikbaarheid.**
* **Gemakkelijker te ontwikkelen en te onderhouden programma's.**
* **Programma's worden georganiseerd als een verzameling samenwerkende objecten.**
### 3.7 De `datetime` klasse in Python
De `datetime` module in Python biedt functionaliteit voor het werken met datums en tijden.
* **`datetime.now()`:** Geeft een `datetime` object terug dat de huidige datum en tijd vertegenwoordigt.
* **Attributes:** Een `datetime` object heeft attributes zoals `year`, `month`, `day`, `hour`, `minute`, `second` en `microsecond`.
**Voorbeeld:**
```python
from datetime import datetime
d = datetime.now()
print("Huidig jaar:", d.year)
print("Huidige maand:", d.month)
print("Huidige dag van de maand:", d.day)
print("Huidig uur:", d.hour)
print("Huidige minuut:", d.minute)
print("Huidige seconde:", d.second)
```
---
# Vergelijking tussen procedureel en objectgeoriënteerd programmeren
Dit deel verkent de fundamentele verschillen tussen procedurele en objectgeoriënteerde programmeerparadigma's en belicht de voordelen van OOP voor softwareontwikkeling.
### 4.1 Inleiding tot programmeerparadigma's
Objectgeoriënteerd programmeren (OOP) is een programmeerparadigma dat werkt met objecten, entiteiten uit de echte wereld die uniek identificeerbaar zijn. Elk object bezit een unieke identiteit, een toestand en gedrag. De toestand wordt bepaald door een verzameling gegevensvelden (ook wel eigenschappen genoemd) en hun huidige waarden, terwijl het gedrag wordt gedefinieerd door een set methoden.
### 4.2 Concepten van objectgeoriënteerd programmeren
#### 4.2.1 Objecten
Een object combineert zowel toestand als gedrag. De toestand bepaalt het object, terwijl het gedrag definieert wat het object doet.
* **Toestand:** Verzameling van gegevensvelden met hun huidige waarden.
* **Gedrag:** Verzameling van methoden die acties uitvoeren.
**Voorbeeld:**
Een `Cirkel` object kan een `radius` als gegevensveld hebben en `getArea()` als methode. Er kunnen meerdere instanties van de `Cirkel` klasse bestaan, elk met een eigen `radius` waarde.
#### 4.2.2 Klassen
Een klasse is een blauwdruk of sjabloon voor het creëren van objecten. Het definieert de gegevensvelden en methoden die objecten van die klasse zullen hebben.
* **Klasse Naam:** De naam van de klasse.
* **Gegevensvelden:** Variabelen die de informatie van een object opslaan.
* **Methoden:** Functies die binnen een klasse zijn gedefinieerd en acties uitvoeren op de objecten van die klasse.
* **Initializer:** Een speciale methode (vaak `__init__` in Python) die wordt aangeroepen bij het creëren van een nieuw object om de gegevensvelden te initialiseren.
#### 4.2.3 Constructor en het creëren van objecten
Nadat een klasse is gedefinieerd, kunnen objecten ervan worden gemaakt met behulp van de constructor.
1. De constructor creëert een object in het geheugen voor de klasse.
2. Vervolgens roept de constructor de initializer-methode van de klasse aan om het object te initialiseren en de gegevensvelden in te stellen.
De syntax voor het creëren van een object is `ClassName(argumenten)`.
#### 4.2.4 De `self` parameter
Alle methoden binnen een klasse, inclusief de initializer, hebben een eerste parameter genaamd `self`. Deze parameter verwijst naar het object zelf dat de methode aanroept. Hoewel een andere naam gebruikt kan worden, is `self` de conventie. Via `self` kunnen de gegevensvelden (instance variables) van het object worden benaderd.
> **Tip:** De `self` parameter wordt intern gebruikt door de methode, maar hoeft niet expliciet meegegeven te worden bij het aanroepen van de methode.
#### 4.2.5 Toegang tot leden van objecten
Nadat een object is aangemaakt, kunnen de gegevensvelden en methoden worden benaderd met behulp van de puntoperator (`.`), ook wel de object-lidtoegang-operator genoemd.
**Voorbeeld:**
`c.radius = 10` om een gegevensveld aan te passen, of `c.getArea()` om een methode aan te roepen.
#### 4.2.6 UML-notatie voor klassen en objecten
Unified Modeling Language (UML) biedt een grafische manier om klassen en objecten te beschrijven. Een klasse wordt weergegeven met de klassenaam bovenaan, gevolgd door de gegevensvelden en vervolgens de methoden. Objecten worden weergegeven met de notatie `objectnaam: Klassennaam` en kunnen ook hun specifieke waarden voor de gegevensvelden tonen.
#### 4.2.7 Immuutable en mutable objecten
* **Immuutable objecten:** Objecten waarvan de toestand niet kan worden gewijzigd nadat ze zijn aangemaakt. Bij elke aanpassing wordt er een nieuw object gecreëerd. Voorbeelden zijn integers en strings.
* **Mutable objecten:** Objecten waarvan de toestand wel kan worden gewijzigd nadat ze zijn aangemaakt. Voorbeelden zijn lijsten en dictionaries.
#### 4.2.8 Gegevensveldencapsulatie
Encapsulatie is het principe van het verbergen van de interne implementatiedetails van een klasse en het beschermen van de gegevensvelden tegen directe externe wijzigingen. Dit wordt bereikt door gegevensvelden "privé" te maken (in Python aangeduid met twee leidende underscores, bijv. `__radius`). Dit maakt de klasse gemakkelijker te onderhouden en voorkomt dat de gegevens worden gecorrumpeerd.
> **Tip:** Definieer gegevensvelden als privé als de klasse bedoeld is voor gebruik door andere programma's. Als de klasse alleen intern wordt gebruikt, is directe toegang vaak acceptabel.
#### 4.2.9 Klasse abstractie en encapsulatie
* **Klasse abstractie:** Scheidt de implementatie van een klasse van het gebruik ervan. De gebruiker van de klasse hoeft alleen de publieke interface (de contract, d.w.z. de handtekeningen van publieke methoden en constanten) te kennen en niet hoe de klasse intern is geïmplementeerd.
* **Encapsulatie:** Zorgt ervoor dat de interne implementatiedetails verborgen blijven voor de gebruiker.
Een klasse-implementatie gedraagt zich als een "black box" die door de client wordt gebruikt via zijn contract.
### 4.3 Procedureel programmeren versus objectgeoriënteerd programmeren
#### 4.3.1 Procedureel programmeren
In procedureel programmeren zijn gegevens en de operaties op die gegevens gescheiden. Deze methodologie vereist dat gegevens naar methoden worden verzonden. De focus ligt op sequenties van instructies en procedures.
#### 4.3.2 Objectgeoriënteerd programmeren
Objectgeoriënteerd programmeren plaatst gegevens en de bijbehorende operaties samen binnen een object. Deze aanpak lost veel problemen op die inherent zijn aan procedureel programmeren.
* **Organisatie:** OOP organiseert programma's op een manier die de echte wereld weerspiegelt, waarin objecten zowel attributen (toestand) als activiteiten (gedrag) hebben.
* **Herbruikbaarheid:** Het gebruik van objecten verbetert de herbruikbaarheid van software.
* **Onderhoudbaarheid:** Programma's worden gemakkelijker te ontwikkelen en te onderhouden.
* **Denkpatroon:** Programmeren in Python omvat denken in termen van objecten; een Python-programma kan worden gezien als een verzameling samenwerkende objecten.
**Kernverschil:** In procedureel programmeren staan functies centraal en worden gegevens apart verwerkt. In OOP staan objecten centraal, die zowel gegevens als de functies die op die gegevens werken, bevatten. Dit leidt tot een meer modulaire en onderhoudbare code, vooral bij grootschalige projecten.
---
## Veelgemaakte fouten om te vermijden
- Bestudeer alle onderwerpen grondig voor examens
- Let op formules en belangrijke definities
- Oefen met de voorbeelden in elke sectie
- Memoriseer niet zonder de onderliggende concepten te begrijpen
Glossary
| Term | Definition |
|------|------------|
| Objectgeoriënteerd programmeren (OOP) | Een programmeerparadigma dat programmeert met behulp van objecten, waarbij een object een entiteit in de echte wereld vertegenwoordigt met een unieke identiteit, staat en gedrag. |
| Object | Een entiteit in de echte wereld die distinctief geïdentificeerd kan worden, en die zowel een staat (datavelden met waarden) als gedrag (methoden) heeft. |
| Staat (van een object) | Bestaat uit een verzameling datavelden, ook wel eigenschappen genoemd, met hun huidige waarden, die de kenmerken van een object beschrijven. |
| Gedrag (van een object) | Gedefinieerd door een verzameling methoden die aangeven wat een object kan doen of welke acties het kan uitvoeren. |
| Dataveld (eigenschap) | Een variabele binnen een klasse die de staat van een object opslaat. |
| Methode | Een functie die gedefinieerd is binnen een klasse en die door objecten wordt aangeroepen om acties uit te voeren. |
| Klasse | Een blauwdruk of sjabloon voor het creëren van objecten, die datavelden (variabelen) en methoden definieert. |
| Initializer | Een speciale methode binnen een klasse die wordt aangeroepen bij het creëren van een nieuw object om de datavelden te initialiseren. In Python wordt dit vaak aangeduid met `__init__`. |
| Constructor | De syntax die wordt gebruikt om een nieuw object te creëren vanuit een klasse. Het proces omvat het creëren van het object in het geheugen en het initialiseren ervan via de initializer. |
| Instance methode | Een methode die is gedefinieerd binnen een klasse en die wordt aangeroepen door een specifiek object om acties uit te voeren die betrekking hebben op dat object. |
| `self` parameter | Een parameter die in methoden van een klasse wordt gebruikt om te verwijzen naar het object zelf waarop de methode wordt aangeroepen. Het wordt automatisch ingesteld en is essentieel voor het benaderen van instance variabelen. |
| Dot operator (.) | Wordt gebruikt om toegang te krijgen tot de datavelden en methoden van een object na creatie. |
| UML (Unified Modeling Language) | Een gestandaardiseerde grafische notatietaal die wordt gebruikt voor het visualiseren, specificeren, construeren en documenteren van software-intensieve systemen, inclusief klassen en objecten. |
| Immutable object | Een object waarvan de interne staat niet gewijzigd kan worden nadat het is aangemaakt. Elke wijziging resulteert in de creatie van een nieuw object. |
| Mutable object | Een object waarvan de interne staat wel gewijzigd kan worden nadat het is aangemaakt, zonder dat er een nieuw object wordt gecreëerd. |
| Data field encapsulation | Een principe waarbij de interne data van een object wordt beschermd tegen directe externe wijziging, meestal door private data fields te gebruiken en toegang te bieden via publieke methoden (getters en setters). |
| Class abstraction | Het proces waarbij de complexe implementatiedetails van een klasse worden verborgen voor de gebruiker, die alleen interactie heeft met de klasse via een gedefinieerd publiek interface (de 'class contract'). |
| Procedureel programmeren | Een programmeerparadigma waarbij de nadruk ligt op procedures of routines die instructies uitvoeren die op data worden toegepast. Data en operaties zijn vaak gescheiden. |