Est-ce considéré comme un anti-motif de lire STDIN depuis une bibliothèque?

39

Lors de l'écriture d'une bibliothèque pour un grand projet sur lequel je travaillais au travail, un problème nécessitant l'envoi d'un jeton à une adresse électronique, puis sa réintégration dans le code, afin de pouvoir l'utiliser ultérieurement.

Mon collègue me dit simplement de lire STDIN (en utilisant Python:) code = input("Enter code: "), puis de le faire passer par un utilisateur. Toutefois, cela me semble une mauvaise pratique, car la bibliothèque pourrait (dans ce cas, le sera certainement) être utilisée dans une tâche en arrière-plan sur un serveur. .

Je me demandais si cela était considéré ou non comme un anti-modèle.

Paradoxis
la source
45
Tout ce qui est mauvais n’est pas nécessairement un "anti-modèle", bien que ce soit certainement mauvais.
Phoshi
4
"motif" signifie quelque chose que les programmeurs font fréquemment. Ce n'est qu'un anti-modèle si c'est à la fois (A) une mauvaise idée et (B) quelque chose que les développeurs font tout le temps.
Salomon Slow
20
C'est beaucoup trop bête pour être un anti-modèle. Un anti-motif est quelque chose qui semble naturel et sensé, mais qui s'avère mauvais quand on y jette un œil. Ce que vous décrivez ici est évidemment horrible.
Evan Harper
Je ne vois le sens d'aucune des réponses. L'exercice de jeton d'utilisateur doit avoir pour objectif de prouver que l'adresse électronique de l'utilisateur fonctionne. Si ce n'était pas le cas, il serait beaucoup plus simple de simplement stocker le jeton.
emory
2
C'est assez affreux. Si le passage d'un tel jeton est absolument nécessaire, vous pouvez créer un fichier exécutable séparé à l'aide de la bibliothèque et le transmettre à son stdin. Mais détourner le stdin de l'exécutable appelant, c'est un non-non.
Grandmasterb

Réponses:

78

En règle générale, les bibliothèques doivent être totalement déconnectées de l'environnement. Cela signifie qu’ils ne doivent pas effectuer d’opérations sur des flux standard, sur des fichiers spécifiques, ni s’attendre à l’environnement ou au contexte dans lequel ils sont utilisés.

Bien sûr, il y a des exceptions à cette règle, mais il doit y avoir une très bonne raison pour cela. Dans le cas de l'utilisation stdin, je ne trouve aucune raison (à moins que votre bibliothèque ne fournisse réellement des routines pour lire à partir de stdin, comme à std::cinpartir de C ++). De plus, prendre les flux d'E / S à partir d'un paramètre plutôt que de les coder en dur ajoute tellement de flexibilité qu'il ne vaut pas la peine de ne pas le faire.

Paul92
la source
36
L'exception est une bibliothèque qui a pour objectif spécifique d'interagir avec un environnement. Même dans ce cas, les détails de l'environnement doivent être abstraits. Par exemple, un pilote graphique doit communiquer avec le bus PCIe, mais l'ID du bus doit être fourni via la configuration.
C’est le genre d’exception à laquelle je pensais: mon idée était plus proche de ncurses - en général, une bibliothèque d’interface utilisateur à environnement texte. Si son but est de lire l'entrée utilisateur et de fournir une sortie utilisateur, c'est une bonne raison.
SF.
5
@SF. Même une bibliothèque comme ncurses devrait prendre une paire de descripteurs de fichiers comme arguments plutôt que de coder en dur l’utilisation de 0 et 1. Vous voudrez peut-être écrire un programme dans lequel stdin et stdout peuvent être redirigés et que vous voulez ouvrir /dev/ttypour communiquer avec le utilisateur. Le programme peut même être lancé sans terminal et ouvrir son propre terminal avec xterm -S.
Kasperd
3
@kasperd: La meilleure approche consiste à fournir des valeurs par défaut raisonnables et la capacité de les remplacer.
SF.
1
Dans ce cas particulier, je ne vois aucune raison de demander un flux en entrée. Pourquoi ne pas simplement accepter le jeton en tant que paramètre?
jpmc26
16

Je considérerais que ce n’est pas nécessairement un anti-modèle, mais une bibliothèque mal conçue. Il devrait être facile de demander une chaîne en tant que paramètre de méthode, où l'entrée pourrait être passée directement.

Si cela ne correspond pas à cette utilisation, un paramètre de méthode peut être un flux, avec STDIN transmis à la méthode.

Si cela ne correspond pas à cet usage, la bibliothèque n'est pas assez flexible.

Bryan Boettcher
la source
4

Envisagez peut-être d’avoir la possibilité dans votre bibliothèque de définir un rappel sur une fonction fournie par l’utilisateur qui lira les entrées de n’importe où , puis renverra la valeur appropriée à la partie de la bibliothèque qui utilise cette fonction.

FrustratedWithFormsDesigner
la source
1

Si elle lit à partir de stdin, cela signifie qu'elle aimerait s'approprier stdin au niveau du programme. Cela n’est probablement pas compatible avec les autres bibliothèques qui lisent à partir de stdin, protocole moins spécifique pour la façon dont elles partagent l’utilisation. Dans au moins mon glossaire personnel, cela ferait de la bibliothèque un cadre , ce qui représente un compromis coûteux.

Mais dans ce cas, la bibliothèque devrait probablement prendre uniquement un descripteur de fichier d’entrée.

Djechlin
la source
0

La réponse de @ Paul92 est une bonne discussion générale, mais je voudrais proposer une solution propre (ish) à cela:

Dans une bibliothèque, ce code doit être adaptable à tout environnement d'exécution. Vous ne pouvez donc pas vraiment demander STDINdes données cruciales. D'une part, les utilisateurs de votre bibliothèque pourraient ne pas disposer de stdin pour un certain nombre de raisons. Au lieu de cela, vous souhaiterez peut-être utiliser une forme de modèle de stratégie pour personnaliser le mode de récupération du jeton.

En Python, la meilleure option est probablement de passer la stratégie d'extraction de jetons en tant que paramètre de fonction. Quelque chose comme ca:

def stdin_prompt():
    return input("Enter code: ")

def my_library_function(arg1, arg2, ... argn, token_provider = stdin_prompt):
    ...
    token = token_provider()
    ...
    return stuff

# somewhere in the user code
stuff = my_library_function(a1, a2, ... an, lambda: "123456")

Pensez-y comme ça. Le jeton dont vous avez besoin est un argument de la fonction de bibliothèque. Étant donné que la valeur du jeton peut ne pas être connue de manière statique sur le site de l'appel, vous ne pouvez pas réellement demander la valeur en tant qu'argument. Au lieu de cela, l'appelant doit fournir une fonction qui sera chargée de fournir le jeton lors de l'appel.

Toute la responsabilité de fournir les mécanismes exacts du jeton est maintenant externalisée de la fonction de bibliothèque. Le consommateur de la fonction est maintenant responsable de l’acquisition du jeton par tout moyen disponible au moment de l’exécution. Il peut demander à STDIN, mais il peut également servir de passerelle de messagerie, attendre que le message apparaisse dans la boîte de réception, le lire, extraire le jeton et automatiser complètement le processus. Il peut s'agir d'une boîte de dialogue graphique ou d'un formulaire Web. N'importe quoi vraiment - toutes les options sont maintenant entre les mains du consommateur de la bibliothèque.

Roland Tepp
la source