2009-10-31 1 views
6

jamais sûr où placer des fonctions telles que:Création d'une classe AppToolbox Catch-All - Est-ce une mauvaise pratique?

String PrettyPhone(String phoneNumber) // return formatted (999) 999-9999 
String EscapeInput(String inputString) // gets rid of SQL-escapes like ' 

Je crée une classe Toolbox pour chaque application qui sert de référentiel pour les fonctions qui ne correspondent pas parfaitement dans une autre classe. J'ai lu que ces classes sont une mauvaise pratique de programmation, en particulier mauvaise conception orientée objet. Cependant, ces références semblent plus l'opinion des concepteurs individuels et des développeurs plus qu'un consensus général. Donc ma question est, est-ce une boîte à outils attrape-tout un modèle de conception pauvres? Si oui, pourquoi et quelle alternative y a-t-il?

Répondre

6

Bonne question. Je trouve toujours que tout projet suffisamment complexe nécessite des classes «utilitaires». Je pense que c'est simplement parce que la nature de la programmation orientée objet nous oblige à placer les choses dans une taxonomie hiérarchique bien structurée, quand cela n'est pas toujours possible ou approprié (par exemple, essayer de créer un modèle objet pour les mammifères). C'est le problème qui motive le travail dans aspect oriented programming (c.f. cross cutting concern). Souvent, ce qui entre dans une classe d'utilité sont des choses qui sont des préoccupations transversales. Une alternative à l'utilisation de classes d'outils ou utilitaires consiste à utiliser des méthodes d'extension pour fournir des fonctionnalités supplémentaires aux types primitifs. Cependant, le jury ne sait toujours pas si cela constitue un bon logiciel.

Mon dernier mot sur le sujet est: aller avec si vous avez besoin, assurez-vous simplement que vous n'êtes pas de raccourcir de meilleurs designs. Bien sûr, vous pouvez toujours refactoriser plus tard si vous en avez besoin.

1

Il n'y a rien de mal à cela. Une chose est d'essayer de le diviser en parties logiques. En faisant cela, vous pouvez garder votre intellisense propre.

MyCore.Extensions.Formatting.People 
MyCore.Extensions.Formatting.Xml 
MyCore.Extensions.Formatting.Html 
0

Je pense que la raison pour laquelle il est mal vu parce que la « boîte à outils » peut se développer et vous serez le chargement d'une tonne de ressources chaque fois que vous voulez appeler une seule fonction.

Il est également plus élégant d'avoir les méthodes qui s'appliquent aux objets de la classe actuelle - cela a tout simplement plus de sens. Cela dit, personnellement, je ne pense pas que ce soit un problème, mais je l'éviterais simplement pour les raisons ci-dessus.

2

Dans ces exemples, je serais plus enclin à étendre chaîne:

class PhoneNumber extends String 
{ 
    public override string ToString() 
    { 
     // return (999) 999-9999 
    } 
} 

Si vous écrivez tous les endroits que vous avez besoin de ces fonctions, vous pouvez comprendre ce qui utilise en fait et puis l'ajouter à la classe appropriée . Cela peut parfois être difficile mais encore quelque chose que vous devriez viser.

EDIT:

Comme indiqué ci-dessous, vous ne pouvez pas remplacer chaîne en C#. Le point que je tentais de faire est que cette opération est faite sur un numéro de téléphone afin que c'est là la fonction appartient:

interface PhoneNumber 
{ 
    string Formatted(); 
} 

Si vous avez des formats différents, vous pouvez échanger des implémentations de PhoneNumber sans encombrer votre code avec des instructions if , par exemple,

Au lieu de:

if(country == Countries.UK) output = Toolbox.PhoneNumberUK(phoneNumber); 
else ph = Toolbox.PhoneNumberUS(phoneNumber); 

Vous pouvez simplement utiliser:

output = phoneNumber.Formatted(); 
+0

La question concerne C#, pas Java. Quoi qu'il en soit, la classe String est scellée (finale en Java), dans .NET et Java –

2

Je pense une classe d'aide statique est la première chose qui vient à l'esprit. Il est si courant que certains s'y réfèrent même dans le cadre de la conception orientée objet. Cependant, le plus gros problème avec les classes auxiliaires est qu'elles ont tendance à devenir une grosse décharge. Je pense que j'ai vu cela se produire sur quelques-uns des plus grands projets dans lesquels j'étais impliqué. Vous travaillez sur un cours et vous ne savez pas où coller cette fonction et ainsi vous le mettez dans votre classe d'aide. A quel point vos assistants ne communiquent pas bien ce qu'ils font. Le nom 'helper' ou 'util' lui-même dans le nom de la classe ne veut rien dire. Je pense que presque tous les gourous OO défendent contre les aides car vous pouvez très facilement les remplacer par des classes plus descriptives si vous y réfléchissez suffisamment. J'ai tendance à être d'accord avec cette approche car je pense que les assistants violent le principe de la responsabilité unique. Honnêtement, prenez ceci avec un grain de sel. Je suis un peu opiniâtre sur OOP :)

+0

J'ai tendance à créer des helpers spécifiques dans une bibliothèque commune. Vous ne voyez donc pas de classe d'assistance générale contenant à la fois des fonctions d'assistance d'E/S et des aides de chaîne. Mais j'ai: IoHelper, StringHelper, et ainsi de suite. Voilà comment je reçois ma «séparation des préoccupations». ;-) –

1

Mon expérience a été que les fonctions d'utilité se produisent rarement isolément. Si vous avez besoin d'une méthode de formatage des numéros de téléphone, vous en aurez également besoin pour valider les numéros de téléphone et analyser les numéros de téléphone. En suivant le principe de YAGNI, vous ne voudriez certainement pas écrire de telles choses tant qu'elles ne sont pas réellement nécessaires, mais je pense qu'il est utile d'aller de l'avant et de séparer ces fonctionnalités en classes individuelles. La croissance de ces classes de méthodes uniques en sous-systèmes mineurs se produira naturellement au fil du temps. J'ai trouvé que c'était le moyen le plus facile de garder le code organisé, compréhensible et maintenable à long terme.

1

Lorsque je crée une application, je crée généralement une classe statique qui contient des méthodes statiques et des propriétés que je n'arrive pas à trouver ailleurs.

Ce n'est pas un design particulièrement bon, mais c'est un peu le point: cela me donne un endroit pour localiser toute une classe de décisions de conception que je n'ai pas encore pensé. Généralement, lorsque l'application se développe et est affinée par le refactoring, il devient plus clair où ces méthodes et propriétés devraient effectivement résider. Heureusement, l'état des outils de refactoring est tel que ces changements ne sont généralement pas exceptionnellement douloureux à faire.

J'ai essayé de le faire dans l'autre sens, mais l'inverse consiste essentiellement à implémenter un modèle d'objet avant d'en savoir assez sur mon application pour concevoir correctement le modèle d'objet. Si je fais que, je passe beaucoup de temps et d'énergie à trouver une solution médiocre que je dois revoir et reconstruire à partir de la base à un moment donné dans le futur. Bon, d'accord, si je sais que je vais refactoriser ce code, pourquoi ne pas passer à l'étape de la conception et de la construction des classes inutilement compliquées qui ne fonctionnent pas vraiment? Par exemple, j'ai construit une application qui est utilisée par plusieurs clients. J'ai compris assez tôt que je devais avoir un moyen de séparer les méthodes qui doivent fonctionner différemment pour différents clients. J'ai construit une méthode utilitaire statique que je pouvais appeler à n'importe quel point du programme où j'avais besoin d'appeler une méthode personnalisée, et je l'ai coincée dans ma classe statique.

Cela a bien fonctionné pendant des mois. Mais il arriva un moment où il commençait tout juste à avoir l'air laid. Et j'ai donc décidé de le refactoriser dans sa propre classe.Et quand j'ai parcouru mon code en regardant tous les endroits où cette méthode était appelée, il est devenu extrêmement clair que toutes les méthodes personnalisées devaient vraiment être membres d'une classe abstraite, les assemblages des clients devaient contenir une seule classe dérivée Cela implémente toutes les méthodes abstraites, puis le programme suffit pour extraire le nom de l'assembly et de l'espace de noms de sa configuration et créer une instance de la classe d'entités personnalisées au démarrage. C'était très simple pour moi de trouver toutes les méthodes qui devaient être personnalisées, car tout ce que j'avais à faire était de trouver tous les endroits où ma méthode load-a-custom-feature était appelée. Il m'a fallu une bonne partie de l'après-midi pour parcourir toute la base de code et rationaliser cette conception, et le résultat final est vraiment flexible et robuste et résout le problème. Le fait est que, lorsque j'ai implémenté cette méthode pour la première fois (en réalité, c'était trois ou quatre méthodes interdépendantes), j'ai reconnu que ce n'était pas la bonne réponse. Mais je n'en savais pas assez pour décider quelle était la bonne réponse. Donc je suis allé avec la réponse fausse la plus simple jusqu'à ce que la bonne réponse soit devenue claire.

0

J'ai posté un commentaire, mais j'ai pensé que j'élaborerais un peu plus. Qu'est-ce que je fais est de créer une bibliothèque commune avec des espaces de noms: [Organisation]. [Product] .Common comme la racine et un sous-noms helpers. Quelques personnes ici mentionnent des choses comme créer une classe et bousculer des choses qu'ils ne savent pas où mettre là-bas. Faux. Je dirais que même si vous avez besoin d'une méthode d'aide, elle est liée à quelque chose, alors créez une classe d'aide statique nommée (IoHelper, StringHelper, etc.) et placez-la dans l'espace de noms Helpers. De cette façon, vous obtenez une structure et vous obtenez une sorte de séparation des préoccupations.

Dans l'espace de noms racine, vous pouvez utiliser des classes d'utilitaire d'instance qui requièrent un état (elles existent!). Et inutile de dire aussi utiliser un nom de classe approprié, mais ne suffixez pas avec Helper.

Questions connexes