web-dev-qa-db-fra.com

Fermetures: Pourquoi sont-ils si utiles?

Comme OO Développeur, j'ai peut-être de la difficulté à voir sa valeur. Quelle valeur ajoutée donne-t-il? Est-ce qu'ils correspondent à un OO World?

55
koen

Les fermetures ne vous donnent aucun pouvoir supplémentaire.

Tout ce que vous pouvez réaliser avec eux, vous pouvez obtenir sans eux.

Mais ils sont très utilisables pour rendre le code plus clair et lisible. Et comme nous savons tous que Clean Lisible Code Code est un code qui est plus facile à déboguer et contient moins de bugs.

Laissez-moi vous donner courte Java Exemple d'utilisation possible:

    button.addActionListener(new ActionListener() {
        @Override public void actionPerformed(ActionEvent e) {
            System.out.println("Pressed");
        }
    });

Serait remplacé (si Java avait des fermetures) avec:

button.addActionListener( { System.out.println("Pressed"); } );
29
Gregory Mostizky

Vous pouvez le voir comme généralisation d'une classe.

Votre classe tient un état. Il a des variables de membres que ses méthodes peuvent utiliser.

Une fermeture est simplement un moyen plus pratique de donner une fonction d'accès à l'état local.

Plutôt que de devoir créer une classe qui connaît la variable locale que vous souhaitez que la fonction utilise, vous pouvez simplement définir la fonction sur place, et elle peut implicitement accéder à chaque variable actuellement visible.

Lorsque vous définissez une méthode de membre dans une traditionnelle OOP Langue, sa fermeture est "Tous les membres visibles dans cette classe".

Les langues avec le support de fermeture "appropriée" généralisent simplement ceci, la fermeture d'une fonction est donc "toutes les variables visibles ici". Si "ICI" est une classe, vous avez une méthode de classe traditionnelle.

Si "ICI" est à l'intérieur d'une autre fonction, vous avez des programmeurs fonctionnels comme une fermeture. Votre fonction peut maintenant accéder à tout ce qui était visible dans la fonction mère.

Il s'agit donc simplement d'une généralisation, en supprimant la restriction idiote selon laquelle "les fonctions ne peuvent être définies qu'à l'intérieur de classes", mais en gardant l'idée que "les fonctions peuvent voir toutes les variables sont visibles au point où elles sont déclarées".

67
jalf

Pour moi, le plus grand avantage des fermetures est lorsque vous écrivez le code qui commence une tâche, laisse la tâche à exécuter et spécifie ce qui devrait se passer lorsque la tâche est effectuée. Généralement, le code qui fonctionne à la fin de la tâche a besoin d'un accès aux données disponibles au début et les fermetures facilitent cela.

Par exemple, une utilisation courante dans JavaScript consiste à démarrer une demande HTTP. Quiconque commence il veut probablement contrôler ce qui se passe lorsque la réponse arrive. Donc, vous ferez quelque chose comme ça:

function sendRequest() {
  var requestID = "123";
  Ext.Ajax.request({
    url:'/myUrl'
    success: function(response) {
      alert("Request " + requestID + " returned");
    }
  });
}

En raison de la fermeture de JavaScript, la variable "demandes" est capturée à l'intérieur de la fonction de réussite. Cela montre comment vous pouvez écrire les fonctions de demande et de réponse au même endroit et partagez des variables entre elles. Sans fermeture, vous auriez besoin de passer à DIREQUID comme argument ou de créer un objet contenant des demandes demandeur et la fonction.

28
JW.

IMHO cela revient à pouvoir capturer des blocs de code --et leur contexte à référencer à un moment donné plus tard et exécuté lorsque/si/si nécessaire.

Ils ne sembleraient peut-être pas une grosse affaire et des fermetures ne sont certainement pas quelque chose dont vous avez besoin pour faire effectuer quotidiennement - mais elles peuvent faire Code de code beaucoup plus simple et nettoyant pour écrire/gérer.

[Édition - Exemple de code basé sur le commentaire ci-dessus]

Java:

List<Integer> numbers = ...;
List<Integer> positives = new LinkedList<Integer>();
for (Integer number : integers) {
    if (number >= 0) {
        positives.add(number);
    }
}

Scala (sautant certaines des autres Néanties comme l'inférence de type et des caractères génériques, nous comparons donc uniquement l'effet de la fermeture):

val numbers:List[Int] = ...
val positives:List[Int] = numbers.filter(i:Int => i >= 0)
5
Martin

C'est une pitié, les gens n'apprennent plus Smalltalk à Edu; Là, les fermetures sont utilisées pour les structures de contrôle, les rappels, l'énumération de la collecte, la manipulation des exceptions et plus encore. Pour un joli petit exemple, voici un fil de travail d'action d'une file d'attente de travail (en smalltalk):

|actionQueue|

actionQueue := SharedQueue new.
[
    [
        |a|

        a := actionQueue next.
        a value.
    ] loop
] fork.

actionQueue add: [ Stdout show: 1000 factorial ].

et, pour ceux qui ne peuvent pas lire SmallTalk, la même chose dans la syntaxe JavaScript:

var actionQueue;

actionQueue = new SharedQueue;
function () {
    for (;;) {
        var a;

        a = actionQueue.next();
        a();
    };
}.fork();

actionQueue.add( function () { Stdout.show( 1000.factorial()); });

(Eh bien, comme vous le voyez: Syntaxe aide à lire le code)

EDIT: Remarquez comment ActionQueue est référencé de l'intérieur des blocs, qui fonctionne même pour le bloc-fil fourche. C'est ce qui rend les fermetures si faciles à utiliser.

4
blabla999

Voici quelques articles intéressants:

3
Jesper

Les fermetures s'intègrent assez bien dans un OO World.

À titre d'exemple, considérez le C # 3.0: il a des fermetures et de nombreux autres aspects fonctionnels, mais reste une langue très orientée objet.

Dans mon expérience, les aspects fonctionnels de C # ont tendance à rester dans la mise en œuvre des membres de la classe, et pas autant que partie de l'API publique mes objets se terminer exposer.

En tant que tel, l'utilisation de fermetures a tendance à être des détails sur la mise en œuvre dans un code sinon orienté objet.

Je les utilise tout le temps, comme cet extrait de code de l'un de nos tests de l'unité (contre MOQ ) montre:

var typeName = configuration.GetType().AssemblyQualifiedName;

var activationServiceMock = new Mock<ActivationService>();
activationServiceMock.Setup(s => s.CreateInstance<SerializableConfigurationSection>(typeName)).Returns(configuration).Verifiable();

Il aurait été assez difficile de spécifier la valeur d'entrée (typename) dans le cadre de la fusion d'attentes si elle n'avait pas été pour la fonction de fermeture de C #.

3
Mark Seemann

En ce qui concerne la requête finale: "Les fermetures (fermetures) correspondent à un OO World?"

Dans de nombreuses langues, des fermetures, ainsi que des fonctions de première classe, fournissent la base même de concevoir OO Programmes: JavaScript, Lua et Perl vous viennent à l'esprit.

Dans un autre plus "traditionnel" OO Langue avec laquelle je suis familier, Java, il y a 2 différences principales:

  1. Les fonctions sont pas constructions de première classe
  2. In Java Les détails de la mise en œuvre de OO (si des fermetures sont utilisées en interne) n'est pas exposée directement au développeur, car elle est en JavaScript, Lua et Perl

Par conséquent, dans une langue "OO traditionnelle", telle que Java, l'ajout de fermetures est en grande partie exactement autant de sucre syntaxique.

0
George Jempty