Bonnes utilisations pour Apache CollectionUtils

J’ai trouvé la classe CollectionUtils il y a environ un an et certaines des méthodes comme collecter et transformer semblent vraiment géniales, mais je n’ai pas encore trouvé d’utilisation où il ne serait pas syntaxiquement plus propre et / ou plus simple d’écrire la logique avec une simple boucle.

A-t-on trouvé une utilisation unique \ utile pour ces méthodes (transform, predicatedCollection, collect, etc.), par exemple les méthodes qui prennent un transformateur ou un prédicat comme argument?

Je pense que la question clé est la conception / la codification de la flexibilité.

Si vous avez un cas d’utilisation unique (par exemple, en sélectionnant les membres d’une collection qui satisfont à une condition spécifique), codez ensuite une boucle à la main relativement simple. D’autre part…

Supposons que l’ensemble des conditions possibles devienne important ou pourrait même être composé à la volée au moment de l’exécution (même de manière dynamic, en fonction des entrées / interactions de l’utilisateur). Ou supposons qu’il y ait quelques conditions très complexes qui pourraient être composées avec des opérateurs (par exemple, A et B, C et non D, etc.) dans des cas encore plus nombreux.

Supposons que, ayant fait la sélection, il y ait eu un autre traitement à effectuer sur la collection résultante.

Considérons maintenant la structure du code qui pourrait résulter d’une approche en ligne brute de l’écriture de ce qui précède: une boucle externe contenant un processus de décision complexe pour déterminer le ou les tests à effectuer, mélangés avec le code qui en fait un. ou plusieurs choses avec les membres “survivants” de la collection. Un tel code a tendance à être (et surtout à devenir avec le temps avec maintenance) difficile à comprendre et difficile à modifier sans risquer d’introduire des défauts.

Il s’agit donc de poursuivre une stratégie dans laquelle chaque aspect:

  • processus de base “sélectionner quelque chose”,
  • des prédicats exprimant des critères élémentaires,
  • combiner des opérateurs qui composent des prédicats, et
  • transformateurs fonctionnant sur des valeurs,

peut être codé et testé indépendamment, puis assemblé au besoin.

collect () est utile lorsque vous avez d’autres représentations possibles de vos objects.

Par exemple, récemment, je travaillais avec un morceau de code qui devait correspondre à des listes d’objects provenant de deux sources différentes. Ces objects étaient de classes différentes car ils étaient utilisés à différents points du code, mais pour mes besoins, ils avaient les mêmes concepts pertinents (c.-à-d. Qu’ils possédaient tous deux un identifiant, tous deux possédaient un indicateur de cascade, etc.) .

J’ai trouvé qu’il était beaucoup plus facile de définir une représentation intermédiaire simple de ces propriétés (en tant que classe interne), de définir des transformateurs pour les deux classes d’objects concrètes (encore une fois très simple car il utilise des méthodes d’accesseur pertinentes). utilisez collect() pour convertir mes objects entrants dans la représentation intermédiaire. Une fois qu’ils sont là, je peux utiliser les méthodes de collections standard pour comparer et manipuler les deux en tant qu’ensembles.

En tant qu’exemple (semi-) concret, disons que j’ai besoin d’une méthode pour vérifier que l’ensemble d’objects de la couche de présentation est un sous-ensemble des objects mis en cache dans la couche de données. Avec l’approche décrite ci-dessus, cela se ferait comme ceci:

 public boolean isColumnSubset(PresSpec pres, CachedDataSpec dataSpec) { final List presObjects = CollectionUtils.collect(pres.getObjects(), PRES_TRANSFORMER); final List dataObjects = CollectionUtils.collect(dataSpec.getCached(), DATA_TRANSFORMER); return dataObjects.containsAll(presObjects); } 

Pour moi, cela est beaucoup plus lisible, la dernière ligne transmettant un réel sens de la méthode, à l’équivalent des boucles:

 public boolean isColumnSubset(PresSpec pres, CachedDataSpec dataSpec) { for (PresSpecificObject presObj : pres.getObjects()) { boolean matched = false; for (CachedDataObject dataObj : dataSpec.getCached()) { if (areObjectsEquivalent(presObj, dataObj)) // or do the tests inline but a method is cleaner { matched = true; break; } } if (matched == false) { return false; } } // Every column must have matched return true; } 

Les deux sont probablement à peu près aussi efficaces, mais en termes de lisibilité, je dirais que le premier est beaucoup plus facile à comprendre immédiatement. Même s’il s’agit de plus de lignes de code globales (en raison de la définition d’une classe interne et de deux transformateurs), la séparation de l’implémentation de traversée de la logique “vrai ou faux” rend cette dernière plus claire. De plus, si vous avez des mésortingques KLOC, il ne peut pas non plus être perceptible. 😉

Bien que je sois d’accord avec Uri en principe, sans fermetures ni littéraux de fonctions, peu importe, Java impose un coût syntaxique assez élevé pour utiliser des méthodes telles que collecter, transformer, etc. Dans de nombreux cas, les lignes de code réelles sont identiques que si vous aviez écrit la boucle simple. addAll, removeAll et tous leurs amis qui ne prennent pas d’objects de fonction comme arguments sont indispensables.

Tout cela s’applique également à la même bonne API Google Collections .

Il est regrettable que Sun ait le pouvoir de résoudre ces problèmes dans Java 7, mais il semblerait que ce ne soit pas le cas . Lâches.

Je ne suis pas sûr d’être d’accord avec votre déclaration …

Une simple boucle ajoute de la complexité et est moins «lisible et évidente» qu’un appel avec un nom judicieux.

Les évangélistes du refactoring affirment que votre objective devrait généralement être de créer des fonctions plates et courtes appelant d’autres opérations. Bien que addAll, find et ces méthodes soient faciles à implémenter, les éviter obligerait le lecteur à saisir quelque chose de plus complexe qu’un seul mot et à provoquer une réplication du code.

IMHO, CollectionUtils présente en fait des opérations plus propres que la bibliothèque de collections Java standard.

Bien que nous n’utilisions pas les CollctionUtils, nous avons implémenté quelques utilitaires similaires parmi ceux que nous utilisons fréquemment.

vide (Collection c)

pour tester des collections, des chaînes et autres pour le vide

a(…)

ça ne revient que! vide (…)

mapToProperty (Collection c, propriété Ssortingng, classe newType)

cela mappe une collection de T1 à une collection de T2 en utilisant la reflection pour appeler “propriété”

imploser (Collection c, Ssortingng sep)

Une ficelle séparée avec les éléments de c