From: Tom McGlynn on
On Jul 29, 10:39 am, Alan Gutierrez <a...(a)blogometer.com> wrote:
> Tom McGlynn wrote:
> > On Jul 29, 7:20 am, Tom Anderson <t...(a)urchin.earth.li> wrote:
> >> On Tue, 27 Jul 2010, Tom McGlynn wrote:
> >>> On Jul 26, 1:33 pm, Tom Anderson <t...(a)urchin.earth.li> wrote:
> >>>> On Mon, 26 Jul 2010, Tom McGlynn wrote:
> >>>>> On Jul 26, 8:01 am, Tom Anderson <t...(a)urchin.earth.li> wrote:
> >>>>>> But Joshua was talking about using instances of Color, where those
> >>>>>> instances are singletons (well, flyweights is probably the right term
> >>>>>> when there are several of them)
> >>>>> I don't think flyweights is the right word.  For me flyweights are
> >>>>> classes where part of the state is externalized for some purpose. This
> >>>>> is orthogonal to the concept of singletons. E.g., suppose I were
> >>>>> running a simulation of galaxy mergers of two 100-million-star
> >>>>> galaxies.  Stars differ only in position, velocity and mass.  Rather
> >>>>> than creating 200 million Star objects I might create a combination
> >>>>> flyweight/singleton Star where each method call includes an index that
> >>>>> is used to find the mutable state in a few external arrays.
> >>>> I am 90% sure that is absolutely not how 'flyweight' is defined in the
> >>>> Gang of Four book
> >>> Here's a bit of what the GOF has to say about flyweights.  (Page 196
> >>> in my version)....
> >>> "A flyweight is a shared object that can be used in multiple contexts
> >>> simultaneously. The flyweight acts as an independent object in each
> >>> context--it's indistinguishable from an instance of the object that's
> >>> not shared.... The key concept here is the distinction between intrinsic
> >>> and extrinsic state.  Intrinsic state is stored in the flyweight.  It
> >>> consists of information that's independent of the flyweight's context,
> >>> thereby making it shareable.  Extrinsic state depends on and varies with
> >>> the flyweights context and therefore can't be shared.  Client objects
> >>> are responsible for passing extrinsic state to the flyweight when it
> >>> needs it."
> >>> That's reasonably close to what I had in mind.
> >> Yes, point taken. I'm still not happy with your usage, though.
>
> >> IIRC, the example in GoF is of a Character class in a word processor. So,
> >> a block of text is a sequence of Character objects. Each has properties
> >> like width, height, vowelness, etc and methods like paintOnScreen. But
> >> because every lowercase q behaves much the same as every other lowercase
> >> q, rather than having a separate instance for every letter in the text, we
> >> have one for every distinct letter.
>
> >> The extrinsic state in this example is the position in the text, the
> >> typeface, the style applied to the paragraph, etc. Certainly, things that
> >> are not stored in the Character. But also not things that intrinsically
> >> belong in the Character anyway; rather, things inherited from enclosing
> >> objects.
>
> >> Whereas in your case, the array offset *is* something intrinsic to the
> >> Star. If it had been something else, say the coordinates of the centre of
> >> mass of the local cluster, then i'd agree that that was Flyweightish. But
> >> i'm not so sure about the array index.
>
> > Hmmm....  The GOF has the location of the letter as extrinsic state,
> > while I'm suggesting the location of the star.  Seems pretty
> > comparable.  My use of the
> > array index is simply the way I supply the extrinsic information, it's
> > not intrinsic to a given Star [and in fact the index of the same
> > 'Star' might change during the simulation, since the array is likely
> > to be resorted continually in the process].   I wonder if the sticking
> > point is the lack of any internal state.  But the GOF notes that the
> > FlyWeight is particularly applicable when "Most object state can be
> > made extrinsic".  This doesn't mean that Star isn't a real class.  We
> > can have lots of methods in the Star class, e.g.,
> > distanceFromNeighbor(i), move(), accelerate(), force(), ...
>
> 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.

Regards,
Tom McGlynn
From: Alan Gutierrez on
Tom McGlynn wrote:
> On Jul 29, 10:39 am, Alan Gutierrez <a...(a)blogometer.com> wrote:
>> Tom McGlynn wrote:
>>> On Jul 29, 7:20 am, Tom Anderson <t...(a)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(a)blogometer.com - http://twitter.com/bigeasy
From: Tom McGlynn on
On Jul 31, 9:06 pm, Alan Gutierrez <a...(a)blogometer.com> wrote:


> 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() {

While this might be a fine way to implement things, I don't think
Star's
created this way are FlyWeights. It looks like all of a Star's state
is internal so they are just standard objects. By definition (at
least
the GOF's) FlyWeights have external state. Not that you have to use
FlyWeight's but it was in trying to illustrate them that I brought
up the example.


> 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.

For me an Adaptor class doesn't change the semantics of the interface
but just the details of the implementation. E.g., one interface has
double getX(); double getY(); double getZ();
and the other has
double[] getPosition();
They both have the idea of "get the position", but differ in the
implementation
details. An Adapter bridges the difference and allows a Class
expecting
objects of the first type to use the second.

n your example I see our Star as an implementation of the
MappedByteBuffer
but I'm not sure which -- if any -- of the GOF patterns the
relationship of Star and MappedByteBuffer represents for me.


>
> 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.

Not sure what you are saying here.

My original approach had sharing.

There was a single actual star instance that's
shared by each logical star. How much more sharing can you get! My
guess
is that it's the fact that sharing is taken to the limit that causes
some of the discomfort people evinced here. They don't like a
FlyWeight
that uses only a single actual instance.

But you're right that you don't need FlyWeight's to address
the issue. Your approach using an external cache might well work fine
though
I'd be concerned if I had to create an object every access. Even the
relatively
efficient object creation that Java now has needs to be amortized over
a fair bit
of computation. Perhaps your approach could be called something like
'lazy
transient instantiation with an external cache'. But it wouldn't have
made my
original point of demonstrating the orthogonality of FlyWeights and
Singletons.

>
> 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.

For me FlyWeight's need to share external state. They need to be
'lighter'
than an equivalent 'normal' object would be.
My guess is that a lot of this has to do with the relationship you
want to emphasize. Given implementations will weave multiple patterns
together. By picking out one aspect we may emphasize one pattern.


>
> 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.
>

It's always more interesting when other people's views differ from
one's
own (though perhaps less gratifying), and I've found the the
discussion
very intriguing and it's alway useful to reread the source text.

Regards,
Tom