Δημοσιεύτηκε: 25 Απρ 2012, 22:54
από migf1
Πολλά, πολλά μπράβο Ηλία!

Δεν το είχα πάρει χαμπάρι πως άνοιξες ξεχωριστό νήμα! Πολύ καλά έκανες :)

Off topic:
Εγώ είχα ξεκινήσει να αναδιοργανώνω τον κώδικα ώστε να είναι πιο ευέλικτος σε αλλαγές, να στήσω σκελετό δηλαδή, αλλά το σταμάτησα γιατί είχα δουλειές. Σήμερα είπα να το συνεχίσω, αλλά τελικά στη το ξαναφτιάχνω από το 0, υλοποιώντας το με OOP λογική. Χωρίς κληρονομικότητες, πολυμορφισμούς, κλπ, αλλά με encapsulation και αυτονομία των δομών που θα παίζουν το ρόλο των κλάσεων.

Για να μην τα πολυλογώ, μου χει βγει το λάδι να διπλογράφω κώδικα, για να είναι συμβατός με την OOP λογική. Δηλαδή μια "κλάση" να μην έχει πρόσβαση στα private members οποιασδήποτε άλλης, και ότι θέλει να το ζητάει μέσω μεθόδων (συναρτήσεων δηλαδή) από το interface της κλάσης.

Προς το παρόν έχω φτιάξει μονάχα την "κλάση" Animal_T (βάζω τη λέξη σε εισαγωγικά, γιατί δεν είναι κανονική κλάση) η οποία περιέχει μια άλλη, την AnimalName που είναι το όνομα του ζώου. Το έχω κάνει έτσι, γιατί λόγω της υποστήριξης πολλών γλωσσών, το όνομα δεν είναι ένα απλό string, έχει ενικό, πληθυντικό, άρθρο διαφορετικό γένους για το κάθε ζώο, και του 'χω βάλει και αιτιατική (ώστε π.χ. στα Ελληνικά να μπορείς να γράψεις και "δες λύκο" και "δες λύκος").

Κανονικά η "κλάση" AnimalName πρέπει να πάει σε δικό της ξεχωριστό αρχείο, με το private interface της υλοποιημένο σε δικό της .c αρχείο και το public σε .h αρχείο, αλλά βαρέθηκα και την άφησα στο ίδιο με την Animal_T. Οπότε σε ένα αρχείο υλοποιώ και το private και το public interface και των 2 "κλάσεων". Οπότε όταν κάποιος θελήσει να ζητήσει κάτι από την AnimalName (ή να της αλλάξει κάτι) πρέπει να το κάνει μέσω του public interface της Animal_T (η οποία με τη σειρά της το μεταβιβάζει στην AnimalName μέσω του public interface αυτηνής).

Βασικά η νοοτροπία του OOP ομοιάζει σεA πολλές τομείς με αυτήν των βιβλιοθηκών, υπό την έννοια πως πρέπει ο κώδικας να είναι γραμμένος γενικά και αυτόνομα για την κάθε "κλάση". Ώστε να μπορεί να δέχεται μελλοντικές αλλαγές/βελτιώσεις/αναβαθμίσεις χωρίς να επηρεάζονται τα προγράμματα που τη χρησιμοποιούν ήδη. Αυτά χρησιμοποιούν το public interface της "κλάσης"/βιβλιοθήκης, ενώ η υλοποίησή της είναι private. Αν είναι εξαρχής σωστά σχεδιασμένο το public interface και η private υλοποίηση, τότε οι μελλοντικές αλλαγές είναι εν πολλοίς απλώς προσθήκη νέων properties & μεθόδων στην υλοποίηση και αντίστοιχες συναρτήσεις διαχείρισής τους στο public interface.

Δίνεις δηλαδή στον άλλον τον interface της κλάσης σου (το .header αρχείο) μαζί με το object αρχείο της κι αρχίζει να φτιάχνει προγράμματα που χρησιμοποιούν τα ζώα της κλάσης σου, χωρίς να ξέρει και χωρίς να τον ενδιαφέρει πως είναι υλοποιημένα εσωτερικά.

Το public interface της "κλάσης" Animal_T που έχω κάνει μέχρι στιγμής είναι αυτό εδώ το .h αρχείο...

Spoiler: show
Κώδικας: Επιλογή όλων

/**********************************************************//**
Animal_T PUBLIC INTERFACE
**************************************************************
*/

#ifndef ANIMAL_H /* start of inclusion guard ================================== */
#define ANIMAL_H

/** The following require re-compilation */
extern const int VERBOSE_DEBUGGING; // for silence: -DANIMAL_NODEBUG

/** Custom boolean type */
#ifndef Bool
#define Bool int
#endif

/** Animal Type for all animals*/
typedef struct Animal_T Animal_T;

/** Animal_T Valid IDs */
enum ATID {
ATID_INVALID = -1,
ATID_BADGER,
ATID_BEAR,
ATID_DEER,
ATID_DUCK,
ATID_FERRET,
ATID_FOX,
ATID_RABBIT,
ATID_RACOON,
ATID_WLDBOAR,
ATID_WOLF,
/* total count */
MAX_ANIMALTYPES
};

/** User Interface */

#ifndef ANIMAL_C

/* Constructors / Destructors */

/****************************************************************************//**
animal_new:
Allocates memory for an animal and populates its attributes.
Popularization is done according to the value of the 'typid' parameter,
which can be any of the above listed ATID values, except MAX_ANIMALTYPES.
Animal attributes are set to their default animal-specific values, unless
ATID_INVALID is passed. (in that case the attributes are just cleared).
Returns a pointer to the newly created animal, or NULL on failure.
*/
extern Animal_T *animal_new( enum ATID typid );

/****************************************************************************//**
animal_new_random:
Allocates memory for a random animal, among the predefined ones.
Animal attributes are set to their animal-specific default values.
Returns a pointer to the newly created animal, or NULL on failure.
*/
extern Animal_T *animal_new_random( void );

/****************************************************************************//**
animal_new_Random:
Allocates memory for a random animal, with random attribute values.
Returns a pointer to the newly created animal, or NULL on failure.
*/
extern Animal_T *animal_new_Random( void );

/****************************************************************************//**
animal_free:
Frees up the memory occupied by the given animal. The animal pointer is
passed by reference (double pointer) and it is set to NULL.
Returns FALSE if the address of the animal-pointer was passed as NULL,
TRUE otherwise.
*/
extern Bool animal_free( Animal_T **animal );


/* Randomizers */

/****************************************************************************//**
animal_randomize:
Assigns randomly a new animal to the memory pointed to be its parameter
(this means that the animal must have been already created).
Animal attributes are set to their animal-specific default values.
Returns FALSE on error (e.g a NULL pointer is passed) TRUE otherwise.
*/
extern Bool animal_randomize( Animal_T *animal );

/****************************************************************************//**
animal_Randomize:
Assigns randomly a new animal with random attributes to the memory
pointed to be its parameter (this means that the animal must have been
already created).
Returns FALSE on error (e.g a NULL pointer is passed) TRUE otherwise.
*/
extern Bool animal_Randomize( Animal_T *animal );

/****************************************************************************//**
animal_randomize_attributes:
Randomizes the attributes of the specified animal.
Returns FALSE on error (e.g a NULL pointer is passed) TRUE otherwise.
*/
extern Bool animal_randomize_attributes( Animal_T *animal );

/****************************************************************************//**
animal_randomize_health:
Randomizes the health attribute of the specified animal.
Returns FALSE on error (e.g a NULL pointer is passed) TRUE otherwise.
*/
extern Bool animal_randomize_health( Animal_T *animal );

/****************************************************************************//**
animal_randomize_mood:
Randomizes the mood attribute of the specified animal.
Returns FALSE on error (e.g a NULL pointer is passed), TRUE otherwise.
*/
extern Bool animal_randomize_mood( Animal_T *animal );


/* Textifiers (usefeul e.g for output) */

/****************************************************************************//**
animal_str_nameArticle:
Returns as a string the grammatical article for the name of the specified
animal, in singular 1st person, or NULL on error.
*/
extern const char *animal_str_nameArticle( const Animal_T *animal );

/****************************************************************************//**
animal_str_nameSingular:
Returns as a string the singular name of the specified animal,
or NULL on error.
*/
extern const char *animal_str_nameSingular( const Animal_T *animal );

/****************************************************************************//**
animal_str_namePlural:
Returns as a string the plural name of the specified animal,
or NULL on error.
*/
extern const char *animal_str_namePlural( const Animal_T *animal );

/****************************************************************************//**
animal_str_book:
Returns as a string the text holding the encyclopedical info of the specified animal,
or NULL on error.
*/
extern const char *animal_str_book( const Animal_T *animal );

/****************************************************************************//**
animal_str_health:
Returns as a string the textified health attribute of the specified animal,
or NULL on error.
*/
extern const char *animal_str_health( const Animal_T *animal );

/****************************************************************************//**
animal_str_mood:
Returns as a string the textified mood attribute of the specified animal,
or NULL on error.
*/
extern const char *animal_str_mood( const Animal_T *animal );

/****************************************************************************//**
animal_report_status:
Prints out a text describing the current status of the specified animal.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_report_status( const Animal_T *animal );

/*
extern Bool animal_say_nameArticle( const Animal_T *animal );
extern Bool animal_say_nameSingular( const Animal_T *animal );
extern Bool animal_say_namePlural( const Animal_T *animal );
extern Bool animal_say_book( const Animal_T *animal );
extern Bool animal_say_health( const Animal_T *animal );
extern Bool animal_say_mood( const Animal_T *animal );
*/


/* Animal moves */

/****************************************************************************//**
animal_move:
Directs the specified animal to move. The animal makes a different move
each time, depending on the current values of its attributes.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_move( Animal_T *animal );


/****************************************************************************//**
animal_attack:
Forces the specified animal to perform an attacking move.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_attack( Animal_T *animal );

/****************************************************************************//**
animal_wander:
Forces the specified animal to perform a wandering move.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_wander( Animal_T *animal );

/****************************************************************************//**
animal_hide:
Forces the specified animal to perform a hiding move.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_hide( Animal_T *animal );


/* Media */

/****************************************************************************//**
animal_media_open_image:
Opens and displays the image file of the specified animal.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_media_open_image( const Animal_T *animal );

/****************************************************************************//**
animal_media_open_image:
Opens and displays the video file of the specified animal.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_media_open_video( const Animal_T *animal );


/****************************************************************************//**
animal_media_open_book:
Displays the encyclopedecal info of the specified animal.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_media_open_book( const Animal_T *animal );


/* Miscellaneous */

/****************************************************************************//**
animal_is_enabled:
Returns TRUE if the type-id of the specified animal is in the range
for ATID_INVALID to MAX_ANIMALTYPES, exclusive!
If not, or if the 'animal' pointer is NULL, the function returns FALSE.
*/
extern Bool animal_is_enabled( const Animal_T *animal );


/****************************************************************************//**
animal_copy:
Copies the memory occupied by animal 'src' into the memory occupied by
animal 'dst'.
Returns FALSE on error, TRUE otherwise.
*/
extern Bool animal_copy( Animal_T *dst, const Animal_T *src );


/****************************************************************************//**
animal_compare_id:
Compares the type-id of the 'first' animal against the type-id of the
'second' animal. It requires that none of the ids equals to ATID_INVALID!
Returns 0 if the type-ids are equal, a positive integer if the first id is
greater than the second, or a negative integer otherwise.
If any of the ids equals to ATID_INVALID, the function returns INT_MIN.
*/
extern int animal_compare_id( const Animal_T *first, const Animal_T *second );

#endif /* ifndef ANIMAL_C */

#endif /* end of inclusion guard =================================================== */


όπου ο χρήστης το μόνο που χρειάζεται να ξέρει είναι ότι περιέχει αυτό το αρχείο (πλην της γλώσσας, που θέλει re-compilation του animal.c). Αν του δώσεις μαζί και το .o αρχείο (ή μάλλον τα .o αρχεία, ένα για κάθε γλώσσα) τότε μπορεί να ξεκινήσει να χρησιμοποιεί απευθείας ότι του παρέχει η "κλήση"/βιβλιοθήκη.

Π.χ...

Spoiler: show
Κώδικας: Επιλογή όλων

#define MAIN_C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "animal.h"
#include "myextras.h"

#define MAX_ANIMALS 10

/*********************************************************//**
*
*************************************************************
*/
int main( void )
{
Animal_T *a = NULL;
int i = 0;

srand( time(NULL) );

/* create a random animal with random attributes */
a = animal_new_Random();
if ( !a )
goto exit_failure;

for (i=0; i < 5; i++ )
{
/* print animal's name, mood, health & dictionary info */
printf( "%s (%s, %s)\n%s",
animal_str_nameSingular( a ),
animal_str_mood( a ),
animal_str_health( a ),
animal_str_book( a )
);

/* let the animal make a move (according to its health & mood) */
animal_move( a );
puts("\n");

pressENTER();

/* change randomly the animal and its attributes */
animal_Randomize( a );
}

animal_free( &a );
pressENTER();
exit( EXIT_SUCCESS );

exit_failure:
animal_free( &a );
pressENTER();
exit( EXIT_FAILURE );

}


Για να μπορεί να χρησιμοποιήσει και τα media, πρέπει στο ζιπ με το .h και τα .o να υπάρχουν και τα αρχεία εικόνας και βίντεο του κάθε ζώου, τοποθετημένα στο σωστό directory (αλλιώς μπορεί να βάλει δικά του, με την προϋπόθεση πως θα τα βάλει στον φάκελο που τα περιμένει η "κλάση"/βιβλιοθήκη και θα τα ονοματίζει με τα ονόματα που περιμένει η βιβλιοθήκη).

Ο κώδικας της εσωτερικής υλοποίησης είναι εδώ (χωρίς τα αρχεία media) : http://www.box.com/s/fb6b2fe18e2246ab22ec
Όποιος θέλει να βάλει δικά του αρχεία media, τότε οι εικόνες πρέπει να έχουν κατάληξη .jpg και τα βίντεο .flv και τα κύρια ονόματα των αρχείων πρέπει να είναι αυτά που έχει στην αρχή του αρχείο animal.c ως key-names, δηλαδή αυτά...

Κώδικας: Επιλογή όλων

/* key-names of animals */
#define STR_BADGER "badger"
#define STR_BEAR "bear"
#define STR_DEER "deer"
#define STR_DUCK "duck"
#define STR_FERRET "ferret"
#define STR_FOX "fox"
#define STR_RABBIT "rabbit"
#define STR_RACOON "racoon"
#define STR_WLDBOAR "wild-boar"
#define STR_WOLF "wolf"


Και πρέπει να μπουν σε έναν υποφάκλεο φάκελο: data/media στο κύριο φάκελο του προγράμματός σας (του εκτελέσιμου δηλαδή που θα χρησιμοποιεί την βιβλιοθήκη).
Δηλαδή για να σας ανοίξει το βίντεο του λύκου η κλήση της συνάρτησης...

Κώδικας: Επιλογή όλων

animal_media_open_video();

χρειάζεται να υπάρχει το αρχείο: data/media/wolf.flv (για εικόνα, data/media/wolf.jpg );

Οπότε για παράδειγμα, αν βάλετε 2 τέτοια αρχεία, τότε σε ένα δικό σας πρόγραμμα που θα το κάνετε compile μαζί με το animal.o ή animal.c ...

Κώδικας: Επιλογή όλων

gcc animal.c mypro.c -o myprog

μπορείτε να γράψετε κάτι σαν αυτό για να σας ανοίξει τα media...

Κώδικας: Επιλογή όλων

#include <stdio.h>
#include "animal.h"

int main( void )
{
Animal_T *a = animal_new( ATID_WOLF );
if ( !a ) {
puts( "internal error" );
return 1;
}

/* open the wolf image */
animal_media_open_image( a );

/* play the wolf video */
animal_media_open_video( a );

animal_free( a );
return 0;
}


Οι συναρτήσεις που μπορείτε να χρησιμοποιήσετε είναι τεκμηριωμένες στο animal.h. Προφανώς ακόμα τα ζώα δεν μπορούν να κάνουν πολλά πράγματα, γιατί είμαι στην αρχή. Επίσης δεν έχω βγάλει public συναρτήσεις που μπορούν να αλλάξουν τη διάθεση, την υγεία, κλπ του ζώου απευθείας. Έχω βάλει όμως randomizers, είτε για την υγεία, είτε για την διάθεση είτε και για τα 2 μαζί. Μπορείτε επίσης να τα δημιουργήστε εξαρχής randomly, με τις: animal_new_random() και animal_new_Random()

Τα ζώα κάνουν 3 κινήσεις: attack, wander και hide οι οποίες δεν είναι πάντα ίδιες, αλλά επηρεάζονται από την τρέχουσα υγεία και διάθεσή του. Αν για παράδειγμα ένα ζώο είναι ετοιμοθάνατο και του πείτε: animal_attack() τότε θα σας πει κάτι σαν "και να θέλει δεν μπορεί"... βασικά το τι θα σας πει ακριβώς εξαρτάται κι από την διάθεσή του.

Μπορείτε επίσης να δώσετε γενικώς: animal_move() οπότε ανάλογα την υγεία του και τη διάθεσή του θα διαλέξει μόνο του αν θα επιτεθεί, αν θα μείνει αδιάφορο ή αν θα απομακρυνθεί φοβισμένο.

Από default η γλώσσα είναι Αγγλικά και είναι ενεργοποιημένα τα debugging info (στα σφάλματα "βαράει" καμπανάκι εκτός από κείμενο). Για να αλλάξετε γλώσσα, όταν κάνετε compile το animal.c δώστε στον compiler -DLANG_EL ή -DLANG_EL_GREEKLISH (χωρίς τίποτα ή με -DLANG_EN βγάζει αγγλικά).

Για τα debugging info, δώστε -DANIMAL_NODEBUG αν δεν θέλετε να σας ειδοποιεί για σφάλματα.

Αν υπάρχει κάνα bug μου λέτε.

ΥΓ. Επίτηδες έβαλα το link κατεβάσματος στη μέση της... έκθεσης, για να το κατεβάσουν μόνο οι σοβαροί :lol: