Παρασκευή 23 Δεκεμβρίου 2016

Classic Spaceship Example (OOP)

Παρακάτω είναι το κλασικό παράδειγμα με το διαστημόπλοιο, το οποίο σε C++ έχει πρόβλημα, γιατί οι συναρτήσεις μπορούν να καθοριστούν με overload να δουλέψουν για διαφορετικά αντικείμενα αλλά αυτό γίνεται την ώρα που κάνουμε compile, οπότε χάνουμε την δυναμική συμπεριφορά όταν αλλάζουμε interface.
Στη Μ2000 δεν έχουμε πολλαπλά interface, αλλά κάνουμε χρήση του αντικειμένου Ομάδα (Group) τα οποία μπορούμε να φτιάξουμε είτε απευθείας με την εντολή Ομάδα ή με συνάρτηση που γυρίζει Ομάδα και φτιάχνεται με την εντολή Κλάση. Οι κλάσεις στην Μ2000 είναι συναρτήσεις που δίνουν αντικείμενα ενός και μόνο τύπου: Ομάδα. Εδώ για να ξεχωρίσουμε τύπους, βάζουμε σε κάθε περίπτωση ένα όνομα τύπου σε μια "συμφωνημένη" ιδιότητα (μεταβλητή στην ομάδα), εδώ Iam$.
Αντί να κάνουμε Overload στο τμήμα (module) το οποίο δεν έχει νόημα στην Μ2000, δίνουμε μια παράμετρο Α η οποία όμως πρέπει να είναι ομάδα. Κάθε φορά ελέγχουμε το Iam$ για να κάνουμε αυτό που θέλουμε στη συγκεκριμένη ομάδα. Εδώ το πέρασμα της ομάδας γίνεται με αντιγραφή (by value). Αν χρεισιμοποιούσαμε το &Α τότε θα έπρεπε στην κλήση του τμήματος (module) να δώσουμε το χαρακτήρα & μπροστά από το όνομα της ομάδας για να την περάσουμε με αναφορά.


\\ M2000 has no types for classes
\\ so for this example we use property Iam$ for indicator of type
\\ A Class is a function which return an object (Group named in M2000)
\\ In M2000 modules and functions are open to include any list of parameters
\\ list of parameters added to a special stack
\\ functions clear this stack at the end
\\ modules use the same special stack to return values also
\\ Here we use module Collide only with one parameter
\\ But module SpaceShip (Look class SpaceShip) is a constructor and can be called
\\ as a function and return an object and this is the real constructor
\\ or as an object module for manipulating (we can add temporary functions or modules)

\\ This is an example from https://en.wikipedia.org/wiki/Double_dispatch#Classic_Spaceship_Example
\\
Class ApolloSpacecraft {
      Iam$="ApolloSpacecraft"
}
Class Asteroid {
      Iam$="Asteroid"
      Module CollideWith {
            \\ A is a Group (object in m2000)
           Read A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "Asteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "Asteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }

}
Class SpaceShip {
      Iam$="SpaceShip"
      module SpaceShip {
            \\ this is module but stand for a construction function for Class
            If match("G") then {
                  \\ if we provide an object (Group) then we can use it
                  read A
                  \\ in next statement we copy A properties to this
                  \\ so we copy a new value for Iam$
                  this=A
            }
      }
}
Class ExplodingAsteroid {
      Iam$="ExplodingAsteroid"
      Module CollideWith {
           Read A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "ExplodingAsteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "ExplodingAsteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }      
}
\\ at the left side we make a group from the function at the right
\\ inside interpreter push an unamed object to a register and then
\\ all elements (variables, functions, modules, etc) get name prefix and became static in this module
theAsteroid=Asteroid()
theSpaceShip=SpaceShip()
theApolloSpacecraft=SpaceShip(ApolloSpacecraft())
theAsteroid.CollideWith theSpaceShip
theAsteroid.CollideWith theApolloSpacecraft
\\ ok
theExplodingAsteroid=ExplodingAsteroid()
theExplodingAsteroid.CollideWith theSpaceShip
theExplodingAsteroid.CollideWith theApolloSpacecraft
\\ ok
\\ link make all variables of theExplodingAsteroid references to a new group
\\ named theAsteroidReference. Second reference for a name is an error.
link theExplodingAsteroid to theAsteroidReference
theAsteroidReference.CollideWith theSpaceShip
theAsteroidReference.CollideWith theApolloSpacecraft
\\ ok
link theApolloSpacecraft to theSpaceShipReference
theAsteroid.CollideWith theSpaceShipReference
theAsteroidReference.CollideWith theSpaceShipReference
\\ ok
\\ print  Asteroid hit an ApolloSpacecraft
\\ and   ExplodingAsteroid hit an ApolloSpacecraft


anotherSpaceShip=SpaceShip()
theAsteroid.CollideWith anotherSpaceShip
theAsteroidReference.CollideWith anotherSpaceShip
\\ prints  Asteroid hit a SpaceShip
\\ and     ExplodingAsteroid hit a SpaceShip
\\ We put a join of theApolloSpacecraft to anotherSpaceShip
\\ if anotherSpaceShip has some other items then these are also there.
\\ so only the same items changed and for the example Iam$ is changed too.
anotherSpaceShip=theApolloSpacecraft
theAsteroid.CollideWith anotherSpaceShip
theAsteroidReference.CollideWith anotherSpaceShip
\\ prints  Asteroid hit an ApolloSpacecraft
\\ and     ExplodingAsteroid hit an ApolloSpacecraft
finalSpaceShip=SpaceShip(theApolloSpacecraft)
theAsteroid.CollideWith finalSpaceShip
theAsteroidReference.CollideWith finalSpaceShip
\\ prints  Asteroid hit an ApolloSpacecraft
\\ and     ExplodingAsteroid hit an ApolloSpacecraft

In an new module we place the program bellow. Here we simulating the visitor pattern. We place a module to SpaceShip.
Class ApolloSpacecraft {
      Iam$="ApolloSpacecraft"
}
Class Asteroid {
      Iam$="Asteroid"
      Module CollideWith {
            \\ A is a Group (object in m2000)
           Read A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "Asteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "Asteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }
  
}
Class SpaceShip {
      Iam$="SpaceShip"
      module SpaceShip {
            \\ this is module but stand for a construction function for Class
            If match("G") then {
                  \\ if we provide an object (Group) then we can use it
                  read A
                  \\ in next statement we copy A properties to this
                  \\ so we copy a new value for Iam$
                  this=A
            }
      }
      module CollideWith {
            Read &inAsteroid
            inAsteroid.CollideWith this
      }
}
Class ExplodingAsteroid {
      Iam$="ExplodingAsteroid"
      Module CollideWith {
           Read A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "ExplodingAsteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "ExplodingAsteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }      
}
theAsteroid=Asteroid()
theSpaceShip=SpaceShip()
theApolloSpacecraft=SpaceShip(ApolloSpacecraft())
theExplodingAsteroid=ExplodingAsteroid()

link theApolloSpacecraft to theSpaceShipReference
link theExplodingAsteroid to theAsteroidReference
theSpaceShipReference.CollideWith &theAsteroid
theSpaceShipReference.CollideWith &theAsteroidReference
\\ prints  Asteroid hit an ApolloSpacecraft
\\ and     ExplodingAsteroid hit an ApolloSpacecraft



Now all calls to CollideWith need a group (object) by reference. I put (&) in the first parameter in Link statement (is optional). We have the same output.

Class ApolloSpacecraft {
      Iam$="ApolloSpacecraft"
}
Class Asteroid {
      Iam$="Asteroid"
      Module CollideWith {
            \\ A is a Group (object in m2000)
           Read &A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "Asteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "Asteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }
   
}
Class SpaceShip {
      Iam$="SpaceShip"
      module SpaceShip {
            \\ this is module but stand for a construction function for Class
            If match("G") then {
                  \\ if we provide an object (Group) then we can use it
                  read A
                  \\ in next statement we copy A properties to this
                  \\ so we copy a new value for Iam$
                  this=A
            }
      }
      module CollideWith {
            Read &inAsteroid
            inAsteroid.CollideWith &this
      }
}
Class ExplodingAsteroid {
      Iam$="ExplodingAsteroid"
      Module CollideWith {
           Read &A
           Select case A.Iam$
           Case "SpaceShip"
                 Print "ExplodingAsteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "ExplodingAsteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }      
}
theAsteroid=Asteroid()
theSpaceShip=SpaceShip()
theApolloSpacecraft=SpaceShip(ApolloSpacecraft())
theExplodingAsteroid=ExplodingAsteroid()

link &theApolloSpacecraft to theSpaceShipReference
link &theExplodingAsteroid to theAsteroidReference
theSpaceShipReference.CollideWith &theAsteroid
theSpaceShipReference.CollideWith &theAsteroidReference


27/12/2018
Και να η έκδοση του προηγούμενου με δείκτες (ενώ η αναφορά ετοιμάζεται μόνο για μια σύνδεση με ένα αντικείμενο ή μεταβλητή, ο δείκτης στις ομάδες μπορεί να αλλάζει)
Δείτε επίσης ότι χρησιμοποιούμε το => αντί για τελεία στους δείκτες. Επίσης για να φτιάξουμε δείκτες χρησιμοποιούμε το -> ή αν θέλουμε να το περάσουμε ως παράμετρο, όπως το this, το Pointer(this) ή Δείκτης(Αυτό).

Εδώ έχει σημασία να κατανοήσει κανείς ότι υπάρχουν δυο ειδών δείκτες. Οι δείκτες που δείχνουν στατική ομάδα, και οι δείκτες που δείχνουν πτητική (μεταφερόμενη δηλαδή). Το Αυτό είναι στατική ομάδα, και κάθε πτητική όταν χρησιμοποιείται γίνεται στατική πρώτα αλλά δεν ξέρουμε τι όνομα έδωσε ο διερμηνευτής οπότε χρησιμοποιούμε το Αυτό.
Δείκτες που δείχνουν αποτέλεσμα μιας συνάρτησης τύπου κλάση είναι δείκτες σε πτητική ομάδα. Αν είχαμε μια πιο πολύπλοκή έκφραση (όχι δηλαδή μια απλή συνάρτηση) θα έπρεπε να βάλουμε μετά το λεπτό βέλος παρένθεση. Ομοίως βάζουμε παρένθεση γύρω από ένα όνομα ομάδας για να μας γυρίσει δείκτη σε αντίγραφό της (που είναι πτητικό). Το πρόβλημα στις στατικές ομάδες είναι ότι ο δείκτης τους δεν ισχύει αν η ομάδα διαγραφεί, δηλαδή δεν έχει μέλλον έξω από το το χώρο που το χρησιμοποιούμε. Αντίθετα δείκτες σε πτητικές ομάδες κρατάνε τις ομάδες "ζωντανές", και αυτές διαγράφονται όταν ο τελευταίος δείκτης διαγραφεί ή πάρει τιμή 0& (πχ Δ->0& θα κάνει το δείκτη Δ να δείχνει μια άδεια ομάδα, όχι Τίποτα  ή Null).

 
Class ApolloSpacecraft {
      Iam$="ApolloSpacecraft"
}
Class Asteroid {
      Iam$="Asteroid"
      Module CollideWith {
            \\ A is a Group (object in m2000)
           Read A
           Select case A=>Iam$
           Case "SpaceShip"
                 Print "Asteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "Asteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }
   
}
Class SpaceShip {
      Iam$="SpaceShip"
      module SpaceShip {
            \\ this is module but stand for a construction function for Class
            If match("G") then {
                  \\ if we provide an object (Group) then we can use it
                  read A
                  \\ in next statement we copy A properties to this
                  \\ so we copy a new value for Iam$
                  this=A
            }
      }
      module CollideWith {
            Read inAsteroid
            inAsteroid=>CollideWith pointer(this)
      }
}
Class ExplodingAsteroid {
      Iam$="ExplodingAsteroid"
      Module CollideWith {
           Read A
           Select case A=>Iam$
           Case "SpaceShip"
                 Print "ExplodingAsteroid hit a SpaceShip"
           Case "ApolloSpacecraft"
                 Print "ExplodingAsteroid hit an ApolloSpacecraft"
           Else
                 Print "???"
           End Select
      }      
}
theAsteroid->Asteroid()
theSpaceShip->SpaceShip()
theApolloSpacecraft->SpaceShip(ApolloSpacecraft())
theExplodingAsteroid->ExplodingAsteroid()
theSpaceShipReference=theApolloSpacecraft
theAsteroidReference=theExplodingAsteroid
theSpaceShipReference=>CollideWith theAsteroid
theSpaceShipReference=>CollideWith theAsteroidReference