Class Constants - pros and cons

L

Lew

Tom said:
But I'm not trying to persuade people that using flyweights in the
sense that I suggested in my example is necessarily the right thing to
do in all circumstances, merely to note that it may be a rational or
even desirable approach in some. That example was given to give a
concrete realization of an object that simultaneously implemented
flyweight and singleton, not for its intrinsic merit but I was a
little bemused by reaction to it.

When dealing with 200 M stars, one might be tempted to use a multi-threaded
approach. Sharing a singleton among threads introduces the complexity and
overhead of synchronization. The straightforward object approach, combined
with appropriate caching (the buffer approach or DBMS approach) simplifies
concurrent implementations.
 
T

Tom McGlynn

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(), ...

.....back to what word to use...
I can't think of a good word for this. Do we need one? What are some
examples of this pattern in the wild?
I was inspired to start this subthread by the sense that something was
missing given your use of flyweight for this concept. As for
examples:

Enumerations are one of course. Another might be the states in finite
state machines.

I think the concept of "An a priori known and unalterable set of
instances" comes up fairly commonly in code and it might be useful to
be able to convey that quickly. If thats true perhaps the first step
is to agree about what the concept is and then worry about the word.


Regards,
Tom McGlynn
 
T

Tom Anderson

When dealing with 200 M stars, one might be tempted to use a
multi-threaded approach. Sharing a singleton among threads introduces
the complexity and overhead of synchronization.

McGlynn's flyweights are immutable, so sharing them between threads should
be fine.

The mutable state lives in the parallel arrays, and access to those would
need to be controlled. *That* could be tricky, because there's no natural
object to lock on if you want to access a given row. You certainly can't
do it with a flyweight, because the same flyweight instance will be in use
by other threads to represent entirely different stars!
The straightforward object approach, combined with appropriate caching
(the buffer approach or DBMS approach) simplifies concurrent
implementations.

Hmm. I don't think Star-level locking would be a good idea, regardless of
how Stars work. You'd want to partition larger units between threads.
Given that you'll have to build a mechanism for controlling concurrency at
that level anyway, whether you have real stars or flyweights might not
matter very much.

tom
 
A

Alan Gutierrez

Tom said:
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.
 
T

Tom McGlynn

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
 
A

Alan Gutierrez

Tom said:
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.
 
T

Tom McGlynn

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top