You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/08/28 12:07:34 UTC

[groovy] branch master updated: doco: lambda version of the template method pattern

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 4fbdc5a  doco: lambda version of the template method pattern
4fbdc5a is described below

commit 4fbdc5a3db6e2db26a3ac8b2429f157ba8e29c0e
Author: Paul King <pa...@asert.com.au>
AuthorDate: Fri Aug 28 22:07:14 2020 +1000

    doco: lambda version of the template method pattern
---
 .../fragment_design-pattern-template-method.adoc   | 38 +++++++++++++---------
 src/spec/test/DesignPatternsTest.groovy            | 26 ++++++++++-----
 2 files changed, 39 insertions(+), 25 deletions(-)

diff --git a/src/spec/doc/fragment_design-pattern-template-method.adoc b/src/spec/doc/fragment_design-pattern-template-method.adoc
index 88a0d9c..7198ce6 100644
--- a/src/spec/doc/fragment_design-pattern-template-method.adoc
+++ b/src/spec/doc/fragment_design-pattern-template-method.adoc
@@ -22,7 +22,8 @@
 = Template Method Pattern
 
 
-The http://en.wikipedia.org/wiki/Template_method_pattern[Template Method Pattern] abstracts away the details of several algorithms.
+The https://en.wikipedia.org/wiki/Template_method_pattern[Template Method Pattern] abstracts
+away the details of several algorithms.
 The generic part of an algorithm is contained within a base class.
 Particular implementation details are captured within base classes.
 The generic pattern of classes involved looks like this:
@@ -33,8 +34,8 @@ skinparam nodesep 100
 class AbstractClass {
     +algorithm1()
     +algorithm2()
-    +primitiveOperationA()
-    +primitiveOperationB()
+    {abstract} +primitiveOperationA()
+    {abstract} +primitiveOperationB()
 }
 class ConcreteClass1 {
     +primitiveOperationA()
@@ -51,21 +52,17 @@ AbstractClass <|-- ConcreteClass1
 AbstractClass <|-- ConcreteClass2
 ....
 
-== Example
+== Example with traditional classes
 
-In this example, `Accumulator` captures the essence of the accumulation algorithm. The base classes `Sum` and `Product` provide particular customised ways to use the generic accumulation algorithm.
+In this example, the base `Accumulator` class captures the essence of the accumulation algorithm.
+The concrete classes `Sum` and `Product` provide particular customised ways to use the generic accumulation algorithm.
 
 [source,groovy]
 ----
 include::../test/DesignPatternsTest.groovy[tags=template_method_example,indent=0]
 ----
 
-The resulting output is:
-
-----
-10
-24
-----
+== Example with simplifying strategies
 
 In this particular case, you could use Groovy's inject method to achieve a similar result using Closures:
 
@@ -74,20 +71,29 @@ In this particular case, you could use Groovy's inject method to achieve a simil
 include::../test/DesignPatternsTest.groovy[tags=template_method_example2,indent=0]
 ----
 
-Thanks to duck-typing, this would also work with other objects which support an add (plus() in Groovy) method, e.g.:
-
-In this particular case, you could use Groovy's inject method to achieve a similar result using Closures:
+Thanks to duck-typing, this would also work with other objects which support an add (`plus()` in Groovy) method, e.g.:
 
 [source,groovy]
 ----
 include::../test/DesignPatternsTest.groovy[tags=template_method_example3,indent=0]
 ----
 
-We could also do the multiplication case as follows:
+We could also do the multiplication case as follows (re-writing as a one-liner):
 
 [source,groovy]
 ----
 include::../test/DesignPatternsTest.groovy[tags=template_method_example4,indent=0]
 ----
+Using closures this way looks like the <<_strategy_pattern,Strategy Pattern>>, but if we realise
+that Groovy's `inject` method is the generic part of the algorithm for our template method,
+then the Closures become the customised parts of the template method pattern.
+
+For Groovy 3+, we can use lambda syntax as an alternative to the closure syntax:
+
+[source,groovy]
+----
+include::../test/DesignPatternsTest.groovy[tags=template_method_example5,indent=0]
+----
 
-Using closures this way looks more like the <<_strategy_pattern,Strategy Pattern>> but if we realise that the built-in ++inject++ method is the generic part of the algorithm for our template method, then the Closures become the customised parts of the template method pattern.
\ No newline at end of file
+Here the stream api's `reduce` method is the generic part of the algorithm for our template method,
+and the lambdas are the customised parts of the template method pattern.
diff --git a/src/spec/test/DesignPatternsTest.groovy b/src/spec/test/DesignPatternsTest.groovy
index a0b5df3..fe4d62e 100644
--- a/src/spec/test/DesignPatternsTest.groovy
+++ b/src/spec/test/DesignPatternsTest.groovy
@@ -2043,7 +2043,7 @@ class DesignPatternsTest extends CompilableTestSupport {
     }
 
     void testTemplateMethod() {
-        shouldCompile '''
+        assertScript '''
             // tag::template_method_example[]
             abstract class Accumulator {
                 protected initial
@@ -2065,33 +2065,41 @@ class DesignPatternsTest extends CompilableTestSupport {
                 def doAccumulate(total, v) { total * v }
             }
 
-            println new Sum().accumulate([1,2,3,4])
-            println new Product().accumulate([1,2,3,4])
+            assert 10 == new Sum().accumulate([1,2,3,4])
+            assert 24 == new Product().accumulate([1,2,3,4])
             // end::template_method_example[]
         '''
     }
 
     void testTemplateMethod2() {
-        shouldCompile '''
+        assertScript '''
             // tag::template_method_example2[]
             Closure addAll = { total, item -> total += item }
             def accumulated = [1, 2, 3, 4].inject(0, addAll)
-            println accumulated    // => 10
+            assert accumulated == 10
             // end::template_method_example2[]
 
             // tag::template_method_example3[]
             accumulated = [ "1", "2", "3", "4" ].inject("", addAll)
-            println accumulated    // => "1234"
+            assert accumulated == "1234"
             // end::template_method_example3[]
 
             // tag::template_method_example4[]
-            Closure multAll = { total, item -> total *= item }
-            accumulated = [1, 2, 3, 4].inject(1, multAll)
-            println accumulated    // => 24
+            assert 24 == [1, 2, 3, 4].inject(1) { total, item -> total *= item }
             // end::template_method_example4[]
         '''
     }
 
+    void testTemplateMethod3() {
+        assertScript '''
+            // tag::template_method_example5[]
+            assert 10 == [1, 2, 3, 4].stream().reduce(0, (l, r) -> l + r)
+            assert 24 == [1, 2, 3, 4].stream().reduce(1, (l, r) -> l * r)
+            assert '1234' == ['1', '2', '3', '4'].stream().reduce('', (l, r) -> l + r)
+            // end::template_method_example5[]
+        '''
+    }
+
     void testVisitorSimpleExample() {
         shouldCompile '''
             // tag::visitor_simple_example[]