Introduction & Test
I would like to create an object which associates employees to X/Y coordinates. Multiple employees can be associated to each coordinate, but each employee can only be associated with one coordinate.
Also critically: employees must be able to have identical externally-configurable attributes, for example, three employees with the name "Mary".
Here's my test (AssociatorTest.java
) that I would like to implement a solution to:
package com.example;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import static junit.framework.TestCase.*;
public class AssociatorTest {
final Employee marySmith = new Employee("Mary");
final Employee maryJohnson = new Employee("Mary");
final Employee maryWilliams = new Employee("Mary");
final Employee maryJones = new Employee("Mary");
final Coordinates coords1 = new Coordinates(1,2);
final Coordinates coords2 = new Coordinates(3,4);
@Test
public void basicAssociatorValidAssociations() throws EmployeeInTwoSpacesError {
// valid associations
Associator associator = new Associator()
.associate(marySmith, coords1)
.associate(maryJohnson, coords2)
.associate(maryWilliams, coords2);
// lookup coords per employee
assertEquals(coords1, associator.getLocation(marySmith).get());
assertEquals(coords2, associator.getLocation(maryJohnson).get());
assertEquals(coords2, associator.getLocation(maryWilliams).get());
// Unaffiliated employee has no location
assertTrue(associator.getLocation(maryJones).isEmpty());
// lookup employees at coords
assertEquals(ImmutableSet.of(marySmith), associator.getEmployees(coords1));
assertEquals(ImmutableSet.of(maryJohnson, maryWilliams), associator.getEmployees(coords2));
assertEquals(ImmutableSet.of(), associator.getEmployees(new Coordinates(5,6)));
}
@Test (expected = EmployeeInTwoSpacesError.class)
public void tryToAssociateAnEmployeeWithTwoCoords() throws EmployeeInTwoSpacesError {
// Employees shouldn't be able to exist in two places at the same time
new Associator()
.associate(marySmith, coords1)
.associate(marySmith, coords2);
}
}
Implementation
My solution depends on using Guava
Here's my Associator.java
:
package com.example;
import com.google.common.collect.*;
import java.util.*;
public class Associator {
private final ImmutableSetMultimap<Coordinates, Employee> associations;
public Associator() {
associations = ImmutableSetMultimap.of();
}
private Associator(final Multimap<Coordinates, Employee> associations) {
this.associations = ImmutableSetMultimap.copyOf(associations);
}
public Associator associate(final Employee employee, final Coordinates coordinate)
throws EmployeeInTwoSpacesError {
if (associations.containsValue(employee)) {
throw new EmployeeInTwoSpacesError();
} else {
Multimap<Coordinates, Employee> mutableMap = HashMultimap.create(associations);
mutableMap.put(coordinate, employee);
return new Associator(mutableMap);
}
}
public Optional<Coordinates> getLocation(final Employee employee) throws EmployeeInTwoSpacesError {
final Set<Coordinates> found = new HashSet<>();
for (Map.Entry<Coordinates, Employee> entry : associations.entries()) {
if (entry.getValue().equals(employee)) {
found.add(entry.getKey());
}
}
int numLocations = found.size();
if (numLocations == 1) {
return Optional.of(found.iterator().next());
} else if (numLocations > 1) {
// This should be unnecessary, but I'm being paranoid
throw new EmployeeInTwoSpacesError();
} else {
return Optional.empty();
}
}
public ImmutableSet<Employee> getEmployees(final Coordinates location) {
return associations.get(location);
}
}
Here's my Employee.java
:
package com.example;
import java.util.Objects;
import java.util.UUID;
public class Employee {
private final String name;
private final UUID uuid;
public Employee(String name) {
this.name = name;
this.uuid = UUID.randomUUID();
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Employee)) {
return false;
}
Employee other = (Employee) o;
return name.equals(other.name) && uuid.equals(other.uuid);
}
@Override
public int hashCode() {
return Objects.hash(name, uuid);
}
}
Here's my Coordinates.java
package com.example;
import java.util.Objects;
public class Coordinates {
private final int x;
private final int y;
public Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Coordinates)) {
return false;
}
Coordinates other = (Coordinates) o;
return x == other.x && y == other.y;
}
@Override
public int hashCode() {
return Objects.hash(x,y);
}
}
And for completeness, here's my EmployeeInTwoSpacesError.java
:
package com.example;
public class EmployeeInTwoSpacesError extends Exception { }
Main Questions:
Any feedback would be appreciated, but here's some specific questions:
- Is the Employee's UUID the best course of action here to avoid the case where you have similar objects? I never hear people discuss UUIDs
- Is the use of
Optional
s appropriate?