Skip to content

Commit df8a4e3

Browse files
MaVdbusscheiluwatar
authored andcommitted
Adding parameterized specification (Issue#1055) (iluwatar#1088)
* Resolution proposition to Issue#1055 (UML diagram left to do) * Deciding not to modify the UML diagram for now
1 parent cc571f4 commit df8a4e3

17 files changed

Lines changed: 328 additions & 30 deletions

File tree

specification/README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Use the Specification pattern when
3131

3232
Real world example
3333

34-
> There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" and give it to the party that will perform the filtering.
34+
> There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" or "creatures heavier than 500 kilograms" and give it to the party that will perform the filtering.
3535
3636
In Plain Words
3737

@@ -43,14 +43,16 @@ Wikipedia says
4343
4444
**Programmatic Example**
4545

46-
If we look at our creature pool example from above, we have a set of creatures with certain properties.
46+
If we look at our creature pool example from above, we have a set of creatures with certain properties.\
47+
Those properties can be part of a pre-defined, limited set (represented here by the enums Size, Movement and Color); but they can also be discrete (e.g. the mass of a Creature). In this case, it is more appropriate to use what we call "parameterized specification", where the property value can be given as an argument when the Creature is created, allowing for more flexibility.
4748

4849
```java
4950
public interface Creature {
5051
String getName();
5152
Size getSize();
5253
Movement getMovement();
5354
Color getColor();
55+
Mass getMass();
5456
}
5557
```
5658

@@ -60,7 +62,7 @@ And dragon implementation looks like this.
6062
public class Dragon extends AbstractCreature {
6163

6264
public Dragon() {
63-
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED);
65+
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0));
6466
}
6567
}
6668
```
@@ -83,13 +85,38 @@ public class MovementSelector implements Predicate<Creature> {
8385
}
8486
```
8587

88+
On the other hand, we selecting creatures heavier than a chosen amount, we use MassGreaterThanSelector.
89+
90+
```java
91+
public class MassGreaterThanSelector implements Predicate<Creature> {
92+
93+
private final Mass mass;
94+
95+
public MassGreaterThanSelector(double mass) {
96+
this.mass = new Mass(mass);
97+
}
98+
99+
@Override
100+
public boolean test(Creature t) {
101+
return t.getMass().greaterThan(mass);
102+
}
103+
}
104+
```
105+
86106
With these building blocks in place, we can perform a search for red and flying creatures like this.
87107

88108
```java
89109
List<Creature> redAndFlyingCreatures = creatures.stream()
90110
.filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList());
91111
```
92112

113+
But we could also use our paramterized selector like this.
114+
115+
```java
116+
List<Creature> heavyCreatures = creatures.stream()
117+
.filter(new MassGreaterThanSelector(500.0).collect(Collectors.toList());
118+
```
119+
93120
## Related patterns
94121

95122
* Repository

specification/etc/specification.ucls

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<attributes public="true" package="true" protected="true" private="true" static="true"/>
118118
<operations public="true" package="true" protected="true" private="true" static="true"/>
119119
</display>
120-
</class>
120+
</class>
121121
<enumeration id="14" language="java" name="com.iluwatar.property.Color" project="specification"
122122
file="/specification/src/main/java/com/iluwatar/property/Color.java" binary="false" corner="BOTTOM_RIGHT">
123123
<position height="-1" width="-1" x="1416" y="223"/>
@@ -126,7 +126,7 @@
126126
<attributes public="true" package="true" protected="true" private="true" static="true"/>
127127
<operations public="true" package="true" protected="true" private="true" static="true"/>
128128
</display>
129-
</enumeration>
129+
</enumeration>
130130
<generalization id="15">
131131
<end type="SOURCE" refId="1"/>
132132
<end type="TARGET" refId="8"/>
@@ -186,13 +186,13 @@
186186
<generalization id="33">
187187
<end type="SOURCE" refId="13"/>
188188
<end type="TARGET" refId="8"/>
189-
</generalization>
190-
<association id="34">
189+
</generalization>
190+
<association id="34">
191191
<end type="SOURCE" refId="5" navigable="false">
192-
<attribute id="35" name="m"/>
193-
<multiplicity id="36" minimum="0" maximum="1"/>
194-
</end>
195-
<end type="TARGET" refId="12" navigable="true"/>
192+
<attribute id="35" name="m"/>
193+
<multiplicity id="36" minimum="0" maximum="1"/>
194+
</end>
195+
<end type="TARGET" refId="12" navigable="true"/>
196196
<display labels="true" multiplicity="true"/>
197197
</association>
198198
<association id="37">
@@ -202,7 +202,7 @@
202202
</end>
203203
<end type="TARGET" refId="14" navigable="true"/>
204204
<display labels="true" multiplicity="true"/>
205-
</association>
205+
</association>
206206
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
207207
sort-features="false" accessors="true" visibility="true">
208208
<attributes public="true" package="true" protected="true" private="true" static="true"/>

specification/src/main/java/com/iluwatar/specification/app/App.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@
3131
import com.iluwatar.specification.creature.Shark;
3232
import com.iluwatar.specification.creature.Troll;
3333
import com.iluwatar.specification.property.Color;
34+
import com.iluwatar.specification.property.Mass;
3435
import com.iluwatar.specification.property.Movement;
3536
import com.iluwatar.specification.selector.ColorSelector;
37+
import com.iluwatar.specification.selector.MassGreaterThanSelector;
38+
import com.iluwatar.specification.selector.MassSmallerThanOrEqSelector;
3639
import com.iluwatar.specification.selector.MovementSelector;
3740
import java.util.List;
3841
import java.util.stream.Collectors;
@@ -51,7 +54,7 @@
5154
* <p>http://martinfowler.com/apsupp/spec.pdf</p>
5255
*/
5356
public class App {
54-
57+
5558
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
5659

5760
/**
@@ -61,6 +64,8 @@ public static void main(String[] args) {
6164
// initialize creatures list
6265
List<Creature> creatures = List.of(new Goblin(), new Octopus(), new Dragon(), new Shark(),
6366
new Troll(), new KillerBee());
67+
// so-called "hard-coded" specification
68+
LOGGER.info("Demonstrating hard-coded specification :");
6469
// find all walking creatures
6570
LOGGER.info("Find all walking creatures");
6671
List<Creature> walkingCreatures =
@@ -79,5 +84,19 @@ public static void main(String[] args) {
7984
.filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING)))
8085
.collect(Collectors.toList());
8186
redAndFlyingCreatures.forEach(c -> LOGGER.info(c.toString()));
87+
// so-called "parameterized" specification
88+
LOGGER.info("Demonstrating parameterized specification :");
89+
// find all creatures heavier than 500kg
90+
LOGGER.info("Find all creatures heavier than 600kg");
91+
List<Creature> heavyCreatures =
92+
creatures.stream().filter(new MassGreaterThanSelector(600.0))
93+
.collect(Collectors.toList());
94+
heavyCreatures.forEach(c -> LOGGER.info(c.toString()));
95+
// find all creatures heavier than 500kg
96+
LOGGER.info("Find all creatures lighter than or weighing exactly 500kg");
97+
List<Creature> lightCreatures =
98+
creatures.stream().filter(new MassSmallerThanOrEqSelector(500.0))
99+
.collect(Collectors.toList());
100+
lightCreatures.forEach(c -> LOGGER.info(c.toString()));
82101
}
83102
}

specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -36,20 +37,23 @@ public abstract class AbstractCreature implements Creature {
3637
private Size size;
3738
private Movement movement;
3839
private Color color;
40+
private Mass mass;
3941

4042
/**
4143
* Constructor.
4244
*/
43-
public AbstractCreature(String name, Size size, Movement movement, Color color) {
45+
public AbstractCreature(String name, Size size, Movement movement, Color color, Mass mass) {
4446
this.name = name;
4547
this.size = size;
4648
this.movement = movement;
4749
this.color = color;
50+
this.mass = mass;
4851
}
4952

5053
@Override
5154
public String toString() {
52-
return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color);
55+
return String.format("%s [size=%s, movement=%s, color=%s, mass=%s]",
56+
name, size, movement, color, mass);
5357
}
5458

5559
@Override
@@ -71,4 +75,9 @@ public Movement getMovement() {
7175
public Color getColor() {
7276
return color;
7377
}
78+
79+
@Override
80+
public Mass getMass() {
81+
return mass;
82+
}
7483
}

specification/src/main/java/com/iluwatar/specification/creature/Creature.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -39,4 +40,6 @@ public interface Creature {
3940
Movement getMovement();
4041

4142
Color getColor();
43+
44+
Mass getMass();
4245
}

specification/src/main/java/com/iluwatar/specification/creature/Dragon.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -33,6 +34,10 @@
3334
public class Dragon extends AbstractCreature {
3435

3536
public Dragon() {
36-
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED);
37+
this(new Mass(39300.0));
38+
}
39+
40+
public Dragon(Mass mass) {
41+
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, mass);
3742
}
3843
}

specification/src/main/java/com/iluwatar/specification/creature/Goblin.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -33,6 +34,10 @@
3334
public class Goblin extends AbstractCreature {
3435

3536
public Goblin() {
36-
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN);
37+
this(new Mass(30.0));
38+
}
39+
40+
public Goblin(Mass mass) {
41+
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, mass);
3742
}
3843
}

specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -33,6 +34,10 @@
3334
public class KillerBee extends AbstractCreature {
3435

3536
public KillerBee() {
36-
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT);
37+
this(new Mass(6.7));
38+
}
39+
40+
public KillerBee(Mass mass) {
41+
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, mass);
3742
}
3843
}

specification/src/main/java/com/iluwatar/specification/creature/Octopus.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -33,6 +34,10 @@
3334
public class Octopus extends AbstractCreature {
3435

3536
public Octopus() {
36-
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK);
37+
this(new Mass(12.0));
38+
}
39+
40+
public Octopus(Mass mass) {
41+
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, mass);
3742
}
3843
}

specification/src/main/java/com/iluwatar/specification/creature/Shark.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
package com.iluwatar.specification.creature;
2525

2626
import com.iluwatar.specification.property.Color;
27+
import com.iluwatar.specification.property.Mass;
2728
import com.iluwatar.specification.property.Movement;
2829
import com.iluwatar.specification.property.Size;
2930

@@ -33,6 +34,10 @@
3334
public class Shark extends AbstractCreature {
3435

3536
public Shark() {
36-
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT);
37+
this(new Mass(500.0));
38+
}
39+
40+
public Shark(Mass mass) {
41+
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, mass);
3742
}
3843
}

0 commit comments

Comments
 (0)