Skip to content

12. Αντικειμενοστραφής Προγραμματισμός

12.1 Εισαγωγή

  • Ο αντικειμενοστραφής προγραμματισμός είναι ο κυριάρχος τρόπος προγραμματισμού σήμερα. Σχεδόν σε όλες τις γλώσσες προγραμματσιμού έχουν εμφανιστεί διάλεκτοι που υποστηρίζουν τον αντικειμενοστραφή προγραμματισμό.
  • Αμιγώς αντικειμενοστραφής γλώσσα: Smalltalk
  • Αντικειμενοστραφής γλώσσες: Java, C#, Ruby
  • Γλώσσες που υποστηρίζουν πολλά προγραμματιστικά υποδείγματα (π.χ. προστακτικό προγραμματισμό, αντικειμειμενοστραφή προγραμματισμό): C++, Python, Objective-C
  • Συναρτησιακές γλωσσες που υποστηρίζουν αντικειμενοστραφή προγραμματισμό: CLOS, OCaml, F#, Scala

12.2 Αντικειμενοστραφής Προγραμματισμός

Η πρώτη αντικειμενοστραφής γλώσσα προγραμματισμού ήταν η SIMULA 67.

Μια αντικειμενοστραφής γλώσσα υποστηρίζει τρια βασικά χαρακτηριστικά:

  1. Αφηρημένοι Τύποι Δεδομένων
  2. Κληρονομικότητα
  3. Δυναμική πρόσδεση κλήσεων μεθόδων σε μεθόδους

12.2.2 Κληρονομικότητα

Επαναχρησιμοποίηση λογισμικού για αύξηση παραγωγικότητας. Η επαναχρησιμοποίηση επιτυγχάνεται με τους Αφηρημένους Τύπους Δεδομένων και την κληρονομικότητα.

Βασική ορολογία:

  • Κλάσεις
  • Αντικείμενα ή στιγμιότυπα κλάσεων
  • Υποκλάσεις ή παραγόμενες κλάσεις ή θυγατρικές κλάσεις
  • Υπερκλάσεις ή γονικές κλάσεις
  • Ιδιωτικά, δημόσια και προστατευμένα μέλη
  • Μέθοδοι στιγμιοτύπων και μέθοδοι κλάσεων
  • Μεταβλητες στιγμιοτύπων και μεταβλητές κλάσεων
  • Μηνύματα (κλήσεις μεθόδων)
  • Πρωτόκολλο μηνυμάτων ή διεπαφή μηνυμάτων
  • Παρακαμφθείσες μέθοδοι
  • Απλή κληρονομικότητα, πολλαπλή κληρονομικότητα

12.2.3 Δυναμική πρόσδεση (dynamic binding)

  • Δυναμική πρόσδεση μηνυμάτων σε ορισμούς μεθόδων (δυναμική αποστολή - dynamic dispatch), πολυμορφικές αναφορές
  • Αφηρημένες κλάσεις (δεν δημιουργούν στιγμιότυπα)
  • Αφηρημένες μέθοδοι (δεν έχουν σώμα παρά μόνο δηλώνεται το πρωτόκολλο της μεθόδου) - στη C++ ονομάζονται pure virtual functions

Παράδειγμα δυναμικής πρόσδεσης στη C++

dynamic_binding_example.cpp
#include <iostream>
using namespace std;

class A {
public:
  virtual void draw() { cout << "Object A: " << this << endl; }
};

class B : public A {
public:
  void draw() { cout << "Object B: " << this << endl; }
};

int main() {
  A *r = new A();
  r->draw();
  r = new B();
  r->draw();
}
Παράδειγμα εκτέλεσης:
$ g++ dynamic_binding_example.cpp
$ ./a.out
Object A: 0x11de05d30
Object B: 0x11de05e80

Παράδειγμα δυναμικής πρόσδεσης στη Java

DynamicBindingExample.java
package ch12_2_3_ex1;

class A {
    public void draw() {
        System.out.println("Object A: " + this);
    }
}

class B extends A {
    public void draw() {
        System.out.println("Object B: " + this);
    }

}

public class DynamicBindingExample {
    public static void main(String[] args) {
        A ref = new A();
        ref.draw();
        ref = new B();
        ref.draw();
    }
}
Παράδειγμα εκτέλεσης:
$ javac ch12_2_3_ex1/DynamicBindingExample.java
$ java ch12_2_3_ex1.DynamicBindingExample
Object A: 0x11de05d30
Object B: 0x11de05e80

12.3 Ζητήματα σχεδιασμού για αντικειμενοστραφείς γλώσσες

12.3.1 Η αποκλειστικότητα των αντικειμένων

Εναλλακτικές

  • Όλα είναι αντικείμενα (π.χ. Smalltalk)
  • Διατήρηση όλης της συλλογής τύπων από τη βασική προστακτική γλώσσα και πρόσθεση του μοντέλου τύπου αντικειμένων (π.χ. C++).
  • Χρήση τύπων προστακτικής γλώσσας για τους βασικούς βαθμωτούς τύπους, αλλά υλοποίηση όλων των δομημένων τύπων ως αντικειμένων (π.χ. Java)

12.3.2 Είναι οι υποκλάσεις υποτύποι;

12.3.3 Απλή και πολλαπλή κληρονομικότητα

  • Γλώσσες που υποστηρίζουν πολλαπλή κληρονομικότητα: C++, Python
  • Γλώσσες που υποστηρίζουν απλή κληρονομικότητα: Java, C#, Smalltalk

12.3.4 Κατανομή και άρση της κατανομής αντικειμένων

12.3.5 Δυναμική και στατική πρόσδεση

12.3.6 Ένθετες κλάσεις

12.3.7 Αρχικοποίηση αντικειμένων

12.4 Υποστήριξη αντικειμενοστραφούς προγραμματισμού σε συγκεκριμένες γλώσσες

12.4.1 Smalltalk

12.4.2 C++

12.4.3 Objective C

12.4.4 Java

12.4.5 C#

12.4.6 Ruby

12.5 Υλοποίηση αντικειμενοστραφών δομών

12.6 Ανάκλαση

12.6.3 Ανάκλαση στη Java

Παράδειγμα ανάκλασης στη Java

ReflectTest.java
package ch12_6_3_ex1;

import java.lang.reflect.*;

public class ReflectTest {
    public static void main(String[] args) {
        Object[] birdList = new Object[3];
        birdList[0] = new Bird1();
        birdList[1] = new Bird2();
        birdList[2] = new Bird3();
        Reflect.callDraw(birdList[2]);
        Reflect.callDraw(birdList[0]);
        Reflect.callDraw(birdList[1]);
    }
}

class Reflect {
    public static void callDraw(Object birdObj) {
        Class cls = birdObj.getClass();
        try {
            Method method = cls.getMethod("draw");
            method.invoke(birdObj);
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException(cls.getName() + " does not support draw");
        } catch (IllegalAccessException e) {
            throw new IllegalArgumentException(
                    "Insufficient accesss permissions to call draw in class " + cls.getName());
        } catch (InvocationTargetException e) {
            throw new RuntimeException();
        }
    }
}

class Bird1 {
    public void draw() {
        System.out.println("This is a draw from Bird1");
    }
}

class Bird2 {
    public void draw() {
        System.out.println("This is a draw from Bird2");
    }
}

class Bird3 {
    public void draw() {
        System.out.println("This is a draw from Bird3");
    }
}
Παράδειγμα εκτέλεσης:
$ javac ch12_6_3_ex1/ReflectTest.java
$ java ch12_6_3_ex1.ReflectTest
This is a draw from Bird3
This is a draw from Bird1
This is a draw from Bird2