Χμμμ… Από πού να τα πιάσεις και που να τα αφήσεις τα Interfaces…
Ας ξεκινήσουμε καλύτερα από το τι είναι κλάση. Χωρίς να μπούμε σε επιστημονικούς ορισμούς, με μία κλάση καθορίζουμε τα πεδία (fields), τις ιδιότητες (properties), τα γεγονότα (events) και τις μεθόδους (methods) ενός τύπου. Ένα interface προσδιορίζει ότι ένας τύπος θα πρέπει να υλοποιεί συγκεκριμένα properties ή/και methods αλλά δεν ενδιαφέρεται για το ποιος τύπος θα είναι αυτός και πως θα τα υλοποιήσει.
Ένα παράδειγμα του παραπάνω είναι το εξής: Έχω ένα interface που ονομάζεται «Παράθυρο» και αυτό το interface προσδιορίζει μία μέθοδο που ονομάζεται «Άνοιξε». Αυτό το interface μπορεί να το χρησιμοποιήσει η κλάση «Σπίτι» αλλά και η κλάση «Αυτοκίνητο». Η κάθε μία θα υλοποιήσει την μέθοδο «Άνοιξε» με δικό της τρόπο αλλά και οι δύο, από τη στιγμή που θα πουν ότι χρησιμοποιούν το interface «Παράθυρο», πέρα από οτιδήποτε άλλο κάνουν, είναι υποχρεωμένες να υλοποιήσουν τη μέθοδο «Άνοιξε».
Σε κώδικα θα είχαμε κάτι όπως το παρακάτω:
Public Interface IWindow
Public Method Open
End Interface
Κατά σύμβαση στο ονόματα των interfaces βάζουμε το “I” μπροστά.
Public Class House
Implements IWindow
Public Method Open Implements IWindow.Open
{turn lever and push code here}
End Method
{other stuff here}
End Class
Public Class Car
Implements IWindow
Public Method Open Implements IWindow.Open
{press green button on the door}
End Method
{other stuff here}
End Class
Η αλήθεια είναι ότι εκ πρώτης όψεως φαίνεται λίγο χαζό… Για ποιόν λόγο να θέλει κανείς να κάνει κάτι τέτοιο και να μην υλοποιήσει απλώς τη μέθοδο Open στις κλάσεις του;
Βασικά, τα Interfaces χρησιμοποιούνται για να επιβάλουν πρότυπα. Ένα απλό παράδειγμα από αυτό είναι το εξής: Φαντάσου ότι έχεις μια ομάδα από developers και ο καθένας έχει αναλάβει να υλοποιήσει ένα τμήμα από ένα μεγάλο project. Αν γίνει συμφωνία να χρησιμοποιούν όλοι συγκεκριμένα interfaces (που έχουν προκύψει από το σχεδιασμό) τότε είναι δυσκολότερο κάποιος να ξεχάσει να συμπεριλάβει κάποια μέθοδο, κάποιος άλλος να υλοποίηση τη μέθοδο αλλά με διαφορετικές παραμέτρους από αυτές που συμφωνήθηκαν, κοκ.
Τώρα, θα μου πεις, «άρα, εγώ που είμαι ένας απλός και μόνος developer δε με απασχολεί το θέμα». Εδώ λοιπόν είναι όλο το ζουμί των interfaces! Όταν κάνεις development σε .NET είσαι ήδη μέλος μιας μεγάλης ομάδας από developers και μπορείς να χρησιμοποιήσεις interfaces που έχουν ήδη καθοριστεί!
Ένα πιο πρακτικό παράδειγμα:
Έχεις δει ότι στο .ΝΕΤ framework όλα τα array έχουν τη μέθοδο sort. Αν λοιπόν έχω ένα array από αριθμούς και πω arrayname.sort τότε αυτό ταξινομείται. Αν όμως αντί για αριθμούς έχω objects από υπαλλήλους, τότε τι είδους ταξινόμηση γίνεται; Οι καλοί άνθρωποι που σχεδίασαν το Framework είχαν προβλέψει τέτοιες περιπτώσεις. Δηλαδή όχι ακριβώς αυτή την περίπτωση αλλά γενικά κάποιος να θέλει να ταξινομήσει objects βάσει της δικής του λογικής. Και γι αυτόν το λόγο καθόρισαν ότι υπάρχει ένα Interface που λέγεται IComparable. Αυτό προσδιορίζει μία μέθοδο που λέγεται CompareTo, παίρνει ως όρισμα ένα Object και επιστρέφει έναν ακέραιο. Έτσι λοιπόν, μπορώ στην κλάση μου να πω ότι «εγώ χρησιμοποιώ το IComparable και έτσι υλοποιώ το function CompareTo».
Όταν θα γεμίσω λοιπόν το array με τα objects μου και πω array.sort η μέθοδος αυτή θα κοιτάξει μήπως τα objects που περιέχει το array υλοποιούν το IComparable και τότε θα τρέξει την CompareTo (που είναι σίγουρη ότι υλοποιούν) και βάσει του αριθμού που θα τις επιστρέψουν, θα αποφασίσει ποιο είναι το μεγαλύτερο object για να κάνει τη ταξινόμηση. Είναι κάτι σαν μηχανισμός plug-in δηλαδή!
Ιδού η κλάση Employee
Public Class Employee
Implements IComparable
Public name As String
Public level As Integer
Public HiringDate As DateTime
Public Sub New(ByVal name As String, ByVal level As Integer, ByVal hiringDate As DateTime)
Me.name = name
Me.level = level
Me.HiringDate = hiringDate
End Sub
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
If obj Is Nothing Then Return 1
If Not (TypeOf obj Is Employee) Then
Throw New ArgumentException
End If
Dim anEmployee As Employee = obj
If level < anEmployee.level Then
Return -1
Else
If level = anEmployee.level Then
If HiringDate < anEmployee.HiringDate Then
Return -1
Else
If HiringDate = anEmployee.HiringDate _
Then
Return 0
Else
Return 1
End If
End If
Else
Return 1
End If
End If
End Function
End Class
Αν θα διαβάσεις την υλοποίηση του CompareTo, θα δεις ότι η ταξινόμηση των υπαλλήλων-objects γίνεται με βάση το property level και αν αυτό είναι ίδιο, με βάση την παλαιότητα (HireDate property).
Και ορίστε πως γεμίζουμε το array και το ταξινομούμε:
Dim myEmployees(9) As Employee
myEmployees(0) = New Employee("a", 2, New DateTime(1990, 1, 1))
myEmployees(1) = New Employee("b", 2, New DateTime(2000, 1, 1))
myEmployees(2) = New Employee("c", 2, New DateTime(1990, 1, 1))
myEmployees(3) = New Employee("d", 4, New DateTime(2000, 1, 1))
myEmployees(4) = New Employee("e", 4, New DateTime(1990, 1, 1))
myEmployees(5) = New Employee("f", 4, New DateTime(2000, 1, 1))
myEmployees(6) = New Employee("g", 1, New DateTime(1990, 2, 5))
myEmployees(7) = New Employee("h", 1, New DateTime(2000, 1, 1))
myEmployees(8) = New Employee("i", 1, New DateTime(1990, 1, 1))
myEmployees(9) = New Employee("j", 0, New DateTime(2001, 1, 1))
Console.WriteLine("The Array instance initially contains values:")
PrintIndexAndValuesUsingALoop(myEmployees)
Array.Sort(myEmployees)
Console.WriteLine("Sorted Array:")
PrintIndexAndValuesUsingALoop(myEmployees)
Η χρήση και η αξία των interfaces δεν σταματά εδώ. Απλά, αυτό είναι ό,τι πιο πρακτικό μπορεί να συναντήσεις. Κατόπιν, μπορείς να κάνεις διάφορα κολπάκια καθώς θα εμβαθύνεις στον object oriented προγραμματισμό, αλλά αυτό είναι άλλη ιστορία.
Vir prudens non contra ventum mingit