Imagine a Person class with a boolean flag indicating whether or not the person is employable - set to false by default.
public class Person{
boolean employable = false;
...
}
Now imagine having some external boolean methods which act on Person objects. For example, consider static boolean methods in a utility class.
public class PersonUtil{
public static boolean ofWorkingAge(Person p){
if(p.getAge() > 16) return true;
return false;
}
...
}
Boolean static methods are in essence analogous to boolean valued functions i.e. predicates.
We can construct a 2^(#predicates)-by-#predicates truth table out of predicates. For example, given three predicates: ofWorkingAge, ofGoodCharacter, isQualified we can construct the following 8-by-3 truth table:
T T T
T T F
T F T
T F F
F T T
F T F
F F T
F F F
We now want to employ people with desirable qualities. Let + indicate that we wish to consider somebody employable (i.e. set their employability flag to true) and - the opposite.
T T T | +
T T F | +
T F T | +
T F F | -
F T T | +
F T F | -
F F T | -
F F F | -
Now imagine having a collection of Person objects. For each person we adjust their employability flag according to the three predicates. We also update a count (this forces us to use the entire truth table instead of just the positives), so that given 1,000 people we want to end up with something like:
T T T | + 100
T T F | + 200
T F T | + 50
T F F | - 450
F T T | + 50
F T F | - 50
F F T | - 50
F F F | - 50
Presumably this can be thought of as filtering with truth tables. Setting employability flags and updating counts is a rather contrived example but you can easily see how we might instead want to set and update much more complicated things.
QUESTION
Is there a way of elegantly doing this? I can think of two solutions:
Clunky solution
Have a giant hand coded if, else if, else chain.
if(ofWorkingAge && ofGoodCharacter && isQualified){
c1++;
p.setEmployable(true)
}
else if(ofWorkingAge && ofGoodCharacter && !isQualified){
c2++;
p.setEmployable(true)
}
...
else if(!ofWorkingAge && !ofGoodCharacter && isQualified){
c7++;
}
else{
c8++;
}
This is just bad.
Slightly smarter solution
Pass predicates (perhaps in an array) and a collection of sentences to a method. Let the method generate the corresponding truth table. Loop over the people, set their employability, and return an array of counts.
I can see how things could be done with functional interfaces. This SO answer is potentially relevant. You could change PrintCommand to IsQualified and pass callCommand a Person instead of a string. But this also seems kindah clunky because we'd then have to have a new interface file for every predicate we come up with.
Is there any other Java 8-ish way of doing this?
Let's start with the list of predicates you have:
List<Predicate<Person>> predicates = Arrays.<Predicate<Person>> asList(
PersonUtil::ofWorkingAge, PersonUtil::ofGoodCharacter,
PersonUtil::isQualified);
To track which predicate is true or false, let's attach names to them creating NamedPredicate class:
public static class NamedPredicate<T> implements Predicate<T> {
final Predicate<T> predicate;
final String name;
public NamedPredicate(Predicate<T> predicate, String name) {
this.predicate = predicate;
this.name = name;
}
#Override
public String toString() {
return name;
}
#Override
public boolean test(T t) {
return predicate.test(t);
}
}
(one may attach BitSet or something like this for efficiency, but String names are also fine).
Now we need to generate a truth table which is a new list of predicates having names like "T T F" and able to apply the given combination of source predicates, negated or not. This can be generated easily with a bit of functional programming magic:
Supplier<Stream<NamedPredicate<Person>>> truthTable
= predicates.stream() // start with plain predicates
.<Supplier<Stream<NamedPredicate<Person>>>>map(
// generate a supplier which creates a stream of
// true-predicate and false-predicate
p -> () -> Stream.of(
new NamedPredicate<>(p, "T"),
new NamedPredicate<>(p.negate(), "F")))
.reduce(
// reduce each pair of suppliers to the single supplier
// which produces a Cartesian product stream
(s1, s2) -> () -> s1.get().flatMap(np1 -> s2.get()
.map(np2 -> new NamedPredicate<>(np1.and(np2), np1+" "+np2))))
// no input predicates? Fine, produce empty stream then
.orElse(Stream::empty);
as truthTable is a Supplier<Stream>, you can reuse it as many times as you want. Also note that all the NamedPredicate objects are generated on the fly by demand, we don't store them anywhere. Let's try to use this supplier:
truthTable.get().forEach(System.out::println);
The output is:
T T T
T T F
T F T
T F F
F T T
F T F
F F T
F F F
Now you can classify the persons collection by the truth table, for example, in the following way:
Map<String,List<Person>> map = truthTable.get().collect(
Collectors.toMap(np -> np.toString(), // Key is string like "T T F"
// Value is the list of persons for which given combination is true
np -> persons.stream().filter(np).collect(Collectors.toList()),
// Merge function: actually should never happen;
// you may throw assertion error here instead
(a, b) -> a,
// Use LinkedHashMap to preserve an order
LinkedHashMap::new));
Now you can easily get the counts:
map.forEach((k, v) -> System.out.println(k+" | "+v.size()));
To update the employable field we need to know how the desired truth table is specified. Let it be the collection of truth strings like this:
Collection<String> desired = Arrays.asList("T T T", "T T F", "T F T", "F T T");
In this case you may use the previously generated map:
desired.stream()
.flatMap(k -> map.get(k).stream())
.forEach(person -> person.setEmployable(true));
Basically, a truth value is a single bit and you can always use an integer value of n bits to encode n truth value. Then, interpreting the integer value as a number allows you to associate values with the combination of truth values using a linear table.
So using an int a encoded truth value/ table index, a generic truth table class may look like this:
public class TruthTable<O,V> {
final List<? extends Predicate<? super O>> predicates;
final ArrayList<V> values;
#SafeVarargs
public TruthTable(Predicate<? super O>... predicates) {
int size=predicates.length;
if(size==0 || size>31) throw new UnsupportedOperationException();
this.predicates=Arrays.stream(predicates)
.map(Objects::requireNonNull).collect(Collectors.toList());
values=new ArrayList<>(Collections.nCopies(1<<size, null));
}
public V get(O testable) {
return values.get(index(testable, predicates));
}
public V get(boolean... constant) {
if(constant.length!=predicates.size())
throw new IllegalArgumentException();
return values.get(index(constant));
}
public V set(V value, boolean... constant) {
if(constant.length!=predicates.size())
throw new IllegalArgumentException();
return values.set(index(constant), value);
}
public static <T> int index(T object, List<? extends Predicate<? super T>> p) {
int size=p.size();
if(size==0 || size>31) throw new UnsupportedOperationException();
return IntStream.range(0, size).map(i->p.get(i).test(object)? 1<<i: 0)
.reduce((a,b) -> a|b).getAsInt();
}
public static <T> int index(boolean... values) {
int size=values.length;
if(size==0 || size>31) throw new UnsupportedOperationException();
return IntStream.range(0, size).map(i->values[i]? 1<<i: 0)
.reduce((a,b) -> a|b).getAsInt();
}
}
The key point is the calculation of the int index from truth values. There are two versions. First, calculate from explicit boolean values for initializing the table or querying its state, second, for an actual test object and the list of applicable predicates. Note that these two methods are factored out into public static methods so that they can be used for alternative table types, e.g. an array of primitive values. The only thing to do is to create a linear storage for 2ⁿ values when you have n predicates, e.g. new int[1<<n] and then using these index methods for determining the entry to access for given values or an actual test candidate.
Instances of the generic TruthTable can be used as follows:
TruthTable<Person,Integer> scoreTable=new TruthTable<>(
PersonUtil::ofWorkingAge, PersonUtil::ofGoodCharacter, PersonUtil::isQualified);
scoreTable.set(+100, true, true, true);
scoreTable.set(+200, true, true, false);
scoreTable.set(+50, true, false, true);
scoreTable.set(-450, true, false, false);
scoreTable.set(+50, false, true, true);
scoreTable.set(-50, false, true, false);
scoreTable.set(-50, false, false, true);
scoreTable.set(-50, false, false, false);
Person p = …
int score = scoreTable.get(p);
I'm not sure if this is what you're looking for, but you could use a bitwise operators on your variables..
if(ofWorkingAge && ofGoodCharacter && isQualified){
c1++;
p.setEmployable(true)
}
might become
int combined = 0b00000000;
combined |= ofWorkingAge ? 0b00000100 : 0b00000000;
combined |= ofGoodCharacter ? 0b00000010 : 0b00000000;
combined |= isQualified ? 0b00000001 : 0b00000000;
switch (combined){
case 0b00000111:
c1++;
p.setEmployable(true)
break;
case 0b00000110:
// etc
where the last bits represent ofWorkingAge/ofGoodCharacter/isQualified.
Related
Let's say I have two event types (A and B) and Fluxes that generate them somehow:
Flux<A> aFlux = ...;
Flux<B> bFlux = ...;
and also a type that holds the current state denoted by type S:
class S {
final int val;
}
I want to create the following:
final S sInitial = ...;
Flux<S> sFlux = Flux.merge(aFlux, bFlux)
.scan((a, e) -> {
if(e instanceof A) {
return mapA(a, (A)e);
} else if(e instanceof B) {
return mapB(a, (B)e);
} else {
throw new RuntimeException("invalid event");
}
})
.startWith(sInitial);
where sCurr is the instance of S that was last outputted by sFlux, starting with sInitial and mapA / mapB return the new value of type S. Both S and sInitial are immutable.
That is, I want to:
Continously output the latest state ...
... that is being generated ...
... based on the current state and the received event ...
... as prescribed by the mapper functions
Is there a way to reorganize the above stream flow in some other way, especially in order to avoid using instanceof?
You could add interface and implement it for your A and B classes
interface ToSConvertible {
S toS(S s);
}
Now you could use reactor.core.publisher.Flux#scan(A, java.util.function.BiFunction<A,? super T,A>) method:
Flux<S> sFlux = Flux.merge(aFlux, bFlux)
.scan(sInitial, (s, e) -> e.toS(s));
Let say I have 2 classes:
public class Person
{
private String name;
private int age;
private Contact contact;
//getter & setter
}
public class Contact
{
private String phone;
private String email;
//getter & setter
}
With the classes above, I want to create 2 instances of Person class, with different field value. Then I want to compare some fields of 2 objects with their getter function, but I don't want to compare all fields.
For example, I want to compare the field name and phone, then I will store this 2 getter method to a list like something below:
List<WhatShouldBeTheDataType> funcList = new ArrayList<>();
funcList.add(MyClass::getName);
funcList.add(MyClass::getContact::getPhone) //I know this won't work, what should be the solution?
then loop through the funcList, pass the 2 objects I want to compare into the function, if the value not same, write something into the database. This can be easily done with ordinary if...else... way, but is it possible to do in Java 8 way?
Below is what I want to achieve in if...else... way:
if(person1.getName() != person2.getName())
{
//message format basically is: "fieldName + value of object 1 + value of object 2"
log.append("Name is different: " + person1.getName() + ", " + person2.getName());
}
if(person1.getContact.getPhone() != person2.getContact().getPhone())
{
log.append("Phone is different: " + person1.getContact.getPhone() + ", " + person2.getContact.getPhone());
}
//other if to compare other fields
It looks like Person and MyClass refer to the same thing in your question.
You need a Function<Person,String>, since your functions accept a Person instance and return a String:
List<Function<Person,String>> funcList = new ArrayList<>();
funcList.add(Person::getName);
funcList.add(p -> p.getContact().getPhone());
For the second function, you can't use a method reference, but you can use a lambda expression instead.
Given an instance of Person, you can apply your functions as follows:
Person instance = ...;
for (Function<Person,String> func : funcList) {
String value = func.apply(instance);
}
to complete Eran's code:
boolean isEqual(Person person1, Person person2){
for (Function<Person,String> function:functionList) {
if (!function.apply(person1).equals(function.apply(person2))) return false;
}
return true;
}
then use the returned boolean to check and update your database.
Although you can use a list of functions (as suggested in Eran's answer), using comparators directly is probably more appropriate for your use case.
You can alternatively use a chain of comparators, and then use the result of compare:
Comparator<Person> comparators = Comparator.comparing((Person p) -> p.getName())
.thenComparing((Person p) -> p.getContact().getPhone());
Person p1 = null, p2 = null;
if(0 != comparators.compare(person1, person2)) {
//p1 and p2 are different
}
Even simpler (and more natural, in my opinion), is overriding equals in Person, and checking if(!person1.equals(person2))
Edit (after update of the question):
Here's a version built on a function list, dynamically generating the log content by adding a field name list:
List<Function<Person, String>> functions =
Arrays.asList(Person::getName, p -> p.getContact().getPhone());
List<String> fieldNames = Arrays.asList("Name", "Phone");
IntStream.range(0, functions.size())
.filter(i -> functions.get(i).apply(person1)
.compareTo(functions.get(i).apply(person2)) != 0)
.mapToObj(i -> String.format("%s is different: %s, %s",
fieldNames.get(i),
functions.get(i).apply(person1),
functions.get(i).apply(person2)))
.forEach(log::append);
This rather takes advantage of the fact that String is already comparable, and avoids creating comparators altogether.
I would like to create a class in Java 8 which is able to recursively create an object which has a method that takes a function parameter based on the parameters I added.
For example, I would like to be able to do this:
new X().param(23).param("some String").param(someObject)
.apply((Integer a) -> (String b) -> (Object c) -> f(a,b,c))
The apply method would then apply the collected parameters to the given function.
I feel this should be possible without reflection while maintaing type-safety, but I can't quite figure out how. A solution in Scala is also welcome, if I can translate it to Java 8. If it's not possible, I'll also accept an answer that explains why.
What I have so far is essentially this:
class ParamCmd<A,X> {
final A param;
public ParamCmd(A param) {
this.param = param;
}
public<B> ParamCmd<B, Function<A,X>> param(B b) {
return new ParamCmd<>(b);
}
public void apply(Function<A,X> f) {
// this part is unclear to me
}
public static void main(String[] args) {
new ParamCmd<Integer,String>(0).param("oops").param(new Object())
// the constructed function parameters are reversed relative to declaration
.apply((Object c) -> (String b) -> (Integer a) ->
"args were " + a + " " + b + " " + c
);
}
}
As noted in the code comments, my problems are keeping the function parameters in the order of the calls of param(), and actually applying the parameters.
For an unlimited amount of parameters, the only solution I could think of is with Heterogeneous Lists in Scala.
It is probably isn't feasible in Java as there is type level computation going on with path-dependant types.
Using Heterogeneous Lists and Path-Dependant types:
import scala.language.higherKinds
object Main extends App {
val builder1 = HCons(23, HCons("Hello", HNil))
val builder2 = HCons(42L, builder1)
val res1:String = builder1.apply(i => s => i + s)
val res2:String = builder2.apply(l => i => s => (i+l) + s)
println(res1) // 23Hello
println(res2) // 65Hello
}
sealed trait HList {
type F[Res]
def apply[Res]: F[Res] => Res
}
case class HCons[Head, HTail <: HList](head: Head, tail: HTail) extends HList {
type F[Res] = Head => (tail.type)#F[Res]
def apply[Res]: F[Res] => Res = f => tail.apply(f(head))
}
case object HNil extends HList {
type F[Res] = Res
def apply[Res]: F[Res] => Res = identity
}
This code prints:
23Hello
65Hello
The second, more limited way of doing this, but which might work with Java, is to create multiple classes for each function length, which returns the next sized function length class wrapping the value, up to some maximal length - See the Applicative Builder in Scalaz: "Scalaz Applicative Builder"
This doesn't answer your question. However, maybe it helps someone to find a solution, or to explain why it isn't possible in Java and/or Scala.
It can be done in C++, with an arbitrary number of parameters, and without losing type-safety. The call-side look as follows. Unfortunately, the lambda syntax in C++ is quite verbose.
bar{}.param(23).param("some String").param(4.2).apply(
[](int i) {
return [=](std::string s) {
return [=](double d) {
std::cout << i << ' ' << s << ' ' << d << '\n';
};
};
});
Following is the definition of foo and bar. The implementation is straight-forward. However, I doubt that it is possible to build something like this in Java, because the way type parameters work in Java. Generics in Java can only be used to avoid type casts, and that's not enough for this use case.
template <typename Param, typename Tail>
struct foo {
Param _param;
Tail _tail;
template <typename P>
auto param(P p) {
return foo<P, foo>{p, *this};
}
template <typename Function>
auto apply(Function function) {
return _tail.apply(function)(_param);
}
};
struct bar {
template <typename P>
auto param(P p) {
return foo<P, bar>{p, *this};
}
template <typename Function>
auto apply(Function function) {
return function;
}
};
Sorry I just could give some leads in Scala:
Perhaps it would help to have a look at http://www.scala-lang.org/api/2.10.4/index.html#scala.Function$
.apply((Integer a) -> (String b) -> (Object c) -> f(a,b,c))
pretty much looks like Function.uncurried
param(23).param("some String").param(someObject)
could be implemented using a list for an accumulator if you don't care for Type safety. If you want to keep the Types you could use the HList out of Shapeless https://github.com/milessabin/shapeless which comes with a handy tuppled method.
Implementation of param():
import shapeless._
import HList._
import syntax.std.traversable._
class Method(val l : HList = HNil) {
def param(p: Any) = new Method( p :: l )
}
Example
scala> val m = new Method().param(1).param("test")
m: Method = Method#1130ad00
scala> m.l
res8: shapeless.HList = test :: 1 :: HNil
I have a String Vector that contains data like this :
5:34, 5:38, 17:21, 22:11, ...
If i try to merge this using Collections.sort( ... ); it will appear like this :
17:21, 22:11, 5:34, 5:38
Actually i want it to appear like this :
5:34, 5:38, 17:21, 22:11
So i want to sort the elements according to the number before the colon ":" then if some elements have the same number before ":" then sort them according to the number after the ":".
What is the simplest way to do this ?
The correct way to do this is to not store non-string values as strings.
The data in your collection has some structure and rules and can't be any arbitrary string. Therefore you should not use the String data type.
Let's define a type called TwoNumbers (because I don't know what the type should represent, even if I could guess):
class TwoNumbers implements Comparable<TwoNumbers> {
private final int num1;
private final int num2;
public TwoNumbers(int num1, int num2) {
if (num1 <= 0 || num2 <= 0) {
throw new IllegalArgumentException("Numbers must be positive!");
}
this.num1 = num1;
this.num2 = num2;
}
public static TwoNumbers parse(String s) {
String[] parts = s.split(":");
if (parts.length != 2) {
throw new IllegalArgumentException("String format must be '<num>:<num>'");
}
try {
return new TwoNumbers(Integer.parseInt(parts[0]), Integer.parseInt(parts[0]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("parts must be numeric!", e);
}
}
public int getNum1() {
return num1;
}
public int getNum2() {
return num2;
}
#Override
public int compareTo(TwoNumbers o) {
if (o == null) {
return 1;
}
int diff = Integer.compare(o.num1, this.num1);
if (diff == 0) {
diff = Integer.compare(o.num2, this.num2);
}
return diff;
}
}
The compareTo method exists as the implementation of the Comparable interface: it defines how objects of this type are ordered.
I've used the final fields (and don't provide setters), because the class implements immutable objects.
This way you can directly sort your data without an additional Comparator and don't need to distribute all that "split and parse" code all over your program. Instead you have a single class that's responsible for handling that specific format and all the other pieces of code can just use that.
This is horribly inefficient, but it should do the job.
Collections.sort(data, new Comparator<String>(){
public int compare(String a, String b){
String[] as = a.split(":");
String[] bs = b.split(":");
int result = Integer.valueOf(as[0]).compareTo(Integer.valueOf(bs[0]));
if(result==0)
result = Integer.valueOf(as[1]).compareTo(Integer.valueOf(bs[1]));
return result;
}
})
(Hint: if it were my code, I'd optimize it to use substrings instead of String.split(), but I'm too lazy)
You could either create a custom Comparator to split the String and parse it into two ints, or create a bespoke class to represent each String and store that in the Collection instead. I favour the latter approach as you only incur the overhead of splitting / parsing the String once; e.g.
public class Data implements Comparable<Data> {
private final int prefix;
private final int suffix;
public Data(String str) {
String[] arr = str.split(":");
if (arr.length != 2) {
throw new IllegalArgumentException();
}
this.prefix = Integer.parseInt(arr[0]);
this.suffix = Integer.parseInt(arr[1]);
}
public int compareTo(Data data) {
// Should really avoid subtraction in case of overflow but done to keep code brief.
int ret = this.prefix - data.prefix;
if (ret == 0) {
ret = this.suffix - data.suffix;
}
return ret;
}
// TODO: Implement equals and hashCode (equals to be consistent with compareTo).
public String toString() { return String.format("%d:%d", prefix, suffix); }
}
Then it's simply a case of storing some Data objects in your Collection; e.g.
List<Data> l = new ArrayList<Data>();
l.add(new Data("13:56"));
l.add(new Data("100:16"));
l.add(new Data("9:1"));
Collections.sort(l);
One more thing - You mention you're using a Vector. You should try to avoid using Vector / Hashtable as these have been superseded by List / Map, which were introduced as part of the Collections Framework in JDK 1.2.
Create a java.util.Comparator and provide it to the sort method.
Implement your own Comparator class that compares two values and call Collections.sort(List list, Comparator c).
Implement your own Comparator and give it as second argument to the Colelctions.sort method.
Generally, objects in Java (including Collections) are compared with their default hashCode() and equals() method. For the built in objects and data types (like String, Integet etc.,) the hashCode() is computed internally and hence they are used as guaranteed by the JLS (Java Language Specification).
As we can't always be dependent upon the default/built in objects and we need to deal with our own custom objects (like Employee, Customer etc.,), we should have to override hashCode() and equals() method, so that we can provide the true/false according to the "BEST" equality of the objects of our custom classes.
Similary, sort() involves a comparison act that indeed needs a Comparator (which is a class implementing the Comparator interface with an overridden method of compare method). You should also override the compare method that takes two Objects to be compared and returns a result (0 for equal, 1 for the 1st object being greater than the second, 2 for the reverse of case 1).
Now, you data should be dealt in a different way which is quite away from the normal comparsion. You need to split the data into two parts (using a split method you can do) and then you can do the individual comparison on the two parats (first part before the colon, second part after the colon).
Finally, you should provide an instance of this custom comparator to the sort method, that will eventually do the custom sorting for your custom data :)
I think this is pretty simple:
public class NumericalStringSort {
public static void main(String[] args) {
List<String> input = Arrays.asList(new String[] {"17:21", "22:11", "5:34", "5:38"});
Collections.sort(input, new NumericalStringComparator());
System.out.println(input);
}
public static class NumericalStringComparator implements Comparator<String> {
public int compare(String object1, String object2) {
return pad(object1).compareTo(pad(object2));
}
private String pad(String input) {
return input.indexOf(":") == 1 ? "0" + input : input;
}
}
}
Just found this (quite old) post and the answers didn't quite solve the problem I have. I needed a more generic solution, as the values were user inputs and something like "abc 1 a 12" and "abc 1 a 1" should be sorted in order of the contained number(s). So I wrote the following Comparator:
new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
String[] s1=splitNumeric(o1);
String[] s2=splitNumeric(o2);
for (int x=0;x<s1.length&&x<s2.length;x++){
if (!s1[x].equals(s2[x])){
if (s1[x].charAt(0)=='N' && s2[x].charAt(0)=='N'){
long l1=Long.parseLong(s1[x].substring(1));
long l2=Long.parseLong(s2[x].substring(1));
return (int)Math.signum(l1-l2);
}
break;
}
}
return o1.compareTo(o2);
}
}
While the function splitNumeric is defined as follows:
private String[] splitNumeric(String s){
final String numbers="0123456789";
LinkedList<String> out=new LinkedList<String>();
int state=-1;
for (int x=0;x<s.length();x++){
if (numbers.contains(s.charAt(x)+"")){
if (state==1)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=1;
out.add("N"+s.charAt(x));
}
}
else{
if (state==0)
out.set(out.size()-1,out.getLast()+s.charAt(x));
else{
state=0;
out.add("S"+s.charAt(x)+"");
}
}
}
return out.toArray(new String[0]);
}
The code will sort Strings
"X 124 B"
"X 1 Y"
"X 111 Z"
"X 12 Y"
"12:15"
"12:13"
"12:1"
"1:1"
"2:2"
as follows:
"1:1"
"2:2"
"12:1"
"12:13"
"12:15"
"X 1 Y"
"X 12 Y"
"X 111 Z"
"X 124 B"
Enjoy :)
This is kind of a design-patterns question in Java.
I am designing a java .jar file to act as a frameworks for managing and processing a certain form of data. I want the end user to be able to dictate the "plumbing" configuration, within certain constraints, in a certain way. The pieces are producers and/or consumers, and I know how to implement them, but the connections are confusing to me... here's a nonsense example that sort of parallels my application.
Suppose I have implemented these elements:
AppleTree => produces apples
ApplePieMaker => consumes apples,
produces apple pies
ApplePress =>
consumes apples, produces apple cider
AppleSave => stores apples,
apple pies, or apple cider into a
file
AppleLoad => "reconstitutes" apples, apple pies, or apple cider from a file that was produced by AppleSave
ApplePieMonitor => displays apple pies on the screen in a GUI format, as they are produced
Now I want the user to be able to specify things like:
AppleTree | ApplePress | AppleSave cider1.sav (produce apples, make them into cider, save them to a file)
AppleTree | AppleSave apple1.sav (produce apples, save them to a file)
AppleLoad apple1.sav | ApplePieMaker | ApplePieMonitor (take saved apples, make them into pies, display the results on the screen in a GUI)
(not sure how to illustrate this, but might be specified as follows)
AppleTree tree1, ApplePieMaker piemaker1 < tree1, AppleSave apples.sav < tree1, AppleSave #select(*.sav) < piemaker1, ApplePress press1 < tree1, AppleSave cider.sav < press1, ApplePieMonitor piemon1 < piemaker1
(produce apples, make them into pies and cider, save the apples, pies and cider to separate files, with the pies going to a file selected by the user at runtime and the others going to predetermine files, and also display the pies on the screen in a GUI)
So I have a rough idea of how to architect a configuration file: namely to structure the thing into elements that have at most 1 input and at most 1 output, and then for each instantiated element, name it, and if it has an input, specify the name of the element providing the input.
What I'm unclear is how to go about coupling the elements of the program when they run. Maybe the way to go is to have a number of interfaces, like AppleConsumer, ApplePieConsumer, etc. so an ApplePieMaker would implement the AppleConsumer interface (incl. method consumeApple()), and an AppleTree would implement an AppleProducer interface which can register consumers at startup, so that every time the AppleTree produces an apple, it has a list of its consumers and calls consumeApple() on each of them, which then does the right thing without the AppleTree having to know what they are doing with the apples....
Any suggestions? Does this sort of thing have a name? I'm not really that experienced in design patterns.
edit: my end users don't know and don't care about Java. They just need to be able to set up a config file (which I'm trying to make as simple as possible so I can give them some good examples) and run my program which will read the config file, construct the elements, hook them together, and go. All the elements are under my control, I don't need to support plugins, so I don't have to be super-general.
I had that problem a while ago, and it was a bit hard to specify cleanly in Java, since my input and output could be plural. However, when you are sure to have a single input and output (since a file is a specific kind of output, right?), you could try to use checked generics.
A processing step implementation (I will call that a filter) has two checked type arguments: input and output (lets for instance suppose they all extend a common interface, and for every new type you inject in the system, you will subclass that interface).
public interface Filter<Input extends Type, Output extends Type> {
public Class<Input> getInputType();
public Class<Output> getOutputType();
public void process(Input in, Output out);
}
A filter chain is then just an array of (compatible) Filters. By compatible, I intend that for each filter, its Output is the same type as its follower Input, the first filter has an Input that match your overall input type, and the last filter has an Output that matches the expected result type. This is easy to validate, in practice, since we are using checked generics.
A filter chain is thus an other (compound) filter. The compatibility of compounded filters should be checked in the constructor, and the array of compounds final. There is no accurate way to express the "chaining" property (compatibility) of the arguments of that constructors with generics, so you are going to have to do that with bare types, which is a bit unclean.
An other way to do it that gets around this limitation, at the cost of more cumbersome writing, is to change the definition of a filter like this:
public interface Filter<Input extends Type, Output extends Type> {
public Class<Input> getInputType();
public Class<Output> getOutputType();
public Output out process(Input in);
}
We will then have to define a compound filter as an imbrication of filter pairs, thus defined:
public class CompoundFilter<Input extends Type, Output extends Type>
implements Filter<Input extends Type, Output extends Type> {
private final Filter<Input extends Type, ? extends Type> l;
private final Filter<Input extends Type, ? extends Type> r;
public <Median extends Type> CompoundFilter(
Filter<Input, Median> r,
Filter<Median, Output> l
) {
this.l = l;
this.r = r;
}
#SuppressWarnings("unchecked")
public Output out process(Input in) {
// Compute l(r(in)) = (l o r) (in)
return ((Output<Input,Type>) l).process(r.process(in));
}
}
Thus, composing filters is just a matter of writing:
Filter<A,B> f1 = new FilterImpl<A,B>;;
Filter<B,C> f2 = new FilterImpl<B,C>;
// this is mathematically f2 o f1
Filter<A,C> comp = new CompoundFilter<A,C>(f1,f2);
I could not help it, I have to workout something for this.
So here it is.
You already have the idea about the apple producer/consumer so this is how I would do it.
Create three interfaces and implement as follows:
Product - Either Apple, AppleCider, ApplePie,
Producer - AppleTree, ApplePress, ApplePieMaker, AppleLoad,
Consumer - ApplePress ( consumes Apples ), ApplePieMaker ( consumes Apples ) , AppleMonitor, AppleSave.
The idea is to have a generic Product, produced by generic Producers and consumed by generic Consumers.
Once you have that, you can create the configuration file pretty much the way you describe it, and parse it to create a new instance for each element.
element1 | element2 | element3 <parameters> | element 4
In a map you create the element name and map it to the class that will create the new instance.
Let's say
map.put( "AppleTree", YouAppleTreeClass.class );
So each time you read an element in the configuration you create the instance:
for( String item: line ) {
Object o = map.get( item ).newInstance();
}
Finally you have to validate the structure of your configuration, but basically it could be like this:
The first element should be a producer
The last should be a consumer
Any intermediate should be producer-consumers
You can parse arguments needed ( file to save data for instance )
Once you have all your objects created and chained, you start producing.
There are some thing you have to workout but they're pretty easy:
Argument passing ( the file where they will be saved/loaded from)
Object re-use in different configurations ( use the same AppleTree always )
Final notes: The following code, is just an scratch, you may really consider a dependency injector to do the job, but of course it will take you a little while to learn it.
The configuration parsing should be made by hand, for the format you're using will be unique for the end-user and should be pretty simple. Still you can deliver as much complexity you want inside your jar ( using any number of frameworks you need ).
You can also take a look to the following design patterns:
Composite
Observer
Interpreter
The implementation below, is a kind of monster the these three ( I didn't compile it, just throw some code to show how the idea would look like )
I hope this helps.
/**
* Anything. An apple, cider, pie, whatever.
*/
interface Product{}
// The kinds of products.
class Apple implements Product{}
class ApplePies implements Product{}
class AppleCider implements Product{}
/**
* This indicates the class will do something.
**/
interface Producer {
// adds a consumer to the list.
public void addConsumer( Consumer c );
// removes the consumer from the list.
public void removeConsumer( Consumer c );
// let know eveytone a product has been created.
public void notifyProductCreation( Product someProduct );
// You're producer? Produce then...
public void startProduction();
}
// To avoid copy/paste all around
class AbstractProducer implements Producer {
private List<Consumer> consumers = new ArrayList<Consumer>();
// adds a consumer to the list.
public void addConsumer( Consumer c ) {
consumers.add( c );
}
// removes the consumer from the list.
public void removeConsumer( Consumer c ) {
consumers.remove( c );
}
public void notifyProductCreation( Product someProduct ) {
for( Consumer c : list ) {
c.productCreated( someProduct );
}
}
}
interface Consumer {
// Callback to know a product was created
public void productCreated( Product p );
}
class AppleTree extends AbstractProducer {
public void startProduction() {
// do something with earh, sun, water..
// and from time to time:
Product ofThisNewApple = new Apple();
notifyProductCreation( ofThisNewApple );
}
}
class ApplePieMaker extends AbstractProducer implements Consumer {
// Ok, a product was created, but
// is it the product I care?
// check first and consume after.
public void productCreated( Product p ){
// Is this the kind of product I can handle..
// well do handle
if( p instanceof Apple ) {
/// start producing pies..
}
}
public void startProduction() {
// collect the needed number of apples and then...
Product ofPie = new ApplePie();
notifyProductCreation( ofPie );
}
}
class ApplePress extends AbstractProducer implements Consumer {
// Yeap, something gots produced.
// Just handle if it is an apple
public void productCreated( Product p ) {
if( p instanceof Apple ) {
// start producing cider
}
}
public void startProduction() {
// collect the needed number of apples and then...
Product ofCiderBottle = new AppleCider();
notifyProductCreation( ofCiderBottle );
}
}
class AppleSave implements Consumer {
public void productCreated( Product p ) {
file.append( p );// any one will do.
}
}
class AppleLoad extends AbstractProducer {
public void startProduction() {
readFromFile();
}
private readFromFile() {
for( Product p : file ) {
notifyProductCreation( p );
}
}
}
class Main {
public static void main( String [] args ) {
Configuration conf = new Configuration();
List<Producer> producers conf.read();
for( Producer p : producers ) {
// fasten your seat belts....
p.startProduction();
}
}
}
/// Ahhh, pretty ugly code below this line.
// the idea is:
// Read the configuration file
// for each line split in the "|"
// for each element create a new instance
// and chain it with the next.
// producer | consumer | etc...
// Becomes....
// new Producer().addConsumer( new Consumer() );
// Return the list of create producers.
class Configuration {
List<Producer> producers
// read the file
// create the instances
// let them run.
public List<Producer> read() {
File file = new File(....
// The format is:
// producer | consumer-producer <params> | consumer
String line = uniqueLineFrom( file );
String [] parts = line.split("|");
if( parts.length == 1 ) {
System.err.println("Invalid configuration. use element | element | etc. Only one element was....");
System.exit( 1 );
}
int length = parts.length;
for( int i = 0 ; i < parts.length ; i++ ) {
Object theInstance = implementationMap.get( parts[i] ).newInstance();
validatePosition( i, length, theInstance , parts[i] );
}
List<Producer> producers = new ArrayList<Producer>();
for( int i = 0 ; i < parts.length ; i++ ) {
Object theInstance = getInstance( parts[i] );
if( not( isLast( i, length ) && isProducer( theInstance ) ) {
// the next is its consumer
Producer producer = ( Producer ) theInstance;
producer.addConsumer( ( Consumer ) getInstance( parts[i+1] ));
producers.add( producer );
}
}
return producers;
}
// creates a new instance from the implementation map.
private Object getInstance( String key ) {
return implementationMap.get( part[i] ).newInstance();
}
// validates if an element at the given position is valid or not.
// if not, prints the message and exit.
// the first element most be a producer
// the last one a consumer
// all the middle elements producer-consumer
//
private void validatePosition( int i, int length, Object theInstance, String element ) {
if( isFirst( i ) && not(isProducer(( theInstance ) ))) {
System.err.println( "Invalid configuration: " + element + " most be a producer ( either Ap...");
System.exit( 2 );
} else if ( isLast( i, length ) && not( isConsumer( theInstance ))) {
System.err.println( "Invalid configuration: " + element + " most be a consumer ( either Ap...");
System.exit( 3 );
} else if ( isMiddleAndInvalid( i, length , instance ) ) {
System.err.println( "Invalid configuration: " + element + " most be a producer-consumer ( either Ap...");
System.exit( 4 );
}
}
private static Map<String,Class> implementationMap = new HashMap<String,Class>() static {
implementationMap.put( "AppleTree", AppleTree.class );
implementationMap.put( "ApplePieMaker ", ApplePieMaker .class );
implementationMap.put( "ApplePress", ApplePress.class );
implementationMap.put( "AppleSave", AppleSave.class );
implementationMap.put( "AppleLoad", AppleLoad.class );
implementationMap.put( "ApplePieMonitor", ApplePieMonitor.class );
};
// Utility methods to read better ( hopefully ) the statements
// If you could read the validations above you may ignore these functions.
private boolean not( boolean value ) {
return !value;
}
private boolean isFirst( int i ) {
return i == 0;
}
private boolean isLast( int i, int l ) {
return i == l -1 ;
}
private boolean isProducer( Object o ) {
return o instanceof Producer;
}
private boolean isConsumer( Object o ) {
return o instanceof Consumer;
}
private boolean isMiddleAndInvalid( int index, int length, Object instance ) {
return not( isFirst( index ) ) && not( isLast( index, length ) ) && not( isProducer( instance ) && isConsumer( instance ));
}
}
I believe what you are trying to do can be done within the Spring framework. It uses dependency injection to say "In order to create X I need Y, so find something that produces Y and see what you need in order to create it".
I may be wrong, but I suggest you have a look.
You would need some sort of Registry where producers could register (Hi, I'm an apple tree and I produce apples) and then the consumers could look up whom ever produces apples. This could also be done in reverse where the consumers register interest and the producers look up. I did something similar using JMX where an Object could query the JMX Server for an Object that produced a certain type of Message and then register with that Object (Publish/Subscribe). I am now porting that app to use OSGi which has a similar capability
Try the Java Beanshell.
BeanShell is a small, free, embeddable Java source interpreter with object scripting language features, written in Java. BeanShell dynamically executes standard Java syntax and extends it with common scripting conveniences such as loose types, commands, and method closures like those in Perl and JavaScript.