web-dev-qa-db-fra.com

Ai-je vraiment une voiture dans mon garage?

Je suis un novice en programmation Java et en essayant de maîtriser le POO.

J'ai donc construit cette classe abstraite:

public abstract class Vehicle{....}

et 2 sous-classes:

public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}

Car et Boat contiennent également des champs et des méthodes uniques qui ne sont pas communs (ils n'ont pas le même nom, je ne peux donc pas définir de méthode abstraite pour eux dans Vehicle).

Maintenant, dans mainClass, j'ai configuré mon nouveau garage:

Vehicle[] myGarage= new Vehicle[10];
myGarage[0]=new Car(2,true);
myGarage[1]=new Boat(4,600);

J'étais très contente du polymorphisme jusqu'à ce que j'essaie d'accéder à l'un des champs propres à Car, tels que:

boolean carIsAutomatic = myGarage[0].auto;

Le compilateur n'accepte pas cela. J'ai travaillé sur ce problème en utilisant le casting:

boolean carIsAutomatic = ((Car)myGarage[0]).auto;

Cela fonctionne ... mais cela n'aide pas avec les méthodes, juste les champs. Ce qui signifie que je ne peux pas faire

(Car)myGarage[0].doSomeCarStuff();

Ma question est donc la suivante: qu'est-ce que j'ai vraiment dans mon garage? J'essaie d'obtenir l'intuition et de comprendre ce qui se passe "dans les coulisses".


pour les futurs lecteurs, un bref résumé des réponses ci-dessous:

  1. Oui, il y a une Car dans myGarage[]
  2. En tant que langage à typage statique, le compilateur Java ne donnera pas accès aux méthodes/champs qui ne sont pas "Vehicle", s'ils y accédaient via une structure de données basée sur la super classe Vehicle (telle que Vehicle myGarage[])
  3. En ce qui concerne la façon de résoudre le problème, il existe 2 approches principales ci-dessous:
    1. Utilisez le transtypage, ce qui atténuera les préoccupations du compilateur et laissera toute erreur de conception dans le temps d'exécution
    2. Le fait que j'ai besoin de casting dit que le design est imparfait. Si j'ai besoin d'accéder à des fonctionnalités non liées au véhicule, je ne devrais pas stocker les voitures et les bateaux dans une structure de données basée sur le véhicule. Soit attribuer toutes ces capacités à Vehicle, soit utiliser des structures basées sur des types plus spécifiques (dérivés)
  4. Dans de nombreux cas, la composition et/ou les interfaces constitueraient une meilleure alternative à l'héritage. Probablement le sujet de ma prochaine question ...
  5. En plus de nombreuses autres idées intéressantes, si l’on a le temps de parcourir les réponses.
259
T-Rex

Si vous devez faire la différence entre Car et Boat dans votre garage, stockez-les dans des structures distinctes.

Par exemple:

public class Garage {
    private List<Car> cars;
    private List<Boat> boats;
}

Vous pouvez ensuite définir des méthodes spécifiques aux bateaux ou spécifiques aux voitures.

Pourquoi avoir un polymorphisme alors?

Disons que Vehicle est comme:

public abstract class Vehicle {
   protected int price;
   public getPrice() { return price; }
   public abstract int getPriceAfterYears(int years);
}

Chaque Vehicle a un prix et peut donc être inséré dans la classe abstraite Vehicle.

Cependant, la formule de détermination du prix après n années dépend du véhicule, il est donc laissé à la classe des exécutants de le définir. Par exemple:

public Car extends Vehicle {
    // car specific
    private boolean automatic;
    @Override
    public getPriceAfterYears(int years) {
        // losing 1000$ every year
        return Math.max(0, this.price - (years * 1000));  
    }
}

La classe Boat peut avoir une autre définition de getPriceAfterYears et des attributs et méthodes spécifiques.

Alors maintenant, dans la classe Garage, vous pouvez définir:

// car specific
public int numberOfAutomaticCars() {
    int s = 0;
    for(Car car : cars) {
        if(car.isAutomatic()) {
            s++;
        }
    }
    return s;
}
public List<Vehicle> getVehicles() {
    List<Vehicle> v = new ArrayList<>(); // init with sum
    v.addAll(cars);
    v.addAll(boats);
    return v;
}
// all vehicles method
public getAveragePriceAfterYears(int years) {
    List<Vehicle> vehicules = getVehicles();
    int s = 0;
    for(Vehicle v : vehicules) {
        // call the implementation of the actual type!
        s += v.getPriceAfterYears(years);  
    }
    return s / vehicules.size();
}

L'intérêt du polymorphisme est de pouvoir appeler getPriceAfterYears sur un Vehiclesans se souciant de l'implémentation.

Habituellement, le downcasting est le signe d’une conception défectueuse: ne rangez pas vos véhicules tous ensemble si vous devez différencier leur type réel.

Remarque: bien entendu, le design peut être facilement amélioré. C'est juste un exemple pour démontrer les points.

147
Jean Logeart

Pour répondre à votre question, vous pouvez savoir ce qui se trouve exactement dans votre garage en procédant comme suit:

Vehicle v = myGarage[0];

if (v instanceof Car) {
   // This vehicle is a car
   ((Car)v).doSomeCarStuff();
} else if(v instanceof Boat){
   // This vehicle is a boat
   ((Boat)v).doSomeBoatStuff();
}

MISE À JOUR: Comme vous pouvez le lire dans les commentaires ci-dessous, cette méthode convient aux solutions simples, mais ce n’est pas une bonne pratique, en particulier si vous avez un grand nombre de véhicules dans votre garage. Alors utilisez-le uniquement si vous savez que le garage restera petit. Si ce n'est pas le cas, recherchez "Eviter une instance de" en cas de débordement de pile, il existe plusieurs façons de le faire.

84
danizmax

Si vous utilisez le type de base, vous ne pouvez accéder qu'aux méthodes publiques et à leurs champs.

Si vous souhaitez accéder au type étendu, mais que vous avez un champ du type de base où il est stocké (comme dans votre cas), vous devez d'abord le convertir, puis vous pouvez y accéder:

Car car = (Car)myGarage[0];
car.doSomeCarStuff();

Ou plus court sans champ temporaire:

((Car)myGarage[0]).doSomeCarStuff();

Puisque vous utilisez des objets Vehicle, vous ne pouvez appeler des méthodes de la classe de base que sur celles-ci sans transtypage. Donc, pour votre garage, il peut être judicieux de distinguer les objets dans différents tableaux - ou de meilleures listes - un tableau n'est souvent pas une bonne idée, car sa gestion est beaucoup moins souple que celle d'une classe basée sur Collection.

22
Alexander Rühl

Vous avez défini que votre garage va stocker les véhicules, de sorte que vous ne vous souciez pas du type de véhicule que vous avez. Les véhicules ont des caractéristiques communes comme le moteur, la roue, un comportement comme le déplacement. La représentation réelle de ces caractéristiques peut être différente, mais les couches abstraites sont identiques. Vous avez utilisé la classe abstraite qui signifie que certains attributs, comportements sont exactement les mêmes par les deux véhicules. Si vous voulez exprimer le fait que vos véhicules ont des caractéristiques abstraites communes, utilisez une interface telle que se déplacer peut vouloir dire différent en voiture ou en bateau. Les deux peuvent aller d'un point A à un point B, mais d'une manière différente (sur roue ou sur l'eau - la mise en œuvre sera donc différente). Vous avez donc des véhicules dans le garage qui se comportent de la même manière et vous ne tenez pas compte des caractéristiques spécifiques d'eux.

Pour répondre au commentaire:

Interface signifie un contrat décrivant comment communiquer avec le monde extérieur. Dans le contrat, vous définissez que votre véhicule peut être déplacé, peut être piloté, mais vous ne décrivez pas son fonctionnement, cela est décrit dans l’implémentation. Par classe abstraite, vous pouvez avoir des fonctions pour lesquelles vous partagez une implémentation, mais vous devez également: fonction que vous ne savez pas comment elle sera mise en œuvre.

Un exemple d'utilisation de la classe abstraite:

    abstract class Vehicle {

    protected abstract void identifyWhereIAm();
    protected abstract void startEngine();
    protected abstract void driveUntilIArriveHome();
    protected abstract void stopEngine();

    public void navigateToHome() {
        identifyWhereIAm();
        startEngine();
        driveUntilIArriveHome();
        stopEngine();
    } 
}

Vous utiliserez les mêmes étapes pour chaque véhicule, mais leur mise en oeuvre différera selon le type de véhicule. La voiture peut utiliser le GPS, le bateau peut utiliser le sonar pour identifier sa position.

13
HamoriZ

Je suis un novice en programmation Java et en essayant de maîtriser le POO.

Juste mes 2 centimes - je vais essayer de faire court, car beaucoup de choses intéressantes ont déjà été dites. Mais, en fait, il y a deux questions ici. Un sur "OOP" et un sur sa mise en oeuvre en Java.

Tout d’abord, oui, vous avez une voiture dans votre garage. Donc, vos hypothèses sont justes. Mais, Java est un langage statiquement typé . Et le système de types du compilateur ne peut "connaître" le type de vos différents objets que par leur déclaration correspondante . Pas par leur utilisation. Si vous avez un tableau de Vehicle, le compilateur le sait seulement. Donc, il vérifiera que vous n’effectuez que des opérations autorisées sur n’importe quel Vehicle. (En d'autres termes, méthodes et attributs visibles dans la déclaration Vehicle).

Vous pouvez expliquer au compilateur que "vous savez en fait que cette Vehicle est une Car" , en utilisant une distribution explicite (Car). le compilateur vous croira - même si en Java il y a un contrôle au moment de l'exécution, ce qui pourrait conduire à un ClassCastException qui évite des dommages supplémentaires si vous menti  (d'autres langages comme C++ ne vérifieront pas au moment de l'exécution - vous devez savoir ce que vous faites)

Enfin, si vous en avez vraiment besoin, vous pouvez vous fier à l’identification du type au moment de l’exécution (c.-à-d.: instanceof) pour vérifier le type "réel" d’un objet avant de tenter de le lancer. Mais ceci est principalement considéré comme une mauvaise pratique en Java.

Comme je l’ai dit, c’est la manière dont Java d’implémenter la POO. Il est tout différent classe  famille de langues communément appelées "langages dynamiques" , que ne vérifie qu'au moment de l'exécution si une opération est autorisée sur un objet ou non. Avec ces langages, vous n'avez pas besoin de "déplacer" toutes les méthodes courantes vers une classe de base (éventuellement abstraite) pour satisfaire le système de types. Ceci s'appelle frappe de canard .

13
Sylvain Leroux

Vous avez demandé à votre majordome:

Jeeves, tu te souviens de mon garage sur l'île de Java? Allez vérifier si le premier véhicule garé là est automatique.

et Jeeves paresseux a dit:

mais monsieur, si c'est un véhicule qui ne peut être automatique ou non automatique?

C'est tout.

Ok, ce n’est pas tout, puisque la réalité est plus typée canard que typée statiquement. C'est pourquoi j'ai dit que Jeeves est paresseux.

9
einpoklum

Votre problème ici se situe à un niveau plus fondamental: vous avez construit Vehicle de telle manière que Garage ait besoin d'en savoir plus sur ses objets que l'interface Vehicle n'en donne. Vous devriez essayer de construire la classe Vehicle de la perspective Garage (et en général du point de vue de tout ce qui va utiliser Vehicle): quel genre de choses doivent-ils faire avec leurs véhicules? Comment puis-je rendre ces choses possibles avec mes méthodes?

Par exemple, à partir de votre exemple:

bool carIsAutomatic = myGarage[0].auto;

Votre garage veut en savoir plus sur le moteur d'un véhicule pour ... des raisons? Quoi qu'il en soit, il n'est pas nécessaire que cela soit simplement exposé par Car. Vous pouvez toujours exposer une méthode isAutomatic() non implémentée dans Vehicle, puis l'implémenter comme return True dans Boat et return this.auto dans Car.

Il serait même préférable d’avoir une EngineType enum à trois valeurs (HAS_NO_GEARS, HAS_GEARS_AUTO_SHIFT, HAS_GEARS_MANUAL_SHIFT), ce qui laisserait à votre code le soin de se fonder sur les caractéristiques réelles d’un générique. Vehicle proprement et avec précision. (Vous aurez besoin de cette distinction pour gérer les motos, de toute façon.)

8
badp

Votre garage contient des véhicules, donc la vue de contrôle statique du compilateur que vous avez un véhicule et comme .auto est un champ de voiture vous ne pouvez pas y accéder, dynamiquement c'est une voiture afin que la distribution ne crée pas de problème, s'il le sera un bateau et que vous essayez de faire un casting sur Car augmentera une exception à l’exécution.

7
Leonid

C’est un bon endroit pour l’application du modèle de conception Visitor.

La beauté de ce modèle réside dans le fait que vous pouvez appeler du code non lié sur différentes sous-classes d’une superclasse sans avoir à faire des conversions bizarres partout ou à mettre des tonnes de méthodes sans lien dans la superclasse.

Cela fonctionne en créant un objet Visitor et en permettant à notre classe Vehicle de accept() le visiteur.

Vous pouvez également créer de nombreux types de Visitor et appeler du code non lié à l'aide des mêmes méthodes, mais simplement une implémentation différente de Visitor, ce qui rend ce modèle de conception très puissant lors de la création de classes propres.

Une démo par exemple:

public class VisitorDemo {

    // We'll use this to mark a class visitable.
    public static interface Visitable {

        void accept(Visitor visitor);
    }

    // This is the visitor
    public static interface Visitor {

        void visit(Boat boat);

        void visit(Car car);

    }

    // Abstract
    public static abstract class Vehicle implements Visitable {

            // NO OTHER RANDOM ABSTRACT METHODS!

    }

    // Concrete
    public static class Car extends Vehicle {

        public void doCarStuff() {
            System.out.println("Doing car stuff");
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }

    }

    // Concrete
    public static class Boat extends Vehicle {

        public void doBoatStuff() {
            System.out.println("Doing boat stuff");
        }

        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }

    }

    // Concrete visitor
    public static class StuffVisitor implements Visitor {

        @Override
        public void visit(Boat boat) {
            boat.doBoatStuff();
        }

        @Override
        public void visit(Car car) {
            car.doCarStuff();
        }
    }

    public static void main(String[] args) {
        // Create our garage
        Vehicle[] garage = {
            new Boat(),
            new Car(),
            new Car(),
            new Boat(),
            new Car()
        };

        // Create our visitor
        Visitor visitor = new StuffVisitor();

        // Visit each item in our garage in turn
        for (Vehicle v : garage) {
            v.accept(visitor);
        }
    }

}

Comme vous pouvez le constater, StuffVisitor vous permet d’appeler un code différent sur Boat ou Car, selon l’implémentation de visit appelée. Vous pouvez également créer d'autres implémentations du visiteur pour appeler différents codes avec le même motif .visit().

Notez également qu'en utilisant cette méthode, il n'y a pas d'utilisation de instanceof ni de vérification de classe de hacky. Le seul code dupliqué entre les classes est la méthode void accept(Visitor).

Si vous voulez supporter 3 types de sous-classes concrètes par exemple, vous pouvez aussi ajouter cette implémentation dans l'interface Visitor.

7
David Xu

Je ne fais que mettre en commun les idées des autres ici (et je ne suis pas un mec Java, donc c'est pseudo plutôt que réel), mais dans cet exemple artificiel, je résumerais mon approche de vérification de voiture dans une classe dédiée, qui ne connaît que les voitures et ne se soucie que des voitures quand on regarde les garages:

abstract class Vehicle { 
    public abstract string getDescription() ;
}

class Transmission {
    public Transmission(bool isAutomatic) {
        this.isAutomatic = isAutomatic;
    }
    private bool isAutomatic;
    public bool getIsAutomatic() { return isAutomatic; }
}

class Car extends Vehicle {
    @Override
    public string getDescription() { 
        return "a car";
    }

    private Transmission transmission;

    public Transmission getTransmission() {
        return transmission;
    }
}

class Boat extends Vehicle {
    @Override
    public string getDescription() {
        return "a boat";
    }
}

public enum InspectionBoolean {
    FALSE, TRUE, UNSUPPORTED
}

public class CarInspector {
    public bool isCar(Vehicle v) {
        return (v instanceof Car);
    }
    public bool isAutomatic(Car car) {
        Transmission t = car.getTransmission();
        return t.getIsAutomatic();
    }
    public bool isAutomatic(Vehicle vehicle) {
        if (!isCar(vehicle)) throw new UnsupportedVehicleException();
        return isAutomatic((Car)vehicle);
    }
    public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
        if (!isCar(garage[bay])) return InspectionBoolean.UNSUPPORTED;
        return isAutomatic(garage[bay]) 
             ? InspectionBoolean.TRUE
             : InspectionBoolean.FALSE;
    }
}

Le fait est que vous avez déjà décidé que vous ne vous souciez que des voitures lorsque vous posez des questions sur la transmission de la voiture. Il suffit donc de demander à CarInspector. Grâce au tri-state Enum, vous pouvez maintenant savoir si c'est automatique ou même s'il ne s'agit pas d'une voiture.

Bien entendu, vous aurez besoin de différents VehicleInspectors pour chaque véhicule qui vous tient à cœur. Et vous venez de pousser le problème de VehicleInspector à instancier dans la chaîne.

Au lieu de cela, vous voudrez peut-être examiner les interfaces.

Résumé getTransmission vers une interface (par exemple, HasTransmission). De cette façon, vous pouvez vérifier si un véhicule a une transmission ou écrire un TransmissionInspector:

abstract class Vehicle { }

class Transmission {
    public Transmission(bool isAutomatic) {
        this.isAutomatic = isAutomatic;
    }
    private bool isAutomatic;
    public bool getIsAutomatic() { return isAutomatic; }
}

interface HasTransmission { 
    Transmission getTransmission(); 
}

class Car extends Vehicle, HasTransmission {
    private Transmission transmission;

    @Override
    public Transmission getTransmission() {
        return transmission;
    }
}

class Bus extends Vehicle, HasTransmission {
    private Transmission transmission;

    @Override
    public Transmission getTransmission() {
        return transmission;
    }
}

class Boat extends Vehicle { }

enum InspectionBoolean {
    FALSE, TRUE, UNSUPPORTED
}

class TransmissionInspector {
    public bool hasTransmission(Vehicle v) {
        return (v instanceof HasTransmission);
    }
    public bool isAutomatic(HasTransmission h) {
        Transmission t = h.getTransmission();
        return t.getIsAutomatic();
    }
    public bool isAutomatic(Vehicle v) {
        if (!hasTranmission(v)) throw new UnsupportedVehicleException();
        return isAutomatic((HasTransmission)v);
    }
    public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
        if (!hasTranmission(garage[bay])) return InspectionBoolean.UNSUPPORTED;
        return isAutomatic(garage[bay]) 
             ? InspectionBoolean.TRUE
             : InspectionBoolean.FALSE;
    }
}

Vous dites maintenant que vous ne pouvez parler que de la transmission, quel que soit le véhicule, et que vous pouvez demander à TransmissionInspector. Le bus et la voiture peuvent être inspectés par le TransmissionInspector, mais celui-ci ne peut se renseigner que sur la transmission.

Maintenant, vous pouvez décider que les valeurs booléennes ne vous intéressent pas. À ce stade, vous préférerez peut-être utiliser un type générique pris en charge, qui expose à la fois l'état pris en charge et la valeur:

class Supported<T> {
    private bool supported = false;
    private T value;

    public Supported() { }
    public Supported(T value) { 
        this.isSupported = true;
        this.value = value; 
    }

    public bool isSupported() { return supported; }
    public T getValue() { 
        if (!supported) throw new NotSupportedException();
        return value;
    }
}

Maintenant, votre inspecteur peut être défini comme:

class TransmissionInspector {
    public Supported<bool> isAutomatic(Vehicle[] garage, int bay) {
        if (!hasTranmission(garage[bay])) return new Supported<bool>();
        return new Supported<bool>(isAutomatic(garage[bay]));
    }

    public Supported<int> getGearCount(Vehicle[] garage, int bay) {
        if (!hasTranmission(garage[bay])) return new Supported<int>();
        return new Supported<int>(getGearCount(garage[bay]));
    }
}

Comme je l'ai déjà dit, je ne suis pas un Java, une partie de la syntaxe ci-dessus est peut-être fausse, mais les concepts doivent être vérifiés. Néanmoins, ne lancez pas ce qui est important ci-dessus sans le tester au préalable.

6
jimbobmcgee

Créer Champs de niveau de véhicule qui permettront de rendre chaque véhicule plus distinct.

public abstract class Vehicle {
    public final boolean isCar;
    public final boolean isBoat;

    public Vehicle (boolean isCar, boolean isBoat) {
        this.isCar  = isCar;
        this.isBoat = isBoat;
    }
}

Définissez les champs de niveau de véhicule de la classe héritante sur la valeur appropriée.

public class Car extends Vehicle {
    public Car (...) {
        super(true, false);
        ...
    }
}

public class Boat extends Vehicle {
    public Boat (...) {
        super(false, true);
        ...
    }
}

Mettre en place utiliser les champs de niveau de véhicule pour déchiffrer correctement le type de véhicule.

boolean carIsAutomatic = false;

if (myGarage[0].isCar) {
    Car car = (Car) myGarage[0];
    car.carMethod();
    carIsAutomatic = car.auto;
}

else if (myGarage[0].isBoat) {
    Boat boat = (Boat) myGarage[0];
    boat.boatMethod();
}

Depuis que vous avez dit à votre compilateur que tout dans votre garage est un véhicule, vous êtes coincé avec les méthodes et les champs de niveau classe de véhicule. Si vous voulez déchiffrer correctement le type de véhicule, vous devez définir certains champs de niveau de classe, par exemple. isCar et isBoat pour que le programmeur comprenne mieux le type de véhicule que vous utilisez.

Java est un langage de type sûr, il est donc préférable de toujours vérifier avant de traiter les données transtypées comme vos Boats et Cars.

2
Jay Harris

Si vous êtes sur Java, vous pouvez utiliser les réflexions pour vérifier si une fonction est disponible et l'exécuter également.

2
wutzebaer

La modélisation des objets que vous souhaitez présenter dans un programme (afin de résoudre un problème) est une chose, le codage en est une autre. Dans votre code, je pense qu’il est fondamentalement inapproprié de modéliser un garage en utilisant un tableau. Les tableaux ne doivent pas souvent être considérés comme des objets, bien qu'ils apparaissent , généralement dans le but de se tenir compte d'eux-mêmes intégrité d'un langage et fournissant une certaine familiarité, mais un tableau en tant que type n'est en réalité qu'un élément spécifique à l'ordinateur, à mon humble avis, en particulier en Java, où vous ne pouvez pas étendre tableaux.

Je comprends que modéliser correctement une classe pour représenter un garage ne vous aidera pas à répondre à votre question "voitures dans un garage"; juste un conseil.

Retournez au code. En plus de résoudre le problème de la programmation orientée objet, quelques questions seraient utiles pour créer une scène et ainsi mieux comprendre le problème que vous souhaitez résoudre (en supposant qu'il y en ait un, et pas seulement "obtenir un blocage"):

  1. Qui ou quoi veut comprendre carIsAutomatic?
  2. Étant donné carIsAutomatic, qui ou quoi effectuerait doSomeCarStuff?

Cela peut être un inspecteur ou quelqu'un qui sait seulement conduire des voitures à transmission automatique, etc., mais du point de vue du garage, tout ce qu'il sait, c'est qu'il détient un véhicule. C'est donc (dans ce modèle) la responsabilité de cet inspecteur. ou conducteur pour dire si c'est une voiture ou un bateau; à ce moment, vous voudrez peut-être commencer à créer un autre groupe de classes représentant des types d'acteurs similaires dans la scène. Cela dépend du problème à résoudre. Si vous en avez vraiment besoin, vous pouvez modéliser le garage comme un système super intelligent, de sorte qu'il se comporte comme un distributeur automatique, au lieu d'un garage ordinaire, dont le bouton indique "Voiture" et un autre indique "Bateau", pour que les gens puissent appuyer sur le bouton pour obtenir une voiture ou un bateau comme ils le souhaitent, ce qui rend ce garage super intelligent responsable de la définition de ce qui doit être présenté (une voiture ou un bateau) à ses utilisateurs; pour suivre cette improvisation, le garage peut exiger une certaine comptabilité lorsqu’il accepte un véhicule, que quelqu'un doit fournir des informations, etc., toutes ces responsabilités vont au-delà d’un simple principal classe.

Cela dit, je comprends certainement tous les problèmes rencontrés, ainsi que ceux qui ont précédé, pour coder un programme OO, en particulier lorsque le problème qu’il essaie de résoudre est très simple, mais OO est En effet, un moyen possible de résoudre de nombreux autres problèmes. D'après mon expérience, avec quelques exemples fournissant des exemples d'utilisation, les gens commencent à concevoir des scènes d'interaction entre objets, les catégorisent en classes (ainsi qu'en interfaces en Java), puis utilisent quelque chose comme votre Classe principale vers bootstrap le monde .

0
IUSR