Re: Class Constants - pros and cons
Tom McGlynn wrote:
On Jul 29, 10:39 am, Alan Gutierrez <a...@blogometer.com> wrote:
Tom McGlynn wrote:
On Jul 29, 7:20 am, Tom Anderson <t...@urchin.earth.li> wrote:
I don't think the state of the `Star` can be extract from the `Star`.
The idea behind the Word Processor example is that you can cache the
font size in an object along with the character code itself, and have an
object that can be reused and reset into the document at any location,
and then participate in a `Composite` pattern, where the characters
participate as tiny graphical objects.
But this implies that 11pt Helvetica 'C' is one object that is reused.
I'm assuming that each of these `Star` objects will have different vales
entirely, therefore `Flyweight` does not apply much at all.
I've called this a tiny `Adaptor` because you're going to take a
`MappedByteBuffer` or parallel arrays of primitives, or something
structure that stores the state of the object, and when you need an
object, create a temporary wrapper around the state of a `Star` stored
at a particular index.
By the GOF's definition an Adapter is used to convert one interface to
another. So given what I would call a FlyWeight style interface
interface IndexedStar {
double[] getPosition(i)
}
then if you want to have a
interface NonIndexedStar {
double[] getPosition()
}
you could create an adapter class
class StarAdapter {
int index;
IndexedStar base;
StarAdapter(int i, IndexedStar star} {
index = i;
this.base = star;
}
double[] getPosition() {
return base.getPosition(i);
}
}
[I'm not suggesting this is a good way to go, just trying
to clarify what the terms mean to me.]
Why is the position of a star any more 'intrinsic' to the star, than
the position of a character is to the character? I think the
difference in perception comes from the fact that as this toy problem
has been set up, there is no internal state for the star. Suppose we
make the problem a little more complex. We have 25 classes of star:
5 ages x 5 spectral types with 4,000,000 of each. Now there are 25
flyweight instances which have different masses, temperatures,
brightness, color, metallicity, magnetic fields, whatever... If I
want to generate an image of the simulation at some time, I need to
use the internal state of the flyweights to generate the image just as
we need the actual patterns of each glyph to generate a page of text.
If this isn't a flyweight what's missing? If it is, note that I
could be using an identical mechanisms to store the position/
velocity... as before.
So I think the issue is that people are unfamiliar with FlyWeights
with little internal state but perhaps I'm missing some more essential
difference.
Maybe this goes back to your thoughts on this being an adapter, which
I'll recast in a positive way: Use of a no-internal state flyweight
can be used to as an adapter to give an object oriented interface for
elements of arrays or other structures.
On another branch of this thread, I wrote some code that actually
compiles, that described a `BigList` that had an `ElementIO`. I'm going
to put it here again for your reference. Hope no one minds.
package comp.lang.java.programmer;
import java.nio.ByteBuffer;
public interface ElementIO<T> {
public void write(ByteBuffer bytes, int index, T item);
public T read(ByteBuffer bytes, int index);
public int getRecordLength();
}
package comp.lang.java.programmer;
import java.nio.MappedByteBuffer;
import java.util.AbstractList;
public class BigList<T> extends AbstractList<T> {
private final ElementIO<T> io;
private final MappedByteBuffer bytes;
private int size;
public BigList(ElementIO<T> io, MappedByteBuffer bytes, int size) {
this.io = io;
this.bytes = bytes;
this.size = size;
}
// result is not `==` to value `set` so only use element type that
// defines `equals` (and `hashCode`).
@Override
public T get(int index) {
return io.read(bytes, index * io.getRecordLength());
}
@Override
public T set(int index, T item) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
T result = get(index);
io.write(bytes, index * io.getRecordLength(), item);
return result;
}
@Override
public void add(int index, T element) {
size++;
// probably off by one, but you get the idea...
for (int i = size - 2; i >= index; i--) {
set(index + 1, get(index));
}
set(index, element);
}
// and `remove` and the like, but of course only `get`, `set`
// and `add` to the very end can be counted on to be performant.
@Override
public int size() {
return size;
}
}
With these generics we could build an implementation of `StarIO`.
public class StarIO implements ElementIO<Star> {
public void write(ByteBuffer bytes, int index, Star item) {
bytes.putDouble(index, item.getPosition[0]);
bytes.putDouble(index + (Double.SIZE / Byte.SIZE),
item.getPosition[0]);
}
public Star read(ByteBuffer bytes, int index) {
double[] position = new double[] {
bytes.getLong(index),
bytes.getLong(index + (Double.SIZE / Byte.SIZE))
};
return new Star(position);
}
public int getRecordLength() {
return (Double.SIZE / Byte.SIZE) * 2;
}
}
Usage is then like one big list. When you write, you don't actually put
the `Star` in an array, you write out the values. When you read you
create a new `Star` read from the underlying `MappedByteBuffer`.
Therefore, to my mind, the `Star` is an Adaptor for the
`MappedByteBuffer` where the strategy for an Adaptor is to:
Convert the interface of a class into another interface clients expect.
Adaptor lets classes work together that couldn't otherwise because of
incompatible interfaces.
Whereas the Flyweight strategy is to:
Use sharing to support large numbers of fine grained objects efficiently.
The solution we are discussing address the problem of supporting large
numbers of fine grained efficiently, but not through sharing.
Thus, there's an opportunity to name a new pattern, if you'd like.
I am not arguing semantics, I don't think, but really trying to
understand what patterns are in play. The interesting concept that makes
one think of flyweight is that the Adaptor is short-lived, which is why,
when you say Flyweight, the name seems apropos, but I believe there's a
pattern here that needs its own name.
This is the pattern that is used by any of the ORM tools. Create a short
lived typed object around a string or binary data for the sake of the
client.
--
Alan Gutierrez - alan@blogometer.com - http://twitter.com/bigeasy