0

I'm encountering a couple awkward situations that seem, in some sense, dual to each other, and was wondering if anyone had any thoughts about how to handle them cleanly.

External initialization

class Human {
   Collection <Human> nextDoorNeighbors;
}

class Neighborhood {
   Collection <Human> humans;
   Neighborhood() {
     // Make humans
     // Then, let them all know who their neighbors are.
   }
}

This is awkward because the humans (in this situation) never have to change their next-door neighbors once they've been set up (they are effectively write-once), but this setup can't be done in the Human constructor because the neighbors that need to be put in the nextDoorNeighbors collection don't all exist when the human is constructed.

Holding something for another

Suppose I want to be able to store Humans in a tree-based map. To do so, the Human has to hold a Comparable ID, even if that isn't logically significant to the concept of a Human. The Human constructs this ID, but it never uses it. Only the map uses it (or even should use it).

dfeuer
  • 48,079
  • 5
  • 63
  • 167

3 Answers3

1

In the first case, maybe the awkwardness is an indication that neighbours shouldn't be a property of Human. Perhaps the Neighbourhood object should be a property of Human, and a method like human.getNeighbours() can be used to get the actual neighbours when they are needed. Then having a neighbours property becomes a private performance issue for the getNeighbours() method.

In the second case, how is your tree-based map providing a structure if the Human is inherently unstructurable? What's the map for if the ID is irrelevant to the human? Typically an ID is relevant, and is used by the class that has it to ensure that it's uniquely identifiable, but if it's genuinely not required, you can use a separate class, like a HumanKey, to wrap the Human for the map.

John Bickers
  • 481
  • 2
  • 6
  • I'm still thinking about the first thing, but for the second, having an identity is an essential property of a human, but having an *orderable* identity is not (much as having a hashable identity is not). – dfeuer Apr 24 '14 at 01:34
  • I think it would probably work, technically, to make `Neighborhood` a nested static class within `Human`, with its constructor serving the dual role of a `Human` factory. This, however, feels utterly backwards. – dfeuer Apr 24 '14 at 01:41
  • Sorry, I didn't mean making the Neighbourhood class nested. I meant something like... well, I can't seem to break this text into paragraphs. Human can contain a property called hood, and that can be set to an instance of the Neighbourhood class. So on construction, Human doesn't know its neighbours, but it does have a handle to an object that can tell it its neighbours later. The idea is to turn the neighbours to something that is naturally post-construction, so the awkwardness disappears. – John Bickers Apr 24 '14 at 01:54
  • The awkwardness just moves elsewhere, I think, with more complex code to cache the neighbors. The reason I mentioned the possibility of nesting the neighborhood class is that doing so should give it access to the neighbors collections without exposing them generally or hiding them with messy package barriers. It also avoids forcing the humans to hold neighborhood fields they may have no use for after neigborhood construction. That is, the Neighborhood class is allowed to drop to the role of a scaffold used in a factory. Your way is clean too, but it's clean in a different (cont) – dfeuer Apr 24 '14 at 02:42
  • (cont) context, in which the neigborhood plays a more substantial role than in my application. In fact, I found myself a bit surprised by how insignificant neighborhoods per se turned out to be. – dfeuer Apr 24 '14 at 02:44
0

I don't really understant what your question is.. Because it's not explicit..

But for the id you can have a static variable in the human class that you will increment in the human constructor and another variable wich will contain the id

It would be something like this

class Human 
{
   private static int humansCounter=0;
   final public int id;

   public Human()
   {
      id=humansCounter++;
   }

}
Tofandel
  • 3,006
  • 1
  • 29
  • 48
  • 1
    I'd recommend making `humansCounter` `private` and `id` `final` to ensure the integrity of each `Human`'s generated ID. – sadakatsu Apr 24 '14 at 01:05
  • Yes you're right.. I'm not realy used to java.. More C# so I don't know all the keywords.. I will edit, thanks. – Tofandel Apr 24 '14 at 01:08
  • You should also use-and-increment so there is less chance of two Threads using the same humansCounter value: `id = humansCounter++;` and if multi-threading is a large concern, use [incrementAndGet()](http://stackoverflow.com/a/4818753/17300) on an `AtomicInteger` – Stephen P Apr 24 '14 at 01:16
  • This does not address the question. I know how to handle these mechanics, and have already done so. The problem is making it *clean*. – dfeuer Apr 24 '14 at 01:36
0

I have an approach that I think is rather clean if the objects themselves need to be aware of the networking. Note that this approach will not work if you concurrently instantiate instances (since each thread will have its own copy of the static fields).

class Foo {
    // instance fields

    public Foo(/* constructor args */) {
        // set instance up
        network(this);
    }

    public boolean isNeighbor(Foo that) {
        return areNeighbors(this, that);
    }

    // static field for tracking edges between neighbors, maybe a
    private static Map<Foo, List<Foo>> neighborhood = new HashMap<>();

    private static void network(Foo me) {
        myNeighbors = new ArrayList<>();
        for (Map.Entry<Foo, List<Foo>> x : neighborhood.entrySet()) {
            Foo n = x.getKey();
            if (/* use me and n's fields to determine whether they are neighbors */) {
                myNeighbors.add(n);
                x.getValue().add(me);
            }
        }
        neighborhood.put(me, myNeighbors);
    }

    public static boolean areNeighbors(Foo a, Foo b) {
        return neighborhood.get(a).contains(b);
    }
}

This approach makes it so that each instance can determine their neighbors without actually knowing their neighbors ahead of time and without using an external class. If an instance's neighbors cannot be inferred from internal state, this approach could be combined with the approach of generating unique IDs (hashes?) for each instance:

class Bar {
    // instance fields

    public Bar(/* constructor args */, int... neighborIndices) {
        // set instance up
        network(this, neighborIndices);
    }

    @Override
    public int hashCode() {
        return /* a unique value based upon this object's state */;
    }

    public boolean isNeighbor(Bar that) {
        return areNeighbors(this, that);
    }

    private static Map<Integer, Bar> lookup = new HashMap<>();
    private static Map<Bar, List<Integer>> neighbors = new HashMap<>();

    private static void network(Bar me, int[] neighbors) {
        lookup.put(me.hashCode(), me);

        List<Integer> neighborList = new ArrayList<>();
        for (int i : neighbors) {
            neighborList.add(i);
        }
        neighbors.put(me, neighborList);
    }

    public static boolean areNeighbors(Bar a, Bar b) {
        return neighbors.get(a).contains(b.hashCode());
    }
}

Naturally, if the neighbor relationships are not associative, it is trivial to modify the first example to be a digraph.

sadakatsu
  • 1,255
  • 18
  • 36