Προς το περιεχόμενο

Java - multiple inheritance


Προτεινόμενες αναρτήσεις

Δημοσ.

Γεια σας,

 

post-236879-0-61506200-1434181662_thumb.jpg

 

Στο δεύτερο ερώτημα του θέματος ουσιαστικά ζητάει την προσομοίωση μιας πολλαπλής κληρονομικότητας στη Java.

 

Οπότε φαντάζομαι θα μπορούσα να το λύσω με την χρήση ενός interface ως εξής:

class zwo{}
class agrio extends zwo {}
class mikro extends agrio {}
class megalo extends agrio {}
interface Katoikidio {}
class katoikidio extends zwo implements Katoikidio {}
class mikro_katoikidio extends mikro implements Katoikidio {}

Είναι αυτή η λύση σωστή ή θα μπορούσε να γίνει και κάτι άλλο για απάντηση στο ερώτημα;

  • Like 1
Δημοσ.

Multiple inheritance δεν υπαρχει στην Java. Multiple inheritance σημαινει μια κλαση να κανει extend 2 αλλες κλασεις ταυτοχρονα. Αυτο δεν επιτρεπεται. 

Interfaces µπορεις να εχεις πανω απο ενα.

Το παραδειγμα δειχνει multiple levels of inheritance. 

 

 

 Παραδειγμα multiple inheritance θα ητανε λεμε τωρα ενα σπιτισιο λιονταρι που κανει

  1. class λιονταρι extends Agrio {}, Katoikidio {}

(Katoikidio την κλαση εννοω).

 

Συμαντικοτατο TIP : Οταν φτιαχνεις interface και εχει το ιδιο ονομα με μια κλαση, οπως στην περιπτωση σου να το ονομαζεις I+ το ονομα, αρα IKatoikidio. (προσωπικα παντα ονομαζω intrafaces με I+.., )

 

Περα απο αυτα τα περιφεριακα, η λυση σου ειναι σωστη.

  • Like 1
Δημοσ.

εμένα μου φαίνονται όλα σαν interfaces, zwo, agrio, katoikidio, megalo, mikro.

όπως το έχεις, το katoikidio δεν κληρονομεί από το zwo τίποτα, οπότε χάνεις κάτι εκεί.

Δημοσ.

εμένα μου φαίνονται όλα σαν interfaces, zwo, agrio, katoikidio, megalo, mikro.

όπως το έχεις, το katoikidio δεν κληρονομεί από το zwo τίποτα, οπότε χάνεις κάτι εκεί.

Μα στο κατοικίδιο interface μόνο τα χαρακτηριστικά του κατοικίδιου θα έχεις. Το ζώα είναι Base class δεν έχει νόημα να του κάνεις Interface εκτός άμα το interface ονομάζεται ζωντανοί οργανισμοί ξέρω γω.

Δημοσ.

αν πάει όπως λες και το agrio interface είναι.

όπως δειχνει στη εικόνα, ότι είναι το agrio είναι και το katoikidio.

Δημοσ.

Είναι ας κάνει και

το άγριο Interface. Αλλά το ζώα σαν abstract Base class δεν σου προσφέρει κάτι να το κάνεις Interface. Και το άγριο είναι abstract αν υποθέσουμε ότι είναι πάντα η μικρά η μεγάλα.

Δημοσ.

αυτός που έκανε την ερώτηση δεν κοιτάζει πως θα έχει καλύτερο αποτέλεσμα, αλλά πως είναι σωστό σύμφωνα με την εκφώνηση.

στην εικόνα φαίνεται ότι το agrio και το katoikidio κληρονομούν από το zwo.

Δημοσ.

αυτός που έκανε την ερώτηση δεν κοιτάζει πως θα έχει καλύτερο αποτέλεσμα, αλλά πως είναι σωστό σύμφωνα με την εκφώνηση.

στην εικόνα φαίνεται ότι το agrio και το katoikidio κληρονομούν από το zwo.

Και στο παράδειγμα του κληρονομουνε από την superclass zwa.

Δημοσ.

στο παράδειγμά του, το katoikidio δεν "παίρνει" τίποτα από το zwo στην εικόνα έχει βελάκι. βαρέθηκα δεν απαντάω άλλο, άκου τον papakaliati

Δημοσ. (επεξεργασμένο)

στο παράδειγμά του, το katoikidio δεν "παίρνει" τίποτα από το zwo στην εικόνα έχει βελάκι. βαρέθηκα δεν απαντάω άλλο, άκου τον papakaliati

η κλάση κατοικίδιο στο παράδειγμα μου extends zwo άρα κληρονομεί τα χαρακτηριστικά της κλάσης zwo και implements katoikidio άρα θα πρέπει και να υλοποιεί τις μεθόδους που περιγράφονται στο interface Katoikidio (Papakaliati thanks για το tip, θα το χρησιμοποιήσω).

 

εμένα μου φαίνονται όλα σαν interfaces, zwo, agrio, katoikidio, megalo, mikro.

όπως το έχεις, το katoikidio δεν κληρονομεί από το zwo τίποτα, οπότε χάνεις κάτι εκεί.

Αν τα έκανα όλα σαν interfaces νομίζω δεν θα μπορούσα να υλοποιήσω μια abstract μέθοδο της κλάσης agrio στις κλάσεις megalo και mikro.

Επεξ/σία από dadusig
Δημοσ.
Το θέμα είναι πολύ ενδιαφέρον από διδακτικής άποψης και ζητώ συγγνώμη για το σεντόνι που ακολουθεί.  :)
 
Το πρόβλημα που θέτει η άσκηση είναι, αν το δεις από υψηλό επίπεδο, η μοντελοποίηση των ιδιοτήτων και των σχέσεων αυτών των πραγμάτων που απαρτίζουν το λεγόμενο domain model της εκφώνησης. Υπάρχουν δηλαδή έννοιες όπως ζώο, κατοικίδιο, κλπ που έχουν πρώτον κάποια εγγενή ιδιότητα η κάθε μία μόνη της και δεύτερον σχετίζονται και μεταξύ τους. Κι εμείς πρέπει να χρησιμοποιήσουμε τα εργαλεία που δίνει η γλώσσα για να αποδώσουμε αυτές τις ιδιότητες στο προγραμματιστικό μοντέλο του κώδικα που αναγνωρίζει ο compiler. Εδώ το μοντέλο αυτό (και άρα τα δομικά στοιχεία που το απαρτίζουν) είναι object-oriented, οπότε τα εργαλεία μας classes, interfaces και objects.
 
Μετά τον παραπάνω πρόλογο θέλω να επιστήσω πάλι την προσοχή στο τι ακριβώς κάνουμε: χρησιμοποιούμε object-oriented τεχνικές για να μοντελοποιήσουμε τα ζώα. Οι τεχνικές αυτές έχουν κάποια χαρακτηριστικά, για παράδειγμα: μια class κληρονομεί όλες τις μεθόδους και τη συμπεριφορά της base class της, ή χαρακτηρίζοντας μια μέθοδο private/protected/public μπορείς να ελέγξεις πού επιτρέπεται η χρήση της.
 
Το σημαντικό εδώ είναι πως αυτά τα χαρακτηριστικά είναι δύο ειδών: α) αυτά που έχουν άμεση αντιστοιχία με τα χαρακτηριστικά του domain model ("όλα τα κατοικίδια είναι επίσης ζώα") και β) αυτά που δεν έχουν να κάνουν με το domain model και λειτουργούν μόνο στο επίπεδο του προγραμματιστικού μοντέλου. Αυτός ο διαχωρισμός δεν είναι καθόλου προφανής σε όσους δεν έχουν ήδη εμπειρία με OOP, αλλά είναι ταυτόχρονα η λεπτομέρεια που κάνει τη διαφορά ανάμεσα σε μια λύση "ιδιαίτερα καλή" και σε μία που "γενικά δουλεύει"  (εδώ μάλιστα είναι έντονος ο παραλληλισμός με τους διάφορους τρόπους που προσεγγίζει κανείς αλγοριθμικά ένα πρόβλημα).
 
Εδώ λοιπόν πρέπει να βγάλουμε στη φόρα το μεγάλο "μυστικό" με την έννοια της class. Μια class κάνει δύο πράγματα:
  1. Μοντελοποιεί μια οντολογική σχέση ανάμεσα σε δύο έννοιες, όπως π.χ. "το κατοικίδιο είναι ζώο" που ήδη αναφέρθηκε.
  2. Χρησιμοποιείται σα μηχανισμός επαναχρησιμοποίησης της υλοποίησης (implementation sharing), π.χ. αν το άγριο ζώο όταν καλείς τη μέθοδο eat() πάει να βοσκήσει, τότε όλα τα μικρά ή μεγάλα άγρια ζώα εξ' ορισμού επίσης θα πηγαίνουν να βοσκήσουν με τον ίδιο τρόπο.
 
Αυτά τα δύο πράγματα είναι πολύ διαφορετικά: το πρώτο έχει να κάνει με το domain model μας (αν δεν υπήρχε θα έπρεπε να έχουμε άλλα εργαλεία για να λύσουμε το πρόβλημα), ενώ το δεύτερο όχι (αν δεν υπήρχε θα μπορούσαμε απλά να κάνουμε copy/paste την υλοποίηση της eat() σε κάθε μία class ζώου ξεχωριστά).
 
Ένα interface, από την άλλη, έχει μόνο το πρώτο από τα δύο: μοντελοποιεί σχέσεις. Δεν έχει υλοποίηση για να επαναχρησιμοποιηθεί.
 
Από τα παραπάνω τώρα βγαίνει το εξής σημαντικό συμπέρασμα: Αν σε κάποιο μοντέλο δεις κάπου ένα interface, αυτό μπορεί να σημαίνει μόνο ένα πράγμα, ότι έτσι καθορίζουμε μια έννοια και ενδεχομένως τη συνδέουμε με άλλες (αυτές που κάνουν implement το interface). Αν όμως δεις μια class, τότε δεν είναι άμεσα προφανές τι συμβαίνει. Υπάρχει για να συνδέσει έννοιες, για να γίνει implementation sharing ή και για τα δύο ταυτόχρονα;
 
Ταυτόχρονα, το επιπλέον implementation sharing έρχεται μαζί μ' ένα περιορισμό: δεν επιτρέπεται multiple inheritance, ενώ επιτρέπεται multiple interface implementation. Επομένως το να χρησιμοποιήσουμε μια class εκεί όπου θα μπορούσαμε να έχουμε ένα interface έχει κι ένα μεγάλο μειονέκτημα: εξαναγκάζουμε όλο τον κώδικα που θα βασιστεί στο σχέδιό μας να μην κάνει inherit από καμία άλλη class στα σημεία όπου υπάρχουν class μέρος του σχεδίου μας. Αυτό το μειονέκτημα δεν εμφανίζεται συνήθως στις απλές περιπτώσεις που διδάσκονται, αλλά είναι τεράστιας σημασίας και η μη εμφάνισή του αποτελεί βασικό χαρακτηριστικό ενός καλού design.
 
Έχοντας τώρα στο νου αυτό το υπόβαθρο, ας δούμε ένα design για το πρόβλημα που έχουμε. Το design θα βασιστεί στους παρακάτω απλούς κανόνες, οι οποίοι προκύπτουν από όσα είπαμε:
  1. Για κάθε έννοια που έχουμε, φτιάχνουμε ένα interface.
  2. Για κάθε έννοια που είναι "χειροπιαστή" (που χρειάζεται να δημιουργούμε instances), φτιάχνουμε μια final class.
  3. Σε κάθε περίπτωση που θέλουμε να κάνουμε implementation sharing, φτιάχνουμε μια class η οποία είναι και abstract.
Σχηματικά δηλαδή,
interface      == μοντελοποίηση
abstract class == implementation sharing
final class    == θέλουμε να φτιάξουμε instances από αυτό

Δε θα χρησιμοποιήσουμε τίποτα εκτός από αυτά, και ιδιαίτερα δε θα χρησιμοποιήσουμε class η οποία δεν είναι ούτε abstract ούτε final.

 
Επομένως έχουμε κατ' ελάχιστο:
interface IZwo
interface IAgrioZwo extends IZwo
interface IKatoikidio extends IZwo
interface IMikroAgrioZwo extends IAgrioZwo
interface IMegaloAgrioZwo extends IAgrioZwo
final class Katoikidio implements IKatoikidio
final class MikroAgrioZwo implements IMikroAgrioZwo
final class MegaloAgrioZwo implements IMegaloAgrioZwo
Αν τώρα κάπου θέλουμε να κάνουμε implementation sharing, βάζουμε επίσης μια abstract class. Για παράδειγμα αν το κάναμε αυτό στο άγριο ζώο:
interface IZwo
interface IAgrioZwo extends IZwo
abstract class AgrioZwo implements IAgrioZwo
interface IKatoikidio extends IZwo
interface IMikroAgrioZwo extends IAgrioZwo
interface IMegaloAgrioZwo extends IAgrioZwo
final class Katoikidio implements IKatoikidio
final class MikroAgrioZwo extends AgrioZwo implements IMikroAgrioZwo
final class MegaloAgrioZwo extends AgrioZwo implements IMegaloAgrioZwo
Επιστώ την προσοχή στο πόσο προφανές είναι το τι γίνεται εδώ -- προφανές όχι με την έννοια ότι δε χρειάζεται σκέψη, αλλά επειδή υπάρχει πάντα μόνο μία λογική και σωστή απάντηση στην ερώτηση "γιατί έγινε έτσι αυτό εδώ;". Για παράδειγμα, στο "abstract class AgrioZwo implements IAgrioZwo" γίνεται ξεκάθαρο ότι το AgrioZwo εκτελεί μόνο σκοπούς implementation sharing, και άρα δε θα έπρεπε να χρησιμοποιείται καθόλου για λόγους μοντελοποίησης (π.χ. θα ήταν λάθος να έχουμε κάπου μια method που παίρνει AgrioZwo σαν παράμετρο). Εκεί που κάνουμε μοντελοποίηση χρησιμοποιούμε μόνο το interface IAgrioZwo, το οποίο με τη σειρά του προορίζεται φανερά για το σκοπό αυτό μιας και δεν έχει καμία άλλη χρήση. Και αυτή η ύπαρξη μόνο μίας ικανοποιητικής απάντησης στο "γιατί" είναι κάτι καλό: όσο περίπλοκο κι αν είναι το μοντέλο, αν σε κάθε βήμα δεν υπάρχει αμφιβολία τότε τελικά μπορείς να το κατανοήσεις χωρίς δράματα.
 
Εδώ λοιπόν ερχόμαστε στο δεύτερο ζήτημα, όπου μας δίνεται μια νέα πληροφορία (για τον pumba). Πώς θα το μοντελοποιήσουμε; Κανένα πρόβλημα, τα έχουμε ήδη όλα έτοιμα και δεν υπάρχουν εμπόδια.
sealed class Gourounaki implements IMikroAgrioZwo, IKatoikidio
Σε περίπτωση που υπάρχει π.χ. και implementation στην AgrioZwo τότε κάνουμε και extend αυτήν και πάλι χωρίς πρόβλημα. Σε περίπτωση που υπάρχει επιπλέον implementation τότε βέβαια δεν επιτρέπεται multiple inheritance και με αυτό το design δε μπορούμε να πάρουμε αυτόματα όλο το implementation που θα θέλαμε και θα καταφύγουμε σε copy/paste, αλλά τουλάχιστον δεν έχουμε αποτύχει στο βασικότερο σκοπό μας: να εκφράσουμε ότι "ο Πούμπα το Γουρουνάκι είναι ΜικρόΆγριοΖώο και ταυτόχρονα Κατοικίδιο".
 
Βέβαια δε θέλω να πω ότι το παραπάνω μοντέλο είναι η τέλεια και αξεπέραστη λύση. Υπάρχουν αλλαγές που αν ζητηθούν θα πρέπει να αλλάξουμε τη δομή της λύση που δώσαμε κι όχι απλά να φτιάξουμε μια νέα class Gourounaki (αυτά τα sealed class δεν είναι πάντα καλή ιδέα, τα προώθησα για να κάνω ένα point). Ούτε καν ότι είναι ένα σχετικά καλό μοντέλο, μιας και αν δεν το ζητούσε η εκφώνηση θα ήταν τουλάχιστον ύποπτο το ότι σκεφτόμαστε να δημιουργήσουμε κάτι που είναι "μικρό άγριο ζώο" (γιατί το "μικρό άγριο" έχει καλύτερη μεταχείριση από το "τριχωτό άγριο"? και τι γίνεται αν θέλω κάτι που είναι μικρό τριχωτό και άγριο?). Αλλά είναι καλό για να δείξει τον τρόπο προσέγγισης του προβλήματος και το ποιά είναι μερικά από τα βασικά αλλά όχι άμεσα εμφανή πράγματα που πρέπει να έχουμε υπόψη.
 
Κλείνοντας, να κάνω κι ένα μικρό rant σχετικά με τη διδασκαλία του OOP όπως γίνεται κλασικά με ασκήσεις σαν αυτή που συζητάμε: κανείς δε σου λέει ότι μια class κάνει δύο διαφορετικά πράγματα και έρχεται με περιορισμούς, οπότε εκεί που δε σε ενδιαφέρει να κάνεις και τα δυό ή ακόμα περισσότερο εκεί που θέλεις να μην έχεις περιορισμούς, δεν είναι το σωστό εργαλείο. Όμως επειδή στη ζωή τα δεδομένα αλλάζουν και κανείς δε μπορεί να προβλέψει το μέλλον, δε μπορούμε να είμαστε σίγουροι πού θα χρειαστεί να μην έχουμε περιορισμούς οπότε είναι βασικό να μη δεσμευόμαστε όσο είναι δυνατόν. Δηλαδή να χρησιμοποιούμε interfaces.
 
Και παρόλα αυτά, σε ξεκινούν από την τελείως λάθος κατεύθυνση
 
4224524.jpg
 
με αποτέλεσμα αντί να μάθει κανείς object oriented design, να μαθαίνει όπως λένε ότι when all you have is a hammer, everything looks like a nail.
 
/rant
 
Ελπίζω να βρήκατε το παραπάνω ενδιαφέρον όσο το θεωρώ και γω σημαντικό, κι ευχαριστώ για την υπομονή να φτάσετε μέχρι το τέλος. Αφήνω τέλος κι αυτά τα links για τους μερακλήδες:
 
 
 
  • Like 13
Δημοσ.

Well said defacer, ήθελα και εγώ βλέποντας την συγκεκριμένη άσκηση να αρχίσω το rant για το τί διδάσκεται και πως δίνονται λάθος κατευθύνσεις.

 

Να σημειώσω επίσης ότι στη πράξη ο πιό συνήθης τρόπος με τον οποίο αντιμετωπίζουμε κάτι το οποίο θα ήθελε Multiple Inheritance είναι το Composition.

 

Οπότε θα είχαμε για παράδειγμα μια κλάση EatingModule την οποία θα είχαν όλα τα ζώα. Ετσι πολύ εύκολα μπορείς να έχεις ένα MikroAgrioKatoikidio το οποίο κάνει eat(); με το DomesticEatingModule άρα τρώει είναι μεν άγριο αλλά ο τρόπος που τρώει είναι σαν κατοικίδιο.

 

Εδώ φυσικά σε δεσμεύει η άσκηση, αλλά δυστυχώς σε βάζει σε λάθος δρόμο.

Δημοσ.

Well said defacer, ήθελα και εγώ βλέποντας την συγκεκριμένη άσκηση να αρχίσω το rant για το τί διδάσκεται και πως δίνονται λάθος κατευθύνσεις.

 

Να σημειώσω επίσης ότι στη πράξη ο πιό συνήθης τρόπος με τον οποίο αντιμετωπίζουμε κάτι το οποίο θα ήθελε Multiple Inheritance είναι το Composition.

 

Οπότε θα είχαμε για παράδειγμα μια κλάση EatingModule την οποία θα είχαν όλα τα ζώα. Ετσι πολύ εύκολα μπορείς να έχεις ένα MikroAgrioKatoikidio το οποίο κάνει eat(); με το DomesticEatingModule άρα τρώει είναι μεν άγριο αλλά ο τρόπος που τρώει είναι σαν κατοικίδιο.

 

Εδώ φυσικά σε δεσμεύει η άσκηση, αλλά δυστυχώς σε βάζει σε λάθος δρόμο.

 

Το αστείο είναι ότι για λόγους που είναι προφανείς αν έχεις εμπειρία, το inheritance είναι πολλές φορές το τελευταίο πράγμα που κάνεις, όταν δεν έχεις πλέον καμία άλλη επιλογή. Αλλά είναι το πρώτο πράγμα που διδάσκεται! Και μετά class extends αέρααααααααααααα!

 

Αυτό που λες EatingModule είναι σε OOD όρους ένα strategy, κλασική μέθοδος. Υπάρχουν και διάφοροι άλλοι τρόποι, π.χ. "behaviors" (γενικός ο όρος, μπορεί να εφαρμοστεί με διάφορες μορφές), visitor pattern, traits/mixins και άλλα.

 

Μάλιστα η Java 8 επιτρέπει mixins, τα οποία ονομάζει default methods. Οπότε κάλλιστα θα μπορούσε, ανάλογα με τις απαιτήσεις, να γίνει κι έτσι.

  • Like 1
Δημοσ.

Εγω απορώ με την αντίδραση σας και το rant . Το παράδειγμα με το animal και ότι παράφραση μπορει να έχει στην συνέχεια ειναι από τα πλέον κλασικά παραδείγματα για να εισάγει κάποιος τους προγραμματιστές στην έννοια της κληρονομικότητας . Αρκετά καθαρό και εύπεπτο. Θεωρώ οτι η άσκηση υπηρετεί άψογα τον σκοπό της . Δηλαδη να μάθει στους χρηστές τι είναι κληρονομικότητα με ένα απλό παράδειγμα , να παρουσιασει στα παιδιά το limitation της Java όσον αφορά την πολλαπλή κληρονομικότητα και να παροτρύνει τα παιδιά να σκεφτούν τον τρόπο που θα προσπεράσουν σε δεύτερη φαση αυτο το πρόβλημα ενώ ουσιαστικά εχουν ήδη μια αρχικη μοντελοποιηση . Τα υπόλοιπα που λέτε ειναι μεν ενδιαφέροντα αλλα δεν αποσκοπούν να διδάξουν τα βασικα αλλα να μπερδεψουν εναν αρχάριο που ακομα δεν έχει κατανοήσει πλήρως πως και τι

Δημοσ.

Το αστείο είναι ότι για λόγους που είναι προφανείς αν έχεις εμπειρία, το inheritance είναι πολλές φορές το τελευταίο πράγμα που κάνεις, όταν δεν έχεις πλέον καμία άλλη επιλογή. Αλλά είναι το πρώτο πράγμα που διδάσκεται! Και μετά class extends αέρααααααααααααα!

   Θεωρω τουλαχιστον υπερβολη να πεις οτι ειναι το τελευταιο πραγμα που κανεις το inheritance. Ειναι το καλυτερο εργαλειο αμα θελεις να αποφυγεις το πολυ copy paste και τα λαθη που απορρεουν απο αυτο, τα οποια ειναι με διαφορα και τα πιο υπουλα.

  Και δεν το συζηταω καν για καποιον με υποβαθρο c++, που φυσικα και το αλφα και το ωμεγα.

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...