Contexte
J'ai actuellement une situation où j'ai un objet qui est à la fois transmis et reçu par un appareil. Ce message a plusieurs constructions, comme suit:
public void ReverseData()
public void ScheduleTransmission()
La méthode ScheduleTransmission
nécessite d'appeler la méthode ReverseData
chaque fois qu'elle est appelée. Cependant, il y a des moments où je devrai appeler ReverseData
en externe (et je devrais ajouter entièrement en dehors de l'espace de noms ) d'où l'objet est instancié dans l'application.
Quant au "recevoir", je veux dire que ReverseData
sera appelé en externe dans un object_received
gestionnaire d'événements pour annuler les données.
Question
Est-il généralement acceptable qu'un objet appelle ses propres méthodes publiques?
Je dirais que ce n'est pas seulement acceptable, mais encouragé, surtout si vous prévoyez d'autoriser des extensions. Afin de prendre en charge les extensions de la classe en C #, vous devez marquer la méthode comme virtuelle selon les commentaires ci-dessous. Vous voudrez peut-être documenter cela, cependant, afin que quelqu'un ne soit pas surpris lorsque la substitution de ReverseData () modifie le fonctionnement de ScheduleTransmission ().
Cela se résume vraiment à la conception de la classe. ReverseData () ressemble à un comportement fondamental de votre classe. Si vous devez utiliser ce comportement à d'autres endroits, vous ne voudrez probablement pas en avoir d'autres versions. Vous devez juste faire attention à ne pas laisser les détails spécifiques à ScheduleTransmission () s'infiltrer dans ReverseData (). Cela va créer des problèmes. Mais puisque vous l'utilisez déjà en dehors de la classe, vous y avez probablement déjà réfléchi.
Absolument.
La visibilité d'une méthode a pour seul but d'autoriser ou de refuser l'accès à une méthode en dehors de la classe ou au sein d'une classe enfant; Les méthodes publiques, protégées et privées peuvent toujours être appelées à l'intérieur de la classe elle-même.
Il n'y a rien de mal à appeler des méthodes publiques. L'illustration de votre question est l'exemple parfait d'une situation où avoir deux méthodes publiques est exactement ce que vous devez faire.
Cependant, vous pouvez surveiller attentivement les modèles suivants:
Une méthode Hello()
appelle World(string, int, int)
comme ceci:
Hello()
{
this.World("Some magic value here", 0, 100);
}
Évitez ce schéma. Utilisez plutôt arguments facultatifs . Les arguments optionnels facilitent la découverte: l'appelant qui tape Hello(
Ne saura pas nécessairement qu'il existe une méthode qui permet d'appeler la méthode avec des valeurs par défaut. Les arguments facultatifs sont également auto-documentés. World()
ne montre pas à l'appelant quelles sont les valeurs par défaut réelles.
Une méthode Hello(ComplexEntity)
appelle World(string, int, int)
comme ceci:
Hello(ComplexEntity entity)
{
this.World(entity.Name, entity.Start, entity.Finish);
}
Utilisez plutôt surcharges . Même raison: meilleure découvrabilité. L'appelant peut voir immédiatement toutes les surcharges via IntelliSense et choisir la bonne.
Une méthode appelle simplement d'autres méthodes publiques sans ajouter de valeur substantielle.
Réfléchissez bien. Avez-vous vraiment besoin de cette méthode? Ou devez-vous le supprimer et laisser l'appelant invoquer les différentes méthodes? Un conseil: si le nom de la méthode ne semble pas correct ou est difficile à trouver, vous devriez probablement le supprimer.
Une méthode valide l'entrée et appelle ensuite l'autre méthode:
Hello(string name, int start, int end)
{
if (name == null) throw new ArgumentNullException(...);
if (start < 0) throw new OutOfRangeException(...);
...
if (end < start) throw new ArgumentException(...);
this.World(name, start, end);
}
Au lieu de cela, World
devrait valider ses paramètres lui-même, ou être privé.
Si quelque chose est public, il peut être appelé à tout moment par n'importe quel système. Il n'y a aucune raison pour que vous ne puissiez pas être l'un de ces systèmes aussi!
Dans les bibliothèques hautement optimisées, vous souhaitez copier l'idiome mis en avant dans Java.util.ArrayList#ensureCapacity(int)
.
ensureCapacity
est publicensureCapacity
a toutes les limites de vérification et valeurs par défaut nécessaires, etc.ensureCapacity
appelle ensureExplicitCapacity
ensureCapacityInternal
est privéensureCapacityInternal
a une vérification d'erreur minimale car toutes les entrées proviennent de l'intérieur de la classeensureCapacityInternal
appelle également ensureExplicitCapacity
ensureExplicitCapacity
est AUSSI privéensureExplicitCapacity
a non vérification d'erreurensureExplicitCapacity
fait le travail réelensureExplicitCapacity
n'est appelé de nulle part sauf par ensureCapacity
et ensureCapacityInternal
De cette façon, le code en lequel vous avez confiance obtient un accès privilégié (et plus rapide!) Car vous savez que ses entrées sont bonnes. Le code auquel vous ne faites pas confiance passe par une certaine rigueur pour vérifier son intégrité et bombarder ou fournir des valeurs par défaut ou gérer autrement de mauvaises entrées. Tous deux se dirigent vers l'endroit qui fonctionne réellement.
Cependant, cela est utilisé dans ArrayList, l'une des classes les plus utilisées dans le JDK. Il est très possible que votre cas ne nécessite pas ce niveau de complexité et de rigueur. En bref, si tous les appels ensureCapacityInternal
étaient remplacés par des appels ensureCapacity
, les performances seraient toujours vraiment, vraiment bonnes. Il s'agit d'une microoptimisation qui n'a probablement été réalisée qu'après mûre réflexion.
Plusieurs bonnes réponses ont déjà été données, et je serais également d'accord avec elles sur le fait qu'un objet peut appeler ses méthodes publiques à partir de ses autres méthodes. Cependant, il y a une légère mise en garde de conception que vous devrez surveiller.
Les méthodes publiques ont généralement un contrat de "prendre l'objet dans un état cohérent, faire quelque chose de sensé, laisser l'objet dans un état cohérent (éventuellement différent)". Ici, "cohérent" peut signifier, par exemple, que le Length
d'un List<T>
n'est pas plus grand que son Capacity
et que référencer les éléments aux indices de 0
à Length-1
ne jettera pas.
Mais à l'intérieur des méthodes de l'objet, l'objet peut être dans un état incohérent, donc lorsque vous appelez l'une de vos méthodes publiques, cela peut faire une très mauvaise chose, car il n'a pas été écrit avec une telle possibilité à l'esprit. Donc, si vous prévoyez d'appeler vos méthodes publiques à partir de vos autres méthodes, assurez-vous que leur contrat est "prenez l'objet dans un état, faites quelque chose de sensé, laissez l'objet dans un (peut-être différent) (peut-être incohérent - mais seulement si l'initiale état incohérent) ".
Un autre exemple lorsque l'appel d'une méthode publique à l'intérieur d'une autre méthode publique est tout à fait correct est une approche CanExecute/Execute. Je l'utilise quand j'ai besoin des deux validation et conservation invariante .
Mais en général, je suis toujours prudent à ce sujet. Si une méthode a()
est appelée dans la méthode b()
, cela signifie que la méthode a()
est un détail d'implémentation de b()
. Très souvent, cela indique qu'ils appartiennent à différents niveaux d'abstraction. Et le fait qu'ils soient tous les deux publics me fait me demander s'il s'agit d'une violation principe de responsabilité unique ou non.