Question

Quand un constructeur peut-il lever une exception? (Ou dans le cas de l’objectif C: quand un initié at-il raison de renvoyer nil?)

Il me semble qu’un constructeur devrait échouer - et donc refuser de créer un objet - si cet objet n’est pas complet. C'est-à-dire que le constructeur devrait avoir un contrat avec son appelant pour fournir un objet fonctionnel et fonctionnel sur lequel des méthodes peuvent être appelées de manière significative? Est-ce raisonnable?

Était-ce utile?

La solution

Le constructeur a pour tâche de placer l'objet dans un état utilisable. Il existe essentiellement deux écoles de pensée à ce sujet.

Un groupe est favorable à la construction en deux étapes. Le constructeur amène simplement l'objet dans un état dormant dans lequel il refuse de travailler. Il existe une fonction supplémentaire qui effectue l’initialisation proprement dite.

Je n'ai jamais compris le raisonnement derrière cette approche. Je fais partie du groupe qui prend en charge la construction en une étape, où l'objet est entièrement initialisé et utilisable après la construction.

Les constructeurs à une étape doivent lancer s'ils ne parviennent pas à initialiser complètement l'objet. Si l’objet ne peut pas être initialisé, il ne doit pas être autorisé à exister, le constructeur doit donc lancer.

Autres conseils

Eric Lippert dit qu'il existe 4 types d'exceptions.

  • Les exceptions fatales ne sont pas de votre faute, vous ne pouvez pas les empêcher, et vous ne pouvez pas les éliminer de manière raisonnable.
  • Les exceptions Boneheaded sont de votre faute, vous auriez pu les éviter. Il s’agit donc de bogues dans votre code.
  • Les exceptions frustrantes sont le résultat de décisions de conception malheureuses. Les exceptions frustrantes sont levées dans des circonstances tout à fait non exceptionnelles et doivent donc être interceptées et traitées en permanence.
  • Enfin, les exceptions exogènes semblent un peu comme des exceptions vexantes, à la différence qu’elles ne résultent pas de choix de conception malheureux. Elles résultent plutôt de réalités externes peu soignées qui affectent votre belle et logique logique de programme.

Votre constructeur ne doit jamais lancer une exception fatale de son propre chef, mais le code qu'il exécute peut provoquer une exception fatale. Quelque chose comme "out of memory" Ce n’est pas quelque chose que vous pouvez contrôler, mais si cela se produit dans un constructeur, hé, cela arrive.

Les exceptions Boneheaded ne devraient jamais se produire dans aucun de vos codes, elles sont donc immédiatement supprimées.

Les exceptions vexantes (l'exemple est Int32.Parse () ) ne devraient pas être émises par les constructeurs, car ils ne présentent pas de circonstances non exceptionnelles.

Enfin, les exceptions exogènes doivent être évitées, mais si vous faites quelque chose dans votre constructeur qui dépend de circonstances externes (comme le réseau ou le système de fichiers), il serait approprié de lancer une exception.

Il n'y a généralement rien à gagner en divorçant l'initialisation d'un objet de la construction. RAII est correct, un appel réussi au constructeur doit aboutir à un objet actif entièrement initialisé ou il doit échouer et les échecs ALL à tout moment du chemin du code doivent toujours générer une exception. Vous ne gagnez rien en utilisant une méthode init () séparée, sauf une complexité supplémentaire à un certain niveau. Le contrat ctor doit être soit un objet fonctionnel valide, soit un objet nettoyé, puis un objet nettoyé.

Si vous implémentez une méthode init distincte, vous devez toujours l'appeler. Il sera toujours possible de lancer des exceptions, de les gérer et de les appeler presque toujours immédiatement après le constructeur, sauf que vous avez maintenant 4 états d'objet possibles au lieu de 2 (IE, construit, initialisé, non initialisé, et échoué vs juste valide et inexistant).

Dans tous les cas, je suis tombé sur une expérience de 25 ans de développement OO où il semble qu'une méthode init distincte permettrait de "résoudre certains problèmes" et sont des défauts de conception. Si vous n'avez pas besoin d'un objet NOW, vous ne devriez pas le construire maintenant, et si vous en avez besoin maintenant, vous devez l'initialiser. KISS devrait toujours être le principe suivi, avec le concept simple que le comportement, l'état et l'API de toute interface doit refléter CE QUE l'objet fait, pas COMMENT, le code client ne doit même pas être conscient que l'objet a une sorte d’état interne qui nécessite une initialisation, ainsi le motif init-après viole ce principe.

À cause de tous les problèmes qu'une classe partiellement créée peut causer, je dirais jamais.

Si vous devez valider quelque chose pendant la construction, rendez le constructeur privé et définissez une méthode de fabrique statique publique. La méthode peut lancer si quelque chose est invalide. Mais si tout se passe bien, le constructeur est appelé, ce qui garantit de ne pas le lancer.

Un constructeur doit lancer une exception lorsqu'il est incapable de terminer la construction dudit objet.

Par exemple, si le constructeur est censé allouer 1024 Ko de RAM, et qu'il échoue, il doit émettre une exception. Ainsi, l'appelant du constructeur sait que l'objet n'est pas prêt à être utilisé. est une erreur quelque part qui doit être corrigé.

Les objets qui sont à moitié initialisés et à moitié morts provoquent simplement des problèmes, car l'appelant n'a aucun moyen de le savoir. Je préférerais que mon constructeur génère une erreur en cas de problème, plutôt que de devoir compter sur la programmation pour exécuter un appel à la fonction isOK () qui renvoie true ou false.

C'est toujours assez risqué, surtout si vous allouez des ressources dans un constructeur; En fonction de votre langue, le destructeur ne sera pas appelé, vous devez donc le nettoyer manuellement. Cela dépend du moment où la vie d’un objet commence dans votre langue.

La seule fois où je l'ai vraiment fait, c'est quand il y a eu un problème de sécurité quelque part, ce qui signifie que l'objet ne devrait pas être créé, mais ne peut pas, il devrait être créé.

Il est raisonnable qu'un constructeur lève une exception tant qu'il se nettoie correctement. Si vous suivez le paradigme de RAII , alors est . fort> assez commun pour un constructeur de faire un travail significatif; un constructeur bien écrit nettoiera lui-même s’il ne peut pas être complètement initialisé.

Pour autant que je sache, personne ne présente une solution assez évidente qui incarne le meilleur de la construction à une et à deux étapes.

note: Cette réponse suppose C #, mais les principes peuvent être appliqués dans la plupart des langues.

Premièrement, les avantages des deux:

Une étape

La construction en une étape nous est bénéfique en empêchant les objets d’exister dans un état non valide, évitant ainsi toutes sortes de gestion d’états erronées et tous les bogues qui l’accompagnent. Cependant, certains d'entre nous se sentent étranges parce que nous ne voulons pas que nos constructeurs lèvent des exceptions, et c'est parfois ce que nous devons faire lorsque les arguments d'initialisation ne sont pas valides.

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    public Person(string name, DateTime dateOfBirth)
    {
        if (string.IsNullOrWhitespace(name))
        {
            throw new ArgumentException(nameof(name));
        }

        if (dateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(dateOfBirth));
        }

        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }
}

Méthode de validation en deux étapes

La construction en deux étapes nous est avantageuse en permettant à notre validation d'être exécutée en dehors du constructeur, et évite ainsi la nécessité d'exécuter des exceptions dans le constructeur. Cependant, cela nous laisse avec " invalide " instances, ce qui signifie qu'il y a un état que nous devons suivre et gérer pour l'instance, ou que nous le jettons immédiatement après l'allocation de tas. Cela pose la question suivante: pourquoi effectuons-nous une allocation de tas, et donc une collecte de mémoire, sur un objet que nous n'utilisons même pas?

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    public Person(string name, DateTime dateOfBirth)
    {
        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }

    public void Validate()
    {
        if (string.IsNullOrWhitespace(Name))
        {
            throw new ArgumentException(nameof(Name));
        }

        if (DateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(DateOfBirth));
        }
    }
}

Single-Stage via un constructeur privé

Alors, comment pouvons-nous garder les exceptions en dehors de nos constructeurs et nous empêcher de procéder à une allocation de tas sur des objets qui seront immédiatement rejetés? C'est assez basique: nous rendons le constructeur privé et créons des instances via une méthode statique conçue pour effectuer une instanciation, et donc une allocation de tas, uniquement après la validation .

public class Person
{
    public string Name { get; }
    public DateTime DateOfBirth { get; }

    private Person(string name, DateTime dateOfBirth)
    {
        this.Name = name;
        this.DateOfBirth = dateOfBirth;
    }

    public static Person Create(
        string name,
        DateTime dateOfBirth)
    {
        if (string.IsNullOrWhitespace(Name))
        {
            throw new ArgumentException(nameof(name));
        }

        if (dateOfBirth > DateTime.UtcNow) // side note: bad use of DateTime.UtcNow
        {
            throw new ArgumentOutOfRangeException(nameof(DateOfBirth));
        }

        return new Person(name, dateOfBirth);
    }
}

Async Single-Stage via un constructeur privé

Outre les avantages susmentionnés en matière de validation et de prévention d’allocation de tas, la méthodologie précédente nous offre un autre avantage intéressant: le support asynchrone. Cela s'avère pratique lorsqu'il s'agit d'une authentification à plusieurs étapes, telle que lorsque vous devez récupérer un jeton porteur avant d'utiliser votre API. Ainsi, vous ne vous retrouverez pas avec un " déconnecté " invalide. Client API, mais vous pouvez simplement recréer le client API si vous recevez une erreur d'autorisation lors de la tentative d'exécution d'une requête.

public class RestApiClient
{
    public RestApiClient(HttpClient httpClient)
    {
        this.httpClient = new httpClient;
    }

    public async Task<RestApiClient> Create(string username, string password)
    {
        if (username == null)
        {
            throw new ArgumentNullException(nameof(username));
        }

        if (password == null)
        {
            throw new ArgumentNullException(nameof(password));
        }

        var basicAuthBytes = Encoding.ASCII.GetBytes(<*>quot;{username}:{password}");
        var basicAuthValue = Convert.ToBase64String(basicAuthBytes);

        var authenticationHttpClient = new HttpClient
        {
            BaseUri = new Uri("https://auth.example.io"),
            DefaultRequestHeaders = {
                Authentication = new AuthenticationHeaderValue("Basic", basicAuthValue)
            }
        };

        using (authenticationHttpClient)
        {
            var response = await httpClient.GetAsync("login");
            var content = response.Content.ReadAsStringAsync();
            var authToken = content;
            var restApiHttpClient = new HttpClient
            {
                BaseUri = new Uri("https://api.example.io"), // notice this differs from the auth uri
                DefaultRequestHeaders = {
                    Authentication = new AuthenticationHeaderValue("Bearer", authToken)
                }
            };

            return new RestApiClient(restApiHttpClient);
        }
    }
}

D'après mon expérience, les inconvénients de cette méthode sont rares.

En règle générale, cette méthodologie signifie que vous ne pouvez plus utiliser la classe en tant que DTO, car la désérialisation sur un objet sans constructeur public par défaut est difficile, au mieux. Toutefois, si vous utilisiez l'objet en tant que DTO, vous ne devriez pas vraiment valider l'objet lui-même, mais plutôt invalider les valeurs qu'il contient lorsque vous essayez de les utiliser, car techniquement, les valeurs ne sont pas "invalides". en ce qui concerne le DTO.

Cela signifie également que vous allez créer des méthodes ou des classes fabriques lorsque vous devez autoriser un conteneur IOC à créer l'objet, sinon le conteneur ne saura pas comment instancier l'objet. Cependant, dans de nombreux cas, les méthodes d'usine finissent par être l'une des méthodes Créer elles-mêmes.

Voir les sections de la FAQ C ++ 17.2 et < a href = "http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4" rel = "nofollow noreferrer"> 17.4 .

En général, j'ai trouvé un code plus facile à porter et à conserver les résultats si les constructeurs sont écrits pour qu'ils n'échouent pas, et le code qui peut échouer est placé dans une méthode séparée qui renvoie un code d'erreur et laisse l'objet dans un état inerte.

Si vous écrivez UI-Controls (ASPX, WinForms, WPF, ...), évitez de lancer des exceptions dans le constructeur, car le concepteur (Visual Studio) ne peut pas les gérer lorsqu’il crée vos contrôles. Connaissez votre cycle de vie de contrôle (événements de contrôle) et utilisez une initialisation différée chaque fois que possible.

Notez que si vous générez une exception dans un initialiseur, vous finirez par perdre du code si un code utilise le modèle [[[[[MyObj alloc] init] autorelease]] ], car l'exception passera la libération automatique.

Voir cette question:

Comment éviter les fuites lors de la remontée une exception dans init?

Vous devez absolument lever une exception d'un constructeur si vous ne parvenez pas à créer un objet valide. Cela vous permet de fournir les invariants appropriés dans votre classe.

En pratique, vous devrez peut-être faire très attention. N'oubliez pas qu'en C ++, le destructeur ne sera pas appelé. Par conséquent, si vous lancez après avoir alloué vos ressources, vous devez prendre le plus grand soin de le gérer correctement!

Cette page décrit de manière approfondie la situation en C ++.

Lancez une exception si vous ne parvenez pas à initialiser l'objet dans le constructeur, par exemple, les arguments illégaux.

En règle générale, une exception doit toujours être levée le plus tôt possible, car elle facilite le débogage lorsque la source du problème est plus proche de la méthode signalant que quelque chose ne va pas.

Lancer une exception pendant la construction est un excellent moyen de rendre votre code plus complexe. Les choses qui semblaient simples deviennent soudainement difficiles. Par exemple, disons que vous avez une pile. Comment ouvrez-vous la pile et renvoyez la valeur supérieure? Eh bien, si les objets de la pile peuvent intégrer leurs constructeurs (construction du temporaire à retourner à l'appelant), vous ne pouvez pas garantir que vous ne perdrez pas de données (pointeur de pile de la décrémentation, construction de la valeur de retour à l'aide du constructeur de copie de valeur dans pile, qui jette, et maintenant une pile qui vient de perdre un objet)! C'est pourquoi std :: stack :: pop ne renvoie pas de valeur et vous devez appeler std :: stack :: top.

Ce problème est bien décrit ici , consultez l'élément 10, écriture d'un code protégé contre les exceptions.

Le contrat habituel dans OO est que les méthodes d'objet fonctionnent réellement.

Donc, en tant que corrolaire, ne jamais retourner un objet zombie à un constructeur / init.

Un zombie n'est pas fonctionnel et il se peut qu'il manque des composants internes. Juste une exception null-pointeur en attente d’arrivée.

J'ai d'abord fait des zombies dans l'Objectif C, il y a de nombreuses années.

Comme toutes les règles empiriques, il existe une "exception".

Il est tout à fait possible qu'une interface spécifique ait un contrat stipulant que il existe une méthode "initialiser" cela est autorisé à passer une exception. Il est possible qu'un objet implémentant cette interface ne réponde pas correctement aux appels, à l'exception des outils de définition de propriétés, jusqu'à ce que le processus initialize ait été appelé. Je l'ai utilisé pour les pilotes de périphérique dans un système d'exploitation OO au cours du processus de démarrage et cela a fonctionné.

En général, vous ne voulez pas d’objets zombies. Dans des langues telles que Smalltalk avec devenir , les choses deviennent un peu pétillantes, mais une utilisation excessive de devenir est également un mauvais style. Devenir permet à un objet de se transformer in-situ en un autre objet. Il n’est donc pas nécessaire d’utiliser une enveloppe (Advanced C ++) ou un modèle de stratégie (GOF).

Je ne peux pas aborder les meilleures pratiques en Objective-C, mais en C ++, il est normal qu'un constructeur lève une exception. D'autant qu'il n'y a pas d'autre moyen de s'assurer qu'un état exceptionnel rencontré lors de la construction est signalé sans recourir à la méthode isOK ().

La fonctionnalité block try de fonction a été conçue spécifiquement pour prendre en charge les échecs lors de l'initialisation du constructeur par le membre (bien qu'elle puisse également être utilisée pour des fonctions standard). C'est le seul moyen de modifier ou d'enrichir les informations sur les exceptions qui seront émises. Mais en raison de son objectif initial (utilisation dans les constructeurs), il ne permet pas que l'exception soit avalée par une clause catch () vide.

Oui, si le constructeur ne construit pas l'une de ses parties internes, il peut être tenu - par choix - de sa responsabilité de lancer (et dans un certain langage, de le déclarer) un exception explicite , dûment noté dans la documentation du constructeur.

Ce n’est pas la seule option: il pourrait terminer le constructeur et construire un objet, mais avec une méthode 'isCoherent ()' renvoyant false, afin de pouvoir signaler un état incohérent (cela peut être préférable dans certains cas). , afin d'éviter une interruption brutale du flux de travail d'exécution en raison d'une exception)

Attention: comme EricSchaefer l'a dit dans son commentaire, cela peut apporter une certaine complexité aux tests unitaires (un lancer peut augmenter la complexité cyclomatique de la fonction en raison de la condition qui la déclenche)

S'il échoue à cause de l'appelant (comme un argument null fourni par l'appelant, où le constructeur appelé attend un argument non nul), le constructeur lève quand même une exception d'exécution non contrôlée.

Je ne suis pas sûr que toute réponse puisse être entièrement indépendante de la langue. Certaines langues traitent les exceptions et la gestion de la mémoire différemment.

Je travaillais auparavant avec des normes de codage exigeant des exceptions, mais uniquement des codes d'erreur sur les initialiseurs, car les développeurs avaient été gravés par le langage qui traitait mal les exceptions. Les langues sans garbage collection gèrent le tas et la pile de manière très différente, ce qui peut être important pour les objets non RAII. Il est toutefois important qu'une équipe décide d'être cohérente afin de savoir par défaut si elle doit appeler des initialiseurs après les constructeurs. Toutes les méthodes (y compris les constructeurs) doivent également être bien documentées quant aux exceptions qu’elles peuvent générer, afin que les appelants sachent les gérer.

Je suis généralement favorable à une construction en une étape, car il est facile d'oublier d'initialiser un objet, mais il existe de nombreuses exceptions à cette règle.

  • La prise en charge linguistique d’exceptions n’est pas très bonne.
  • Vous avez une raison impérieuse de continuer à utiliser nouveau et supprimer
  • Votre initialisation nécessite beaucoup de processeur et doit être exécutée de manière asynchrone sur le thread qui a créé l'objet.
  • Vous créez une DLL qui peut générer des exceptions en dehors de son interface vers une application utilisant un langage différent. Dans ce cas, il ne s’agit peut-être pas trop de ne pas lancer d’exceptions, mais de s’assurer qu’elles sont interceptées avant l’interface publique. (Vous pouvez intercepter des exceptions C ++ en C #, mais vous devez sauter à travers des cerceaux.)
  • Constructeurs statiques (C #)

La question du PO a une "quotité agnostique". tag ... cette question ne peut pas répondre de la même manière en toute sécurité pour toutes les langues / situations.

La hiérarchie de classes de l'exemple C # suivant jette dans le constructeur de la classe B, en ignorant un appel immédiat au IDisposeable.Dispose de la classe A, à la sortie du du principal> à l'aide de , en ignorant la suppression explicite de Les ressources de la classe A.

Si, par exemple, la classe A avait créé un Socket lors de la construction, connectée à une ressource réseau, il en serait probablement de même après le bloc utilisant (un anomalie relativement cachée).

class A : IDisposable
{
    public A()
    {
        Console.WriteLine("Initialize A's resources.");
    }

    public void Dispose()
    {
        Console.WriteLine("Dispose A's resources.");
    }
}

class B : A, IDisposable
{
    public B()
    {
        Console.WriteLine("Initialize B's resources.");
        throw new Exception("B construction failure: B can cleanup anything before throwing so this is not a worry.");
    }

    public new void Dispose()
    {
        Console.WriteLine("Dispose B's resources.");
        base.Dispose();
    }
}
class C : B, IDisposable
{
    public C()
    {
        Console.WriteLine("Initialize C's resources. Not called because B throws during construction. C's resources not a worry.");
    }

    public new void Dispose()
    {
        Console.WriteLine("Dispose C's resources.");
        base.Dispose();
    }
}


class Program
{
    static void Main(string[] args)
    {
        try
        {
            using (C c = new C())
            {
            }
        }
        catch
        {           
        }

        // Resource's allocated by c's "A" not explicitly disposed.
    }
}

En ce qui concerne Java, chaque fois que vous initialisez un constructeur avec des valeurs illégales, une exception doit être levée. De cette façon, il ne sera pas construit dans un mauvais état.

Pour moi, c'est une décision de conception quelque peu philosophique.

Il est très agréable d’avoir des instances valables tant qu’elles existent, à partir de l’heure actuelle. Dans de nombreux cas non triviaux, cela peut nécessiter de lever des exceptions du cteur si une allocation mémoire / ressources ne peut pas être effectuée.

Certaines autres approches sont la méthode init (), qui pose certains problèmes. L'une d'elles est de s'assurer que init () est effectivement appelé.

Une variante utilise une approche paresseuse pour appeler automatiquement init () la première fois qu'un accesseur / mutateur est appelé, mais cela nécessite que tout appelant potentiel se préoccupe de la validité de l'objet. (Par opposition à "cela existe, c’est donc une philosophie valable").

J'ai déjà vu divers modèles de design proposés pour traiter ce problème. Par exemple, être capable de créer un objet initial via ctor, mais avoir à appeler init () pour mettre la main sur un objet initialisé contenant des accesseurs / mutateurs.

Chaque approche a ses hauts et ses bas; J'ai utilisé tous ces éléments avec succès. Si vous ne créez pas d'objets prêts à l'emploi dès l'instant où ils ont été créés, je vous recommande une forte dose d'assertions ou d'exceptions pour vous assurer que les utilisateurs n'interagissent pas avant init ().

Addendum

J'ai écrit du point de vue des programmeurs C ++. Je suppose également que vous utilisez correctement l'idiome RAII pour gérer les ressources libérées lorsque des exceptions sont générées.

Je viens d’apprendre l’objectif C, je ne peux donc pas vraiment parler d’expérience, mais j’ai lu à ce sujet dans la documentation d’Apple.

http://developer.apple.com/documentation /Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_6.html

Cela vous indiquera non seulement comment traiter la question que vous avez posée, mais il vous expliquera également très bien.

En utilisant des fabriques ou des méthodes de fabrique pour la création d'objets, vous pouvez éviter les objets non valides sans générer d'exceptions des constructeurs. La méthode de création doit renvoyer l'objet demandé s'il est capable d'en créer un ou null si ce n'est pas le cas. Vous perdez un peu de flexibilité dans la gestion des erreurs de construction chez l'utilisateur d'une classe, car renvoyer null ne vous dit pas ce qui n'a pas fonctionné lors de la création de l'objet. Mais cela évite également d’ajouter la complexité de plusieurs gestionnaires d’exceptions chaque fois que vous demandez un objet, et le risque d’attraper des exceptions que vous ne devriez pas gérer.

Le meilleur conseil que j'ai vu à propos des exceptions est de lever une exception si, et seulement si, l'alternative est le non-respect d'une condition de publication ou le maintien d'un invariant.

Ce conseil remplace une décision subjective peu claire (est-ce une bonne idée ) par une question technique précise basée sur les décisions de conception (invariant et conditions de post) que vous auriez déjà dû prendre.

Les constructeurs ne sont qu'un cas particulier, mais pas spécial, de ce conseil. La question est donc de savoir quels invariants une classe devrait avoir. Les défenseurs d'une méthode d'initialisation distincte, appelée après la construction, suggèrent que la classe possède au moins deux modes de fonctionnement , avec un mode non prêt après la construction et au moins un < em> prêt mode, entré après l'initialisation. C'est une complication supplémentaire, mais acceptable si la classe dispose de toute façon de plusieurs modes de fonctionnement. Il est difficile de voir en quoi cette complication est utile si la classe n’aurait autrement pas de mode de fonctionnement.

Notez que le paramétrage poussé dans une méthode d’initialisation distincte ne vous permet pas d’éviter la levée d’exceptions. Les exceptions que votre constructeur pourrait avoir levées seront maintenant levées par la méthode d'initialisation. Toutes les méthodes utiles de votre classe devront générer des exceptions si elles sont appelées pour un objet non initialisé.

Notez également qu'éviter la possibilité que votre constructeur génère des exceptions est gênant et, dans de nombreux cas, impossible dans de nombreuses bibliothèques standard. En effet, les concepteurs de ces bibliothèques estiment qu’il est judicieux de supprimer des exceptions de constructeurs. En particulier, toute opération qui tente d’acquérir une ressource non partageable ou finie (telle que l’allocation de mémoire) peut échouer, et cet échec est généralement signalé dans les langages et les bibliothèques OO par le biais d’une exception.

Les acteurs ne sont pas censés faire un "intelligent". De toute façon, les choses qui lancent une exception ne sont pas nécessaires. Utilisez une méthode Init () ou Setup () si vous souhaitez effectuer une configuration d’objet plus complexe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top