web-dev-qa-db-fra.com

Remplacement des méthodes LINQ avec des méthodes d'extension

Donc, je suis tombé dans le piège à FAD et j'ai commencé à remplacer une grande quantité de requêtes LINQ avec des méthodes d'extension.

Par exemple:

orders.Where(o => o.Status == ShippedStatus.Shipped).Select(o => o.ID)

est devenu :

orders.ShippedOrderIds

Les méthodes d'extension sont statiques avec tous les inconvénients impliqués et je crois que le "plus correct" OO moyen de gérer ce type de refacteur serait de le compromettre dans un objet "ordres" et d'exposer la propriété (ies) qui font cela à la place.

Quelques questions:

  1. Y a-t-il une troisième alternative (ou plus) qui a plus de sens que l'une ou l'autre de ces approches?
  2. Combien pire est l'approche d'extension que le "vrai" OO approche?

Clarification rapide des contextes de refactorisation - Aucun de ces refacteurs ne fonctionne sur des objets uniques, juste des collections d'objets.

6
JohnsonCore

Les méthodes d'extension vous offrent une opportunité de raisonner sur les problèmes une manière totalement différente fonctionnellement . La programmation fonctionnelle est un paradigme totalement différent de l'objet de l'objet et présente certains avantages. Par exemple, lorsque vous utilisez la pensée fonctionnelle et les variables immuables, votre programme devient instantanément du thread-Safe sans utiliser de serrures ni de mécanismes de concurrence. De plus, lorsque vous écrivez Fonctions pure , le test de l'unité est un coup d'œil et vous n'avez pas besoin de moqueurs ou de talons du tout. De plus, les méthodes d'extension vous permettent de mettre des méthodes sur les interfaces-- Aucune classe requise du tout ... et il vous suffit de les implémenter une fois et il fonctionnera sur n'importe quelle classe qui implémente l'interface, même si elles ne partagent pas de base classer.

Voici les règles pour que votre code fonctionne bien à cette mode:

  1. Écrivez vos méthodes d'extension afin qu'elles soient pur . Ils ne doivent pas accéder à aucun état en dehors des entrées de méthode et ne doivent modifier aucun État.

  2. Écrivez vos méthodes d'extension afin qu'ils renvoient toujours une nouvelle instance plutôt que de modifier l'instance qui lui a été remise.

  3. Écrivez-les génériquement lorsque cela est possible; Cela permet une chaîne de méthode de type-sécurité qui peut transformer des types.

  4. Si vous avez besoin de méthodes pour avoir des dépendances, injecter des paramètres, par exemple Where accepte un délégué qui permet à l'appelant de "injecter" un moteur logique (exposé comme un Func<T,bool>) qui détermine si une ligne sera incluse.

Au fait, toutes les méthodes LINQ sont conformes à ces règles.

Donc, par exemple, si vous avez plusieurs types de classes ayant un statut "expédié", vous pouvez implémenter ceci:

interface IShippable
{
    ShippedStatus ShippedStatus { get; }
    int Id;
}


static public IEnumerable<T> ShippedOrderIds<T>(this IEnumerable<T> source) where T : IShippable
{
    return source.Where( s => s.ShippedStatus == ShippedState.Shipped ).Select( s => s.Id );
}

Cela fonctionnera avec n'importe quel article expédié sans nécessiter de dénigrement ni de sous-emploi; C'est pur, vous n'avez donc pas besoin d'injecter des dépendances et il n'y a rien à moquer ni à isoler; Et cela permettra une méthode de sécurité enchaînant si vous écrivez d'autres méthodes d'extension.

Ce n'est pas un mauvais moyen d'aller du tout. La seule façon dont cela pourrait être mauvais est si vous pensez que vous écrivez du code orienté objet. Vous n'êtes pas. Vous écrivez le code de fonctionnement et cela peut être une bonne chose.

9
John Wu

Les méthodes d'extension brillent vraiment lorsque vous n'avez pas accès à la classe étant étendue - par exemple, des classes de cadre comme int ou string. Il est impossible de les ajouter à la classe. Vous devez donc écrire une méthode static et une méthode d'extension est plus facile à lire. Si vous avez accès à la classe Orders, vous devez ajouter des méthodes ou des propriétés dans cette classe.

Si l'objet orders est une instance de IEnumerable<Order> (où Order est votre propre classe), alors quelques méthodes d'extension sur IEnumerable<Order> ne sont pas un problème, par rapport à la création d'un droit Orders classe (qui pourrait très bien implémenter IEnumerable). Si vous allez écrire plusieurs de ces méthodes d'extension, vous devriez plutôt les ajouter à une classe O Orders. Je prendrais un pas en arrière et examinez vraiment la quantité d'amélioration que cela vous obtient vraiment.

Si vous continuez avec cette refactoring, déterminez si l'une de ces méthodes peut être combinée - par exemple, orders.GetOrderIdsByStatus(ShippedStatus status), plutôt que d'avoir séparé orders.ShippedOrderIds, orders.CancelledOrderIds, orders.ProcessingOrderIds, etc.

2
mmathis
  1. LINQ est très lisible et normalisé. D'autres personnes sauront exactement ce que chaque fonction fait. Les méthodes d'extension sont personnalisées, vous devez donc ajouter une complexité supplémentaire.
  2. Les méthodes d'extension doivent être utilisées pour ce qu'ils ont été conçus - extension de choses que vous n'avez pas accès à. Ils n'ont pas beaucoup d'avantage en dehors de cela.
1
Petras Purlys