2009-06-12 7 views
1

J'ai un problème pour concevoir une partie de mon programme (ne l'écris pas, pour une fois!). C'est un peu difficile à expliquer sans écrire un roman, alors je vais essayer d'être bref.Correct façon de mettre en page l'interface C#

Fondamentalement, j'ai un programme qui lit/écrit des paramètres d'un morceau de matériel. Actuellement, il le fait sur Serial, mais finalement, je vais le faire sur USB, en utilisant l'encapsuleur .NET pour la puce FTDI http://www.ftdichip.com/Projects/CodeExamples/CSharp.htm

Je pense que mon problème est, je sais que je veux plusieurs couches d'abstraction , mais je n'arrive pas à savoir où dessiner les lignes. Premièrement, je ne veux pas que mes fonctions ReadParam(), WriteParam() et SendCommand() soient assises dans ma classe principale. Cela semble juste pavé. Donc, évidemment, ils devraient être dans une autre classe, que je vais instancier. Appelons cela Comm pour l'instant.

La première option est, je pourrais faire une interface, disons IComm, et mes deux modes Serial et USB implémentent cela. Le problème avec ceci est, un grand pourcentage du code serait dupliqué dans les deux saveurs, parce que j'ai ReadReplyData() spécial et d'autres fonctions, qui font le pré-traitement des données sérielles avant qu'elles le retournent à l'interface graphique. L'option suivante est donc Comm, une classe intermédiaire, qui définit une interface ICommDriver. Comm implémenterait une fonction de formatage privée ReadReplyData(), ainsi que les fonctions publiques ReadParam(), WriteParam() et SendCommand(), alors que ICommDriver ne spécifierait que des fonctions Read et Write plus simples.

Tout cela semble trivial, sauf pour deux rebondissements. Premièrement, je veux que ce soit multi-theaded, évidemment, de sorte que l'interface graphique ne se bloque pas. Donc, je pense que Comm utiliserait un BackgroundWorker pour faire toutes les lectures/écritures. En outre, la saveur Serial doit être indiquée au port COM à ouvrir (à partir d'une liste déroulante GUI), alors que la saveur USB ne le fait pas. Est-ce que je fais cette partie de l'interface ou pas?

Merci pour votre aide tout le monde, j'ai écrit/supprimé du code pendant des jours en essayant de trouver la bonne façon de le faire!

Jonathon

Répondre

7

Je sais que je veux plusieurs couches de abstraction, mais je ne peux pas sembler savoir où tracer les lignes

C'est là votre problème. C'est une approche fondamentalement erronée du développement et c'est exactement ce qui mène à cette paralysie. Développer plusieurs concrets implémentations des saveurs en premier. Faites-les travailler dans votre application avec la logique de kludgy if type1 else type2. Puis revenez en arrière et refactoriser tous pour partager un contrat commun, base commune, qu'avez-vous. Ce sera de façon aveuglante ce qui doit aller où.

Avec plus de détails dans les commentaires:

Si vous avez un code partagé entre les implémentations, vous devez utiliser une classe abstraite. Dans mon expérience, il est la meilleure pratique de garder les méthodes publiques finales et appeler des méthodes abstraites protégées des méthodes publiques, comme ceci:

public interface IComm 
{ 
    void WriteParam(...); 
} 

public abstract class CommStandardBase : IComm 
{ 
    public void WriteParam(...) 
    { 
     DoWriteParam(...); 
    } 

    private void DoWriteParam(...) 
    { 
     CommonWrite1(...); 
     HandleWriteParam(...); 
     CommonWrite2(...); 
    } 

    protected abstract void HandleWriteParam(...); 

    private void CommonWrite1(...) 
    { 
     ... 
    } 

    private void CommonWrite2(...) 
    { 
     ... 
    } 
} 

Faire de chaque classe autonome. Il devrait être à instance unique, à un seul thread et peut être passé entre les travailleurs et les journalistes.

+0

J'ai déjà le logiciel qui fonctionne, et j'essaie de refactoriser et de nettoyer le code. Comme je l'ai mentionné, je déteste avoir mes fonctions 'ReadParam()' et WriteParam() 'dans ma classe MainForm. Les deux versions sont essentiellement des wrappers de la classe .NET SerialPort et de la classe FTD2XX_NET de FTDI. Ils sont donc presque identiques, à l'exception des fonctions sous-jacentes qu'ils appellent. –

+1

@Jonathon qu'est-ce qui ne va pas avec une classe abstraite qui implémente la fonctionnalité partagée? –

+0

Je suis d'accord avec cette implémentation lorsque vous avez ce problème de code partagé. –

1

En ce qui concerne exactement quel type d'interfaces dont vous avez besoin, qui est en fin de compte à vous et celui qui sait comment cette application fonctionne à ce faible niveau. Je voudrais répondre à la partie sur l'utilisation de vos implémentations dans une interface utilisateur et le commentaire sur Comm en utilisant un BackgroundWorker. Je recommanderais d'inverser cela. BackgroundWorker est vraiment un composant de niveau d'interface utilisateur .... mais Comm serait plutôt un composant "central" (comme un objet métier dans une application d'entreprise). Votre interface utilisateur doit créer un BackgroundWorker qui crée ensuite des instances Comm pour effectuer le travail requis et orchestrer les événements de votre communication pour mettre à jour l'interface utilisateur. Si vous avez besoin de votre interface et de BackgroundWorker pour communiquer pendant une longue période, je vous recommande de créer un objet de transfert de données que votre interface utilisateur peut créer, déposer dans une file d'attente et utiliser les handles de threads ManualResetEvent ou AutoResetEvent pour communiquer entre votre interface utilisateur thread et le BackgroundWorker. Cela devrait vous donner un produit plus faiblement couplé, vous permettant de développer votre classe Comm indépendamment de tout type d'interface utilisateur qui peut l'afficher (vous permettant éventuellement d'avoir des WindForms, WPF, ligne de commande, et peut-être même des clients PowerShell.)

1

Je suis un peu en mode VB.NET au momement ... Voici donc ce

 

Interface IComm 

    Function ReadParam() 
    Function WriteParam() 
    Function SendCommand() 

End Interface 



> 


MustInherit Class CommBase 

.... Load this up with the overideable 

End Class 


 

Ensuite, mettre en œuvre que l'interface et hériteront la base si nécessaire. Je suis également d'accord avec Rex M. Ne pas pousser trop loin pour un couplage lâche.

Questions connexes