Skip to content

Commit

Permalink
Adding parameterized specification (Issue#1055) (#1088)
Browse files Browse the repository at this point in the history
* Resolution proposition to Issue#1055 (UML diagram left to do)

* Deciding not to modify the UML diagram for now
  • Loading branch information
MaVdbussche authored and iluwatar committed Nov 16, 2019
1 parent cc571f4 commit df8a4e3
Show file tree
Hide file tree
Showing 17 changed files with 328 additions and 30 deletions.
33 changes: 30 additions & 3 deletions specification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Use the Specification pattern when

Real world example

> 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.
> 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.
In Plain Words

Expand All @@ -43,14 +43,16 @@ Wikipedia says
**Programmatic Example**

If we look at our creature pool example from above, we have a set of creatures with certain properties.
If we look at our creature pool example from above, we have a set of creatures with certain properties.\
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.

```java
public interface Creature {
String getName();
Size getSize();
Movement getMovement();
Color getColor();
Mass getMass();
}
```

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

public Dragon() {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED);
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0));
}
}
```
Expand All @@ -83,13 +85,38 @@ public class MovementSelector implements Predicate<Creature> {
}
```

On the other hand, we selecting creatures heavier than a chosen amount, we use MassGreaterThanSelector.

```java
public class MassGreaterThanSelector implements Predicate<Creature> {

private final Mass mass;

public MassGreaterThanSelector(double mass) {
this.mass = new Mass(mass);
}

@Override
public boolean test(Creature t) {
return t.getMass().greaterThan(mass);
}
}
```

With these building blocks in place, we can perform a search for red and flying creatures like this.

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

But we could also use our paramterized selector like this.

```java
List<Creature> heavyCreatures = creatures.stream()
.filter(new MassGreaterThanSelector(500.0).collect(Collectors.toList());
```

## Related patterns

* Repository
Expand Down
18 changes: 9 additions & 9 deletions specification/etc/specification.ucls
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
</class>
<enumeration id="14" language="java" name="com.iluwatar.property.Color" project="specification"
file="/specification/src/main/java/com/iluwatar/property/Color.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1416" y="223"/>
Expand All @@ -126,7 +126,7 @@
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</enumeration>
</enumeration>
<generalization id="15">
<end type="SOURCE" refId="1"/>
<end type="TARGET" refId="8"/>
Expand Down Expand Up @@ -186,13 +186,13 @@
<generalization id="33">
<end type="SOURCE" refId="13"/>
<end type="TARGET" refId="8"/>
</generalization>
<association id="34">
</generalization>
<association id="34">
<end type="SOURCE" refId="5" navigable="false">
<attribute id="35" name="m"/>
<multiplicity id="36" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="12" navigable="true"/>
<attribute id="35" name="m"/>
<multiplicity id="36" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="12" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="37">
Expand All @@ -202,7 +202,7 @@
</end>
<end type="TARGET" refId="14" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
import com.iluwatar.specification.creature.Shark;
import com.iluwatar.specification.creature.Troll;
import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.selector.ColorSelector;
import com.iluwatar.specification.selector.MassGreaterThanSelector;
import com.iluwatar.specification.selector.MassSmallerThanOrEqSelector;
import com.iluwatar.specification.selector.MovementSelector;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -51,7 +54,7 @@
* <p>http://martinfowler.com/apsupp/spec.pdf</p>
*/
public class App {

private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

/**
Expand All @@ -61,6 +64,8 @@ public static void main(String[] args) {
// initialize creatures list
List<Creature> creatures = List.of(new Goblin(), new Octopus(), new Dragon(), new Shark(),
new Troll(), new KillerBee());
// so-called "hard-coded" specification
LOGGER.info("Demonstrating hard-coded specification :");
// find all walking creatures
LOGGER.info("Find all walking creatures");
List<Creature> walkingCreatures =
Expand All @@ -79,5 +84,19 @@ public static void main(String[] args) {
.filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING)))
.collect(Collectors.toList());
redAndFlyingCreatures.forEach(c -> LOGGER.info(c.toString()));
// so-called "parameterized" specification
LOGGER.info("Demonstrating parameterized specification :");
// find all creatures heavier than 500kg
LOGGER.info("Find all creatures heavier than 600kg");
List<Creature> heavyCreatures =
creatures.stream().filter(new MassGreaterThanSelector(600.0))
.collect(Collectors.toList());
heavyCreatures.forEach(c -> LOGGER.info(c.toString()));
// find all creatures heavier than 500kg
LOGGER.info("Find all creatures lighter than or weighing exactly 500kg");
List<Creature> lightCreatures =
creatures.stream().filter(new MassSmallerThanOrEqSelector(500.0))
.collect(Collectors.toList());
lightCreatures.forEach(c -> LOGGER.info(c.toString()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -36,20 +37,23 @@ public abstract class AbstractCreature implements Creature {
private Size size;
private Movement movement;
private Color color;
private Mass mass;

/**
* Constructor.
*/
public AbstractCreature(String name, Size size, Movement movement, Color color) {
public AbstractCreature(String name, Size size, Movement movement, Color color, Mass mass) {
this.name = name;
this.size = size;
this.movement = movement;
this.color = color;
this.mass = mass;
}

@Override
public String toString() {
return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color);
return String.format("%s [size=%s, movement=%s, color=%s, mass=%s]",
name, size, movement, color, mass);
}

@Override
Expand All @@ -71,4 +75,9 @@ public Movement getMovement() {
public Color getColor() {
return color;
}

@Override
public Mass getMass() {
return mass;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

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

Color getColor();

Mass getMass();
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class Dragon extends AbstractCreature {

public Dragon() {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED);
this(new Mass(39300.0));
}

public Dragon(Mass mass) {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class Goblin extends AbstractCreature {

public Goblin() {
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN);
this(new Mass(30.0));
}

public Goblin(Mass mass) {
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class KillerBee extends AbstractCreature {

public KillerBee() {
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT);
this(new Mass(6.7));
}

public KillerBee(Mass mass) {
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class Octopus extends AbstractCreature {

public Octopus() {
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK);
this(new Mass(12.0));
}

public Octopus(Mass mass) {
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class Shark extends AbstractCreature {

public Shark() {
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT);
this(new Mass(500.0));
}

public Shark(Mass mass) {
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
package com.iluwatar.specification.creature;

import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size;

Expand All @@ -33,6 +34,10 @@
public class Troll extends AbstractCreature {

public Troll() {
super("Troll", Size.LARGE, Movement.WALKING, Color.DARK);
this(new Mass(4000.0));
}

public Troll(Mass mass) {
super("Troll", Size.LARGE, Movement.WALKING, Color.DARK, mass);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
package com.iluwatar.specification.property;

/**
* <p>Color property.</p>
* Color property.
*/
public enum Color {

Expand Down

0 comments on commit df8a4e3

Please sign in to comment.