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[]