Maison >Java >javaDidacticiel >Orientation objet en C ? Implémentation d'une interface à partir de zéro.

Orientation objet en C ? Implémentation d'une interface à partir de zéro.

Mary-Kate Olsen
Mary-Kate Olsenoriginal
2025-01-21 10:05:12693parcourir

Orientação a Objetos em C? Implementando uma interface do zero.

J'ai toujours été curieux des ordinateurs et j'ai toujours pensé : "D'accord, je sais comment l'utiliser, mais comment ça marche vraiment ? Dans ce processus, je fais souvent une expérience de pensée : si on me demandait de commencer par ?" En partant de zéro, que ferais-je ? Dans cet article, nous explorerons le fonctionnement des interfaces dans la programmation orientée objet (en utilisant Java), puis implémenterons une version modeste de l'interface en C.

Regardons un exemple

Notre exemple est simple : calculer le prix d'un véhicule. S'il s'agit d'une voiture, le prix sera basé sur sa vitesse de pointe ; s'il s'agit d'une moto, le prix sera basé sur sa cylindrée. On définit d'abord le comportement du véhicule à l'aide d'une interface :

<code class="language-java">public class Main {
    public interface Vehicle {
        Integer price();
    }
}</code>

Rien de spécial ici, juste une méthode qui renvoie un entier. Implémentons maintenant la classe car :

<code class="language-java">public class Main {
    // ...

    public static class Car implements Vehicle {
        private final Integer speed;

        public Car(Integer speed) {
            this.speed = speed;
        }

        @Override
        public Integer price() {
            return speed * 60;
        }
    }
}</code>

Très classique : une mise en place d'une méthode constructeur et prix, multipliant la vitesse par 60. Maintenant, implémentons la classe moto :

<code class="language-java">public class Main {
    // ...

    public static class Motorcycle implements Vehicle {
        private final Integer cc;

        public Motorcycle(Integer cc) {
            this.cc = cc;
        }

        @Override
        public Integer price() {
            return cc * 10;
        }
    }
}</code>

Presque pareil, la seule différence est que maintenant on multiplie le déplacement par 10. Ensuite, nous mettons en œuvre une méthode pour imprimer le prix du véhicule :

<code class="language-java">public class Main {
    // ...

    public static void printVehiclePrice(Vehicle vehicle) {
        System.out.println("$" + vehicle.price() + ".00");
    }
}</code>

Pas de secrets. Enfin, notre méthode principale :

<code class="language-java">public class Main {
    // ...

    public static void main(String[] args) {
        Car car = new Car(120);
        Motorcycle motorcycle = new Motorcycle(1000);

        printVehiclePrice(car);
        printVehiclePrice(motorcycle);
    }
}</code>
<code>$ java Main.java
00.00
000.00</code>

C'est le modèle que nous souhaitons réaliser, mais maintenant implémenté à partir de zéro en C.

Comment pouvons-nous résoudre ce problème ?

Quand je pense aux objets, la première chose qui me vient à l'esprit est un ensemble de données qui représentent un état et des méthodes pour faire fonctionner et gérer cet état. La manière la plus directe de représenter une collection de données en langage C est une structure. Pour les méthodes, l’approche la plus proche est une fonction qui reçoit un état en paramètre. Cet état correspondra à cela dans la classe, par exemple :

<code class="language-c">typedef struct {
    int height_in_cm;
    int weight_in_kg;
} Person;

float person_bmi(Person *person) {
    float height_in_meters = (float)person->height_in_cm / 100;
    float bmi =
        (float)person->weight_in_kg / (height_in_meters * height_in_meters);

    return bmi;
}</code>

Ici, nous définissons les données d'une personne dans la structure Personne et utilisons ces données pour effectuer des calculs simples. C'est la structure la plus proche d'une classe que nous puissions avoir en C. Peut-être qu'utiliser des pointeurs de fonction dans les structures est également une bonne idée ? Eh bien, je laisse cela pour le prochain article.

D'accord, nous avons une structure semblable à celle d'une classe. Maintenant, comment définissons-nous l’interface en langage C ? Si vous y réfléchissez bien, le compilateur/interprète ne fait pas de magie pour deviner quelles classes implémentent l'interface. Il le détermine au moment de la compilation et remplace toutes les parties où nous utilisons l'interface par des types concrets. Dans le programme compilé, l'interface n'existe même pas.

Le compilateur du langage C n'offrant pas cette possibilité, nous devons implémenter cette solution nous-mêmes. Nous devons connaître tous les types qui implémentent notre interface et comprendre comment utiliser les fonctions de ces implémentations.

Implémenter l'interface en langage C

Tout d’abord, définissons le squelette de notre humble interface. Nous allons créer une énumération qui contient les différentes implémentations et signatures de nos fonctions.

<code class="language-java">public class Main {
    public interface Vehicle {
        Integer price();
    }
}</code>

Ici, nous définissons notre énumération qui contient l'implémentation que nous implémenterons plus tard. Cela ne semble peut-être pas le cas, mais cette partie est très importante. Ensuite, nous déclarons la fonction Vehicle_free (qui sera expliquée plus tard) et la fonction Vehicle_price, que nous voulons implémenter dans notre "classe". Regardons maintenant l'implémentation de la voiture :

<code class="language-java">public class Main {
    // ...

    public static class Car implements Vehicle {
        private final Integer speed;

        public Car(Integer speed) {
            this.speed = speed;
        }

        @Override
        public Integer price() {
            return speed * 60;
        }
    }
}</code>

La fonction car_init initialise un nouvel "objet" Car en mémoire. En Java, cela se fera automatiquement via new. Ici, nous devons le faire manuellement. La fonction vehicle_free sera utilisée pour libérer la mémoire allouée par tout "objet" précédemment initialisé, implémenté à l'aide de car_free etc. La mise en œuvre pour les motos est très similaire :

<code class="language-java">public class Main {
    // ...

    public static class Motorcycle implements Vehicle {
        private final Integer cc;

        public Motorcycle(Integer cc) {
            this.cc = cc;
        }

        @Override
        public Integer price() {
            return cc * 10;
        }
    }
}</code>

Presque pareil, sauf que maintenant on initialise avec VEHICLE_MOTORCYCLE et on multiplie par 10. Regardons maintenant la fonction qui imprime le prix du véhicule :

<code class="language-java">public class Main {
    // ...

    public static void printVehiclePrice(Vehicle vehicle) {
        System.out.println("$" + vehicle.price() + ".00");
    }
}</code>

C'est si simple... on dirait que nous ne faisons pas beaucoup de travail. Maintenant, le dernier et le le plus important point est que nous devons implémenter les fonctions que nous avons déclarées dans la définition de l'interface ci-dessus, vous vous souvenez ? Heureusement, nous n’avons même pas besoin de penser à cette mise en œuvre. Nous avons toujours un simple interrupteur/boîtier exhaustif et c'est tout.

<code class="language-java">public class Main {
    // ...

    public static void main(String[] args) {
        Car car = new Car(120);
        Motorcycle motorcycle = new Motorcycle(1000);

        printVehiclePrice(car);
        printVehiclePrice(motorcycle);
    }
}</code>

Maintenant, nous pouvons utiliser tout ce que nous avons fait :

<code>$ java Main.java
00.00
000.00</code>
<code class="language-c">typedef struct {
    int height_in_cm;
    int weight_in_kg;
} Person;

float person_bmi(Person *person) {
    float height_in_meters = (float)person->height_in_cm / 100;
    float bmi =
        (float)person->weight_in_kg / (height_in_meters * height_in_meters);

    return bmi;
}</code>

Succès ! Mais vous pensez peut-être : « Eh bien, à quoi ça sert ? »

Un vrai cas d'usage

L'un de mes types de projets préférés est celui des analyseurs, des analyseurs aux simples analyseurs d'expressions mathématiques. Généralement, lorsque vous implémentez ces analyseurs, vous rencontrez quelque chose appelé AST (Abstract Syntax Tree). Comme son nom l'indique c'est un arbre qui va représenter la syntaxe à laquelle vous avez affaire, par exemple la déclaration de variable int foo = 10 est un nœud de l'AST qui contient trois autres nœuds, un nœud de type, pour int, un nœud identifiant ; , pour foo, et un nœud d'expression, pour 10, qui contient un autre nœud entier avec une valeur de 10. Vous voyez à quel point c'est compliqué ?

Quand nous faisons cela en C, nous devons choisir entre une énorme structure avec plusieurs champs pour représenter n'importe quel nœud AST possible, ou une définition abstraite utilisant plusieurs petites structures, chaque structure représente des nœuds différents, tout comme nous le faisons ici avec notre "interfaces". Si vous voulez voir un exemple simple, dans cet analyseur d'expressions mathématiques, j'ai implémenté la deuxième approche.

Conclusion

Rien de ce qu'un compilateur ou un interprète fait n'est magique. C'est toujours un exercice amusant d'essayer de mettre en œuvre quelque chose soi-même. J'espère que cette lecture a été utile. Merci!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn