Interface C#: Comprendre la connexion du formulaire de prix (Tim Corey, Leçon 09)
Dans la série " C# App Start To Finish " de Tim Corey, la leçon 09 est centrée sur le câblage d'un formulaire de prix. À première vue, ce formulaire semble simple — il suffit de recueillir l'entrée utilisateur, de la valider, de créer un modèle, et de l'enregistrer. Mais Tim explique que la véritable complexité réside dans le choix du lieu d'enregistrement des données : une base de données, un fichier texte, ou les deux. La vidéo de Tim nous guide à travers la solution en introduisant un concept clé dans la programmation C#: les interfaces.
Dans cet article, nous examinerons de plus près les interfaces grâce à l'explication de Tim pour mieux comprendre comment elles aident à créer des applications évolutives et maintenables.
Le problème : où enregistrer les données ?
Tim commence par indiquer le but du formulaire de prix : il accepte l'entrée, la valide, et l'enregistre dans un stockage. Mais il avertit que la partie délicate est de décider où stocker les données. Il souligne que les tutoriels passent souvent à côté de cela car ce n'est pas facile, mais il veut que les apprenants s'y attaquent de front.
Il explique qu'au départ, vous pourriez tenter une solution simple : vérifier si vous utilisez SQL ou un fichier texte, puis exécuter le processus d'enregistrement correct. Mais Tim montre rapidement à quel point cela devient laid et difficile à maintenir. Si chaque formulaire doit vérifier quel type de stockage utiliser, le code devient dupliqué, désordonné et difficile à modifier.
La manière laide : conditions codées en dur
Tim esquisse un exemple de pseudocode. Il explique que vous pourriez commencer en vérifiant une valeur booléenne comme usingSQL == true, puis ouvrir une connexion à une base de données, enregistrer le modèle, et le retourner avec un ID. Ensuite, vous pourriez faire la même chose pour les fichiers texte, en générant manuellement un ID parce que les fichiers texte ne le font pas automatiquement.
Il souligne que cela devient rapidement répétitif. Plusieurs formulaires nécessitent cette logique, et chaque fois que vous ajoutez une nouvelle source de données comme MySQL, vous devez mettre à jour chaque formulaire. Tim appelle cela " non évolutif " et souligne que cela viole le principe " DRY " (Don't Repeat Yourself - Ne vous répétez pas). Il déclare clairement : " Il doit y avoir une meilleure façon. "
Tirer le fil : La meilleure approche
Tim présente sa stratégie : tirer le fil. Il commence par demander quelles informations le code a besoin et d'où elles proviennent. Il identifie deux questions clés :
Comment savons-nous quelle source de données utiliser ?
Comment nous connectons-nous à deux sources de données différentes pour effectuer la même tâche ?
Tim explique que l'acte réel de sauvegarde est la seule chose qui diffère. Du point de vue du formulaire, il doit seulement déclarer : " Voici le modèle. ". Sauvegardez-le. Le formulaire ne doit pas se soucier de savoir s'il enregistre sur SQL ou un fichier texte.
La solution : Configuration globale + Interface
Tim suggère un système de configuration global. Il dit que pour savoir quelle source de données utiliser, l'application a besoin de données accessibles à l'échelle mondiale, et il propose d'utiliser une classe statique pour stocker cette information. Il reconnaît que les variables globales sont généralement évitées, mais dans ce cas, les données globales sont exactement ce qui est nécessaire.
Ensuite, Tim explique le concept clé : les interfaces. Il définit une interface comme un contrat - une promesse selon laquelle toute classe qui l'implémente contiendra certaines méthodes ou propriétés. Tim souligne que cela permet à l'application d'appeler la même méthode quelle que soit la source de données. Le formulaire ne se soucie pas si c'est SQL ou un fichier texte ; il se soucie seulement d'appeler la méthode.
Tim déclare : " Si vous avez besoin d'accomplir la même tâche mais qu'en arrière-plan elle sera réalisée de deux manières différentes, vous avez besoin d'une interface. "
Création de l'interface
Tim passe à l'implémentation pratique en créant une interface dans la bibliothèque Tracker. Il la nomme IDataConnection et explique la convention qui consiste à préfixer les interfaces par " I ". Il souligne que c'est important pour l'identifier clairement comme une interface.
Tim ajoute une seule méthode à l'interface :
PrizeModel CreatePrize(PrizeModel model);
Il explique que cette méthode est un contrat : elle doit exister dans toute classe qui implémente IDataConnection. Le formulaire appellera cette méthode et s'attendra à recevoir un PrizeModel avec un ID. Tim explique que c'est ainsi que le formulaire reste neutre par rapport au type de stockage.
Création de la classe statique Global Config
Ensuite, Tim crée une classe statique nommée GlobalConfig. Il explique qu'une classe statique ne peut pas être instanciée et est accessible à l'échelle mondiale. C'est là que l'application stockera la liste des connexions de données disponibles.
Il définit une propriété :
public static List<IDataConnection> Connections { get; private set; }
Tim explique l'utilisation du private set afin que seule la classe elle-même puisse modifier la liste, tandis que d'autres parties de l'application ne peuvent que la lire.
Il crée ensuite la méthode :
public static void InitializeConnections(bool database, bool textFiles)
Cette méthode configure les connexions de données disponibles. Tim souligne que la liste permet plusieurs connexions, ce qui signifie que l'application peut enregistrer sur SQL, des fichiers texte, ou les deux.
Comprendre les interfaces : Un exemple réel
Tim fait une pause pour rassurer les apprenants que c'est un sujet complexe, mais réalisable. Il recommande de regarder la vidéo une fois, puis de la revoir tout en codant en même temps.
Il explique que l'interface est un contrat, et que toute classe l'implémentant doit suivre ce contrat. Il démontre cela en créant une classe SQLConnector qui implémente IDataConnection.
Lors de la création de la classe, Visual Studio avertit que le contrat n'est pas rempli. Tim montre comment utiliser " Implémenter l'interface " pour générer automatiquement la méthode CreatePrize. Il explique également le NotImplementedException scaffold et pourquoi il existe - il permet de compiler le code sans faire semblant que la méthode fonctionne.
Création des connecteurs SQL et texte
Tim ajoute la classe SQLConnector et la classe TextConnector, toutes deux implémentant IDataConnection. Il explique que même si enregistrer dans une base de données SQL et enregistrer dans un fichier texte sont des processus très différents, ils satisfont tous deux au même contrat d'interface.
Il ajoute une valeur de retour simple pour l'instant et place des commentaires TODO pour se rappeler d'implémenter la véritable logique de sauvegarde plus tard. Cela permet à l'application de rester fonctionnelle tout en avançant dans le cours.
Configuration finale : Brancher la configuration globale
Tim revient à la classe GlobalConfig et connecte les connexions réelles. Il montre comment initialiser la liste des connexions et y ajouter des instances de SQLConnector et TextConnector.
Il explique pourquoi deux instructions if séparées sont nécessaires au lieu de if-else - parce que l'utilisateur peut vouloir enregistrer sur les deux sources de données simultanément.
Où appeler InitializeConnections ?
Tim explique qu'InitializeConnections doit être appelé au démarrage de l'application. Il modifie Program.cs et appelle :
GlobalConfig.InitializeConnections(true, true);
avant de lancer le formulaire. Cela garantit que la liste des connexions est prête et accessible dans toute l'application.
Il change ensuite le formulaire de démarrage pour CreatePrizeForm afin de pouvoir tester la fonctionnalité immédiatement.
Validation du formulaire Prize
Tim ouvre le formulaire et explique la première tâche : valider les quatre champs. Il préfère garder les gestionnaires d'événements propres, donc il crée une méthode privée nommée ValidateForm().
Tim explique que cette méthode peut être appelée de n'importe où, pas seulement au clic du bouton. Elle retourne un booléen indiquant si le formulaire est valide ou non. Il montre son modèle d'utilisation d'une variable de sortie :
bool output = true; return output;
Il dit qu'il aime commencer par true parce qu'il est plus facile de passer à false quand quelque chose ne va pas que de le définir à true après chaque vérification.
Vérification du numéro de place
Tim explique la première validation : le numéro de place doit être un entier supérieur à zéro.
Il utilise int.TryParse pour convertir le PlaceNumberValue.Text (une chaîne) en un entier. Tim décompose comment fonctionne TryParse :
-
Il prend une chaîne et tente de la convertir en un nombre.
-
Il retourne un booléen indiquant le succès ou l'échec.
- Il utilise un paramètre en sortie pour produire la valeur convertie.
Tim souligne que TryParse est plus sûr que Parse parce qu'il ne se plante pas en cas de mauvaise entrée - il retourne false et définit la sortie à zéro.
Il explique ensuite la logique :
-
Si placeNumberValidNumber est false, définir output = false.
- Si placeNumber < 1, définir output = false.
Tim met en garde contre l'utilisation d'instructions else ici parce que cette méthode a plusieurs vérifications. Si une vérification échoue, la méthode doit encore évaluer les autres pour collecter toutes les erreurs.
Validation du nom de place
Tim passe à la validation suivante : le nom de place ne doit pas être vide.
Il vérifie :
if (placeNameValue.Text.Length == 0) { output = false; }
Tim explique que dans une application réelle, vous afficheriez des messages d'erreur pour chaque validation échouée. Mais pour l'instant, il garde ça simple et ne retourne que vrai/faux.
Validation du montant et du pourcentage de prix
Tim explique que le formulaire doit contenir soit un montant soit un pourcentage de prix (l'un d'entre eux doit être supérieur à zéro). Il note la différence importante :
-
Le pourcentage de prix est un nombre entier (int)
- Le montant de prix est un décimal (decimal) car l'argent peut inclure des centimes.
Il crée des variables :
decimal prizeAmount = 0; int prizePercentage = 0;
Puis il utilise TryParse pour les deux :
bool prizeAmountValid = decimal.TryParse(prizeAmountValue.Text, out prizeAmount); bool prizePercentageValid = int.TryParse(prizePercentageValue.Text, out prizePercentage);
Tim explique que les deux doivent être des nombres valides. Si l'un de ceux-ci est invalide, le formulaire est invalide.
Ensuite, il vérifie qu'au moins un est supérieur à zéro :
if (prizeAmount <= 0 && prizePercentage <= 0) { output = false; }
Tim ajoute également une vérification pour s'assurer que le pourcentage est compris entre 0 et 100 :
if (prizePercentage < 0|| prizePercentage > 100) { output = false; }
Il explique pourquoi : 150 % signifierait que vous donnez plus que le montant total du prix, ce qui est impossible.
Utilisation du résultat de la validation
Une fois toutes les vérifications effectuées, Tim explique comment utiliser le résultat :
if (ValidateForm()) { // créer le modèle et sauvegarder } else { MessageBox.Show("Ce formulaire contient des informations invalides. Veuillez le vérifier et réessayer."); }
Tim note que vous pourriez retourner plus tôt sur le premier échec, mais il choisit de faire passer tous les contrôles, afin que les utilisateurs puissent voir toutes les erreurs de validation en une seule fois. Cela réduit la frustration car ils peuvent tout corriger d'un coup.
Création de PrizeModel
Tim explique qu'une fois le formulaire validé, l'étape suivante consiste à créer un PrizeModel.
Il montre comment instancier un modèle :
PrizeModel model = new PrizeModel(); model.PlaceName = placeNameValue.Text; model.PlaceNumber = placeNumberValue.Text; // problème : ceci est une chaîne
Tim souligne le problème : PlaceNumber est un int, mais la valeur du formulaire est une chaîne. Pour résoudre cela, il explique deux options :
-
Parsea chaque valeur à nouveau dans le formulaire (répétitif).
- Ajouter une surcharge du constructeur dans PrizeModel.
Tim choisit l'option 2.
Constructeur surchargé dans PrizeModel
Tim ajoute un constructeur surchargé qui accepte quatre chaînes :
public PrizeModel(string placeName, string placeNumber, string prizeAmount, string prizePercentage)
{
PlaceName = placeName;
PlaceNumber = int.TryParse(placeNumber, out int placeNumberValue) ? placeNumberValue : 0;
PrizeAmount = decimal.TryParse(prizeAmount, out decimal prizeAmountValue) ? prizeAmountValue : 0;
PrizePercentage = double.TryParse(prizePercentage, out double prizePercentageValue) ? prizePercentageValue : 0;
}
Tim explique qu'il ne se soucie pas si l'analyse échoue parce qu'elle reviendra par défaut à zéro, qui est la valeur par défaut pour les nombres de toute façon.
Ce constructeur permet au formulaire de créer un PrizeModel directement avec des entrées de chaîne et laisse le modèle gérer l'analyse.
Sauvegarde du modèle en utilisant IDataConnection
Maintenant que le modèle existe, Tim explique comment le sauvegarder en utilisant la liste globale de connexions.
Il utilise une boucle foreach :
foreach (IDataConnection db in GlobalConfig.Connections) { db.CreatePrize(model); }
Tim explique que cette boucle appelle CreatePrize() sur chaque connexion (SQL et fichier texte). Même si les méthodes ne sont pas encore implémentées, le formulaire fonctionne et fait semblant de sauvegarder les données. Cela prouve que le modèle d'interface et de configuration globale fonctionne.
Tester le formulaire
Tim insiste sur l'importance de tester tôt. Il ajoute des points d'arrêt et exécute l'application.
-
Il teste d'abord un formulaire vide.
-
Il passe en revue ValidateForm().
-
Il observe que la sortie est fausse et que la validation échoue comme prévu.
-
Ensuite, il remplit les données valides et confirme que le constructeur peuple correctement le modèle.
- Il confirme également que la boucle se déroule à travers les deux connexions.
Tim démontre que le formulaire est fonctionnel et que le modèle est validé.
Nettoyage final : Effacer le formulaire
Tim effectue quelques ajustements finaux :
-
Après avoir créé un prix avec succès, effacez les champs du formulaire.
- Définissez les valeurs par défaut à 0 pour le montant du prix et le pourcentage afin que les utilisateurs n'aient pas à taper zéro à chaque fois.
Il confirme ensuite que le formulaire s'efface correctement après une soumission valide.
Quelle est la suite ?
Tim termine la vidéo en disant que la prochaine étape est de relier les classes de connexion SQL et texte pour qu'elles enregistrent réellement les données.
Il rappelle aux spectateurs de rester à l'écoute pour la prochaine leçon, où il mettra en œuvre le connecteur SQL et se connectera réellement à la base de données.
