2017-01-24 10 views
0

J'ai une classe Java appelée TestExecutor qui est responsable de starting un test. Démarrage du test implique plusieurs étapes:Bon exemple de conception orientée objet en Java

- Update test repository 
- Locate the test script 
- Create result empty directory 
- Execute command 
- Parse output 
- Update database 

Pour chacune de ces étapes que j'ai créé des méthodes privées dans la classe TestExecutor qui effectuent chacune des actions ci-dessus, le tout entouré d'un bloc try-catch. Je suis conscient que ce n'est pas un bon design car ma classe en fait trop et est également un test de douleur à l'unité en raison d'une grande quantité de fonctionnalités cachées dans des méthodes privées. Je voudrais entendre vos suggestions pour refactoriser cette classe car je ne sais pas comment s'éloigner de quelque chose de semblable à la structure ci-dessus. Exemple de code ci-dessous:

public void start() throws TestExecuteException { 
    try { 
     updateRepository(); 
     locateScript(); 
     createResultDirectory(); 
     executeCommand(); 
     parseOutput(); 
     updateDatabase(); 
    catch(a,b,c) { 
    } 
} 

private updateRepository() { 
    // Code here 
} 
// And repeat for other functions 
+0

Pourquoi vos méthodes sont-elles privées et non publiques? Il n'y a aucun problème à avoir plusieurs méthodes dans une seule classe tant que la classe a une seule responsabilité. – underdog

+0

Il n'y a aucune raison pour que les méthodes soient appelées par quelque chose d'extérieur à TestExecutor donc j'ai décidé de les rendre privées. Mon problème est que la classe a une seule responsabilité générale (exécution de test) mais il y a plusieurs responsabilités en dessous (registre de mise à jour, script de localisation, etc.) – Adam

+0

@Adam Veuillez passer en revue les réponses qui vous ont été fournies et commenter . Si l'une des réponses a été utile, vous pouvez les augmenter. Vous pouvez également accepter une réponse en cliquant sur la coche à côté de la réponse qui a été la plus utile. S'il vous plaît lire [Que dois-je faire quand quelqu'un répond à ma question] (http://stackoverflow.com/help/someone-answers). Voter est gratuit. Cela ne vous coûte rien. Accepter une réponse vous donne +2 rep. Alors ne sois pas timide. Allez-y et payez-le! – CKing

Répondre

0

Bien que votre classe me semble correcte.

Je suis conscient que ce n'est pas une bonne conception que ma classe fait trop

En ce qui concerne la classe a une responsabilité unique le nombre de méthodes ne comptent pas.

Cochez cette case template method design pattern. Votre classe fait quelque chose de similaire à ce que fait la classe abstraite Game.

public abstract class Game { 
    abstract void initialize(); 
    abstract void startPlay(); 
    abstract void endPlay(); 

    //template method 
    public final void play(){ 

     //initialize the game 
     initialize(); 

     //start game 
     startPlay(); 

     //end game 
     endPlay(); 
    } 
} 

et est aussi une douleur au test unitaire en raison d'une grande quantité de fonctionnalités étant cachée dans des méthodes privées

Lire this & this sur le test des méthodes privées. Vous pouvez également utiliser un framework comme PowerMock qui vous aide à tester le code non testable.

+0

La classe semble avoir de multiples responsabilités. 1) Parlez à la base de données. 2) Parlez aux annuaires. 3) Analyser les données et créer une sortie. – CKing

0

J'aimerais entendre vos suggestions pour refactorisation cette classe comme je ne suis pas sûr de savoir comment sortir de quelque chose de similaire à la structure ci-dessus

Vous devriez vraiment regarder à la SOLID principes comme point de départ pour écrire du code orienté objet propre et testable. J'ai été présenté au début de ma carrière et cela aide beaucoup à suivre ces principes. Cela dit, je commencerais par regrouper des fonctionnalités connexes dans différentes classes. Par exemple, updateRepository() et updateDatabase() peuvent être déplacés vers une classe distincte appelée DatabaseHelper. De même locateScript() et createResultDirectory() semblent être des opérations liées au disque et peuvent être déplacés vers une classe séparée appelée DirectoryHelper. Je crois que vous comprenez l'essentiel. Ce que vous venez d'atteindre est Seperation of Concerns.

Maintenant que vous avez des classes séparées, vous devez les rassembler et les mettre au travail. Votre TestExecutor peut continuer à avoir le methods que vous avez répertorié. Le seul différent sera que ces méthodes vont maintenant déléguer leur travail aux classes individuelles que nous avons créées ci-dessus. Pour cela, TestExecutor aura besoin d'une référence aux classes DatabaseHelper et DirectoryHelper.Vous pouvez simplement instancier ces classes directement à l'intérieur du TestExecutor. Mais cela signifierait que TestExecutor est étroitement couplé à une implémentation. Ce que vous pouvez faire à la place est de demander le code en dehors de TestExecutor pour fournir le DatabaseHelpe et DirectoryHelper à utiliser. Ceci est connu comme Inversion de dépendance à Injection de dépendance. L'avantage de cette approche est que vous pouvez maintenant passer n'importe quelle sous-classe de DatabaseHelper et DirectoryHelper à TaskExecutor et qu'elle n'a pas besoin de connaître les détails de la mise en œuvre. Cela facilite le test unitaire de TaskExecutor en se moquant de ces dépendances au lieu de transmettre des instances réelles.

Je vais laisser le reste des principes SOLID à explorer, implémenter et apprécier.

0

Je ferais de cette façon. D'abord, appliquer le contrat que chaque étape de test devrait avoir.

interface TestCommand{ 
    void run(); 
} 

Maintenant, faites vos commandes de test en tant que classes séparées. Essayez de rendre ces classes de commandes génériques afin de pouvoir les réutiliser pour des types de commandes similaires. Maintenant dans votre classe où vous voulez exécuter un test, configurez les étapes de test comme suit.

//in your test class do this. 
List<TestStep> testCommands = new ArrayList<>(); 
testCommands.add(new UpdateRepoCommand()); 
testCommands.add(new LocateScriptCommand()); 
// and so on.... 

Maintenant, exécutez toutes vos étapes chronologiquement. En outre, en tant que CKing décrit ci-dessus, suivre le principe SOLID à l'intérieur de ces étapes de test. Injectez les dépendances et écrivez le test unitaire séparément.