Given a hypothetical TransportConsultancy business that provides cost of travel by various airlines to its customers.
New airlines can register themselves with the consultancy, but must adhere to the contract by defining a method getCostPerUnitDistance
which returns an integer
.
Here's the TransportConsultancy
's business logic:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class TransportConsultancy {
public static enum Location {
Seattle(5),
SanFrancisco(10),
Portland(1),
Denver(13);
private int distanceFromHub;
Location(int distanceFromHub) {
this.distanceFromHub = distanceFromHub;
}
}
private List<String> readRegistry() {
try {
return FileUtils.readLines(new File("src/main/resources/AirlineRegistry"), StandardCharsets.UTF_8);
} catch(Exception e) {
throw new RuntimeException("Unable to lookup registry", e);
}
}
public List<String> getAirlineList() {
return readRegistry().stream().map(s -> s.split(",")[0])
.collect(Collectors.toList());
}
public List<String> getLocationList() {
return Arrays.asList(Location.values())
.stream().map(l -> l.name())
.collect(Collectors.toList());
}
private Class getAirlineSPIInstance(final String airline) {
try {
URLClassLoader loader =
new URLClassLoader(new URL[]{new URL(
"file:///Users/tester
. /ws/spisample/"
)});
return loader.loadClass(readRegistry().stream().filter(s -> s.startsWith(airline))
.findFirst().get() // I know, this isn't production code.
.split(",")[1]);
} catch (ClassNotFoundException | MalformedURLException e) {
throw new RuntimeException("Unable to load SPI instance for airline " + airline, e);
}
}
private int getCostPerUnitDistanceFromSPIInstance(final Class spiInstance) {
try {
for (Method m : spiInstance.getDeclaredMethods()) {
if (m.getName().equals("getCostPerUnitDistance")) {
Object instance = spiInstance.newInstance();
return (Integer) m.invoke(instance);
}
}
throw new RuntimeException("Class does not define getCostPerUnitDistance method");
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Unable to getCostPerUnitDistance", e);
}
}
public int getCost(final String airline, final Location start, final Location end) {
return getCostPerUnitDistanceFromSPIInstance(getAirlineSPIInstance(airline))
* (start.distanceFromHub + end.distanceFromHub);
}
}
Now, this business has an agent who interacts with the customers:
import java.util.Scanner;
public class TransportConsultancyAgent {
public static void main(final String a[]) {
TransportConsultancy c = new TransportConsultancy();
Scanner s = new Scanner(System.in);
System.out.println("Locations: ");
c.getLocationList().forEach(System.out::println);
System.out.print("Enter start location: ");
String start = s.nextLine();
System.out.println();
System.out.print("Enter end location: ");
String end = s.nextLine();
System.out.println();
System.out.println("Airlines: ");
c.getAirlineList().forEach(System.out::println);
System.out.print("Enter airline: ");
String airline = s.nextLine();
System.out.println("COST IS: ");
System.out.println(c.getCost(airline, TransportConsultancy.Location.valueOf(start), TransportConsultancy.Location.valueOf(end)));
}
}
public class MyAirline {
public int getCostPerUnitDistance() {
return 10;
}
}
public class YourAirline {
public int getCostPerUnitDistance() {
return 8;
}
}
Finally
- I compile the above two, and put their bytecodes into the class path,
- Make the following entries in the registry file:
MyAirline,MyAirline
YourAirline,YourAirline
- And then, run
java TransportConsultancyAgent
This is doing what I wanted it to do. Can I now claim I have implemented SPI pattern?
question from:
https://stackoverflow.com/questions/66059427/can-i-call-this-an-spi-pattern 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…