oopAan de slag met oop


Opmerkingen

Object-georiënteerd programmeren (OOP) is een programmeerparadigma gebaseerd op het concept van "objecten", die gegevens kunnen bevatten, in de vorm van velden, vaak bekend als attributen; en code, in de vorm van procedures, vaak methoden genoemd.

Invoering

OOP - Object Oriented Programming is tegenwoordig een veel gebruikt programmeerparadigma. In OOP modelleren we echte problemen met behulp van objecten en hun gedrag om ze programmatisch op te lossen.

Er zijn vier belangrijke OOP-concepten

  1. Erfenis
  2. polymorfisme
  3. Abstractie
  4. inkapseling

Deze vier concepten samen worden gebruikt om programma's in OOP te ontwikkelen.

Er zijn verschillende talen die Object Oriented Programming ondersteunen. De meest populaire talen zijn

  • C ++
  • Java
  • C #
  • Python (Python is niet volledig Object Oriented, maar heeft de meeste OOP-functies ervan)

OOP Introductie

intoduction

Object Oriented Programming (meestal aangeduid als OOP) is een programmeerparadigma voor het oplossen van problemen.
Het mooie van een OO (objectgeoriënteerd) programma is dat we het programma beschouwen als een groep objecten die met elkaar communiceren, in plaats van als een sequentieel script dat specifieke orders volgt.

Er zijn veel programmeertalen die OOP ondersteunen, enkele populaire zijn:

  • Java
  • C ++
  • c #

Het is ook bekend dat Python OOP ondersteunt, maar het mist een paar eigenschappen.


OOP-terminologie

De meest basale term in OOP is een klasse .
Een klasse is in feite een object , dat een status heeft en werkt volgens zijn status.

Een andere belangrijke term is een instantie .
Beschouw een klasse als een sjabloon die wordt gebruikt om exemplaren van zichzelf te maken. De klasse is een sjabloon en de instantie (s) zijn de concrete objecten.

Een exemplaar gemaakt van de klasse A wordt meestal aangeduid als 'type A', precies zoals het type 5 is int en het type 'abcd' een tekenreeks is .

Een voorbeeld van het maken van een instantie met de naam insance1 van type (klasse) ClassA :

Java

ClassA instance1 = new ClassA();
 

C ++

ClassA instance1;
 

of

ClassA *instance1 = new ClassA(); # On the heap
 

Python

instance1 = ClassA()
 

Zoals u in het bovenstaande voorbeeld kunt zien, werd in alle gevallen de naam van de klasse vermeld en daarna waren er lege haakjes (behalve de C ++ waar de haakjes kunnen worden verwijderd als ze leeg zijn). Tussen haakjes kunnen we arguments doorgeven aan de constructeur van onze klasse.

Een constructor is een methode van een klasse die wordt aangeroepen telkens wanneer een instantie wordt gemaakt. Er kunnen argumenten voor nodig zijn of niet. Als het programmeerapparaat geen constructor opgeeft voor een klasse die ze bouwen, wordt een lege constructor gemaakt (een constructor die niets doet).
In de meeste talen wordt de constructor gedefinieerd als een methode zonder het retourtype te definiëren en met dezelfde naam van de klasse (bijvoorbeeld in een paar secties).

Een voorbeeld van het maken van een instantie met de naam b1 van type (klasse) ClassB . De constructor van ClassB neemt één argument van het type int :

Java

ClassA instance1 = new ClassA(5);
 

of

int i = 5;
ClassA instance1 = new ClassA(i);
 

C ++

ClassA instance1(5);
 

Python

instance1 = ClassA(5)
 

Zoals u ziet, lijkt het proces van het maken van een instantie sterk op het proces van het aanroepen van een functie.


Functies versus methoden

Beide functies en methoden lijken sterk op elkaar, maar in Object Oriented Design (OOD) hebben ze elk hun eigen betekenis.
Een methode is een bewerking die wordt uitgevoerd op een instantie van een klasse. De methode zelf gebruikt meestal de status van de instantie om te werken.
Ondertussen behoort een functie tot een klasse en niet tot een specifieke instantie. Dit betekent dat het geen gebruik maakt van de status van de klasse of gegevens die in een instantie zijn opgeslagen.

Vanaf nu zullen we onze voorbeelden alleen in Java laten zien, omdat OOP heel duidelijk is in deze taal, maar dezelfde principes werken in elke andere OOP-taal.

In Java heeft een functie het woord statisch als volgt:

// File's name is ClassA
public static int add(int a, int b) {
    return a + b;
}
 

Dit betekent dat u het overal vanuit het script kunt bellen.

// From the same file
System.out.println(add(3, 5));

// From another file in the same package (or after imported)
System.out.println(ClassA.add(3, 5));
 

Wanneer we de functie vanuit een ander bestand aanroepen, gebruiken we de naam van de klasse (in Java is dit ook de naam van het bestand) waartoe het behoort, dit geeft de intuïtie dat de functie bij de klasse hoort en niet bij een van de instanties ervan.

In tegenstelling kunnen we een mehod in ClassA als volgt definiëren:

// File's name is ClassA
public int subtract(int a, int b){
    return a - b;
}
 

Na deze decleratie kunnen we deze methode als volgt noemen:

ClassA a = new ClassA();
System.out.println(a.subtract(3, 5));
 

Hier moesten we een instantie van ClassA maken om de methode aftrekken te noemen. Merk op dat we NIET het volgende kunnen doen:

System.out.println(ClassA.subtract(3, 5));
 

Deze regel levert een compilatiefout op die aangeeft dat we deze niet-statische methode zonder exemplaar hebben genoemd.


De status van een klasse gebruiken

Laten we aannemen dat we onze aftrekmethode opnieuw willen implementeren, maar deze keer willen we altijd hetzelfde nummer aftrekken (voor elke instantie). We kunnen de volgende klasse maken:

class ClassB {

    private int sub_amount;

    public ClassB(int sub_amount) {
        this.sub_amount = sub_amount;
    }

    public int subtract(int a) {
        return a - sub_amount;
    }

    public static void main(String[] args) {
        ClassB b = new ClassB(5);
        System.out.println(b.subtract(3)); // Ouput is -2
    }
}
 

Wanneer we deze code uitvoeren, wordt een nieuwe instantie met de naam b van klasse ClassB gemaakt en krijgt de constructor de waarde 5 .
De constructor neemt nu het gegeven sub_bedrag en slaat het op als zijn eigen privéveld , ook wel sub_bedrag genoemd (deze conventie is erg bekend in Java, om de argumenten dezelfde te noemen als de velden).
Daarna drukken we naar de console het resultaat van het aanroepen van de methode aftrekken op b met de waarde 3 .

Merk op dat we dit bij de implementatie van aftrekken niet gebruiken this. zoals in de aannemer.
In Java hoeft this alleen te worden geschreven als er een andere variabele met dezelfde naam is gedefinieerd in dat bereik. Hetzelfde werkt met het self van Python.
Dus als we sub_bedrag gebruiken in aftrekking, verwijzen we naar het privéveld dat voor elke klasse anders is.

Nog een voorbeeld om te benadrukken.
Laten we de hoofdfunctie in de bovenstaande code als volgt wijzigen:

ClassB b1 = new ClassB(1);
ClassB b2 = new ClassB(2);

System.out.println(b1.subtract(10)); // Output is 9
System.out.println(b2.subtract(10)); // Output is 8
 

Zoals we kunnen zien, zijn b1 en b2 onafhankelijk en hebben ze elk hun eigen status .


Interfaces en erfenis

Een interface is een contract, het definieert welke methoden een klasse zal hebben en dus zijn mogelijkheden. Een interface heeft geen implementatie, het definieerde alleen wat er gedaan moet worden.
Een voorbeeld in Java zou zijn:

interface Printalbe {
    public void print();
}
 

De Printalbe- interface definieert een methode met de naam print, maar deze wordt niet geïmplementeerd (behoorlijk vreemd voor Java). Elke klasse die zichzelf verklaart als implementing deze interface moet een implementatie bieden voor de tekenmethode. Bijvoorbeeld:

class Person implements Printalbe {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void print() {
        System.out.println(name);
    }
}
 

Als Persoon zichzelf zou verklaren als Drawable te implementeren , maar geen implementatie zou leveren om af te drukken , zou er een compilatiefout zijn en zou het programma niet compileren.

Overerving is een term die verwijst naar een klasse die een andere klasse uitbreidt . Laten we bijvoorbeeld zeggen dat we nu een persoon hebben die een leeftijd heeft. Een manier om zo iemand te implementeren, is door de klasse Person te kopiëren en een nieuwe klasse te schrijven met de naam AgedPerson, die dezelfde velden en methoden heeft, maar die een andere eigenschap-leeftijd heeft.
Dit zou vreselijk zijn, omdat we onze hele code dupliceren om een eenvoudige functie aan onze klasse toe te voegen.
We kunnen overerving gebruiken om van Persoon te erven en zo alle functies te krijgen, en ze vervolgens te verbeteren met onze nieuwe functie, als volgt:

class AgedPerson extends Person {

    private int age;

    public AgedPerson(String name, int age) {
        super(name);
        this.age = age;
    }

    public void print() {
        System.out.println("Name: " + name + ", age:" + age);
    }
}
 

Er zijn een paar nieuwe dingen aan de hand:

  • We hebben het opgeslagen woord extends om aan te geven dat we erven van Persoon (en ook de implementatie ervan in Printable , dus we hoeven de implementing Printable opnieuw aan te geven).
  • We hebben het veilige woord super om de constructor van Person aan te roepen.
  • We hebben de afdrukmethode van Persoon vervangen door een nieuwe.

Dit wordt behoorlijk technisch Java, dus ik zal hier niet dieper op ingaan. Maar ik zal vermelden dat er veel extreme gevallen moeten worden geleerd over overerving en interfaces voordat u ze begint te gebruiken. Welke methoden en functies zijn bijvoorbeeld geërfd? Wat gebeurt er met privé- / openbare / beschermde velden bij het erven van een klas? enzovoorts.

Abstracte klasse

Een abstracte klasse is een vrij geavanceerde term in OOP die een combinatie van beide interfaces en overerving beschrijft. Hiermee kun je een klasse schrijven met zowel geïmplementeerde als niet-geïmplementeerde methoden / functies. In Java wordt dit gedaan met behulp van het trefwoord abstract en ik zal het niet meer uitleggen als een snel voorbeeld:

abstract class AbstractIntStack {

    abstract public void push(int element);

    abstract public void pop();

    abstract public int top();

    final public void replaceTop(int element) {
        pop();
        push(element);
    }
}
 

Opmerking: het final trefwoord geeft aan dat u deze methode niet kunt overschrijven wanneer u ervaart van deze klasse. Als een klasse definitief wordt verklaard, kan geen enkele klasse hiervan erven.