Re: Why encapsulate state pattern......



Bruno Desthuilliers wrote:
> Mark Nicholls a écrit :
> > bruno modulix wrote:
> >
> (snip)
> >>
> >>The idea here is to change (part of) the object's *implementation*,
> >>without changing the interface nor the object's identity.
> >
> >
> > I don't see the relevance of identity....
>
> Really ?

Sorry, I think I need more, what is the relevance of identity?

>
> > we could in fact implement
> > what I am suggesting in a single class, the methods simply returning
> > 'this', but navigated to the correct state interface.
> >
> > My main suggestion is when actually sharing a single interface between
> > multiple states implies meaningless or erroneous behaviour, i.e.
> > closing an already closed port.
>
> Deciding if a given *message* (not 'behavior') is meaningless is the
> state object's responsability. If it's meaningless, the best *behavior*
> is to quietly ignore it.

Surely the best behaviour is for it not to exist, or even possibly
exist.

> As for "erroneous", I don't see how a *message*
> could be qualified as such.

OK, but the existence of a meaningless has an effect on the definition
of behaviour, in is quite normal to implement this by throwing an
exception if someone tries to take the sqrt of a negative number, close
a closed port etc.

e.g.

if we have the set of Reals with '*' and 'sqrt' defined, sqrt is not
defined on negative numbers, rather than defining behaviour on R as

R:sqrt(x) x>=0 : sqrt(x) * sqrt(x) = x & x>0
x<0 : undefined (i.e. possibly exception)


if we confine our definition to R+, with '*' and 'sqrt' defined.

R+:sqrt(x) all x: sqrt(x) * sqrt(x) = x

which is the simplest definition of behaviour?

R+?

> Sending the "close" message to an already
> closed connection *could* result in an "erroneous" behavior *if* the
> connection object doesn't know how to handle it's state -

it's an exception if it breaches the precondition, it is not
unreasonable to have a precondition that the port should be open for
you to call close, but this means that the client has to track
encapsulated state, it either queries it, or logically the flow of the
code 'knows' the port is open.....that is a cost.

If 'close' does not exist, that cost evaporates. (the additional cost
is that the client has to track a rule that says old states need to be
thrown away, in the state pattern this is done by context....but I
think that is an easier rule)

> or if the
> close() selector was missing from the internal state object of the
> connection !-)

"close selector"?

"close" has to exist on all states, even when it is meaningless.

>
> (snip)
> >
> >>>The example given in GoF is that of a TCP connection where it can be in
> >>>Listen,Established,Closed.
> >>>
> >>>But actually in this example interface for a Closed connection does not
> >>>need Close, and the interface for Listen,Established does not need
> >>>Open....
> >>
> >>The interface of the connection object is not supposed to change.
> >>
> >
> > but closing a closed port is meaningless or erroneous... so in this
> > example at least the interface implies behaviour that does not make
> > sense in this context.
>
> *this is the state object's responsability, not the client's*. If you -
> as the author of the object - decide that sending the close message to a
> connection object in Closed state is a programming error, then just
> implement the close() method of the ClosedState object as raising an
> exception.

yes, but why bother, why not erradicate the possibility, exceptions are
bad for a lot of reasons, and in some sense imply a lack of static
information in the code, if we can replace the 'exception' by
statically defining what behaviour is valid and what isn't, then our
code is simpler, easier to understand and more efficient.

> If you decide that it's only meanglingless, just ignore it. I
> don't see where is your problem here. It looks like you want the client
> code to be in charge of the connection state...

yes that is the reality, but the client is in charge of all sorts of
state in this manner, I am wary to believe this is any different based
on a gut reaction (though I do share the same gut reaction).

>
> (snip)
>
> > Q&D?
>
> Quick and Dirty

oooo yes, surely we never do that!

>
> > I come back to....closing a closed port is meaningless, thus ideally we
> > would model it in a way that this eventuality never happens,
>
> Good luck.

I've just done it, it's trivial (given the constraints).

>
> > or at
> > least encourage/force the client code to not issue meaningless messages
>
> Which implies that the handling of state becomes the client code's
> responsability.

yes, but the reality is that this knowledge does exist in the client
code with the state pattern, *it just is not encoded into the
metadata*....

i.e.

IPort port = new Port(1234);
port.Open();
port.Send("hello");

there is coupling between Open and Send, they should be in that order,
if I change the order the code *will compile* but *wont run
'correctly*, but this is not enforced or encouraged by any typed
information.

If we do it the way I suggest

IPortClosed closedPort = new PortStateClosed(1234);
IPortOpen openPort = closedPort.Open();
openPort.Send("hello");

now if I change the order, the code *wont compile*.....because the
state transition that the client code normally has to implicitly track
is now explicitly encoded.

yes the management of port state has moved to the client (in some
sense....because that is where it is determined), but the advantage is
that I have enforced the order in which behaviour can be invoked, and
thus my code is simpler, and the implicit inherent coupling in the
order of how behaviour should be invoked is explicitly encoded....i.e.
if you get the order wrong, the compiler tells you.

If we were to compare two methods that send hello....

------------------------------------------------------------

void SendHello(Port openPortHopefully)
{
try
{
port.Send("Hello");
}
catch(PortClosedException e)
{
throw new SendHelloException(e); // ooo dear programmer error
}
}

class SendHelloException : Exception
{
...more horrible code.
}

------------------------------------------------------------

Against.

------------------------------------------------------------

void SendHello(OpenPortState thisIsAnOpenPort)
{
thisIsAnOpenPort.Send("Hello"); // I know this port is open!
}

------------------------------------------------------------

I think I prefer the second one.

>
> > or more importantly not have to worry about hidden state
>
> This is the goal of the State pattern, isn't it ?
>
> > when this
> > state can be tracked by the type information.
>
> Which 'type information' ? And *who* should track the state ?

the relationships between the states *encoded* in the method
signatures, forces the client to execute messages in a prespecified
order.

the order is encoded in the method signatures.
(if the client follows the rule of throwing away old state...which I
accept is a weakness, but one that could be formalised in a compiler)
then the current valid state transitions from any given state are
determined by the type of the current valid state.

The problem with state pattern is that it merges all the states and
transitions into a single interface, so that
invalid/meaningless/erroneous ones are available to both client and
context.

>
> >
> >>As an added benefice, if you take care of spliting the state/transition
> >>mechanism from the stateful object's class and making it configurable,
> >>you now have a class that can support various state/transitions schemes.
> >>This is a good way to implement flexible, configurable workflows.
> >
> >
> > If I do not need it to flexible, then why write it to be flexible?
>
> If you don't need it, then don't use it !-)

OK, so there are many problems where the state pattern gives
unnecessary flexibility at the cost of a loss of statically encoded
state transitions information encoded in method signatures?...i.e. you
agree, my alternative may well be better for a large subset of the same
sort of problems?

>
> > I am
> > arguing that maybe the state pattern makes a fixed scenerio
> > unneccesarily flexible
>
> Like all of the GoF patterns, the main goal is to add flexibility. Don't
> use it until you need it.

OK, but I believe there is a preferable half way house in client
determined state transitions.

>
> > and the cost of that is that the client code
> > needs to track or query encapsulted state.
>
> I beg your pardon ? The client code doesn't know zilch about how the
> server object is implemented.

The state objects are encapsulted, but the current 'state' of the
object cannot be.

it knows everything about contracted behaviour so......
and it *has* to know about when state transitions occur in order to
manipulate the object in a useful way....i.e. it needs to know a port
is open, in order to send data, so it needs to know that open, opens
the port.

// close port if IsOpen == true else exception.
void Close()

So the programmer has to be aware that a port is open in order to
bother writing the code to close......sloppily he may go

if (port.IsOpen())
port.Close();

preferably I would expect the code to be formed in such a way that it
must be open, but a single port interface obscures this knowledge or
forces you into a "tell don't ask" strategy.......so the client needs
to know how state transitions occur...i.e. close means the port is
closed, and this is communicated in the contract....while the specific
state of an object is tracked an encoded in the client code.

>
> >
> >>>so why bother letting the programmer believe that they are
> >>>valid/useful, and have him try to logically track them in his head.
> >>
> >>"Tell, don't ask".
> >
> >
> > exactly..
> >
> > tell the programmer by omitting method calls that are meaningless.
>
> Nope. It's not "tell the programmer", it's "tell the object, and let him
> handle the message"

Oh right,

but again I agree...

openPort.Send("hello");

is infinitely better than

if (port.IsOpen())
{
port.Send("hello");
}
else
{
throw new Exception("oh dear I was expecting the port to be open");
}

>
> > i.e. capturing
> >
> > "you can only open closed ports"
> > "you can only close open ports"
> > "you can only send messages down open ports"
> >
> > in type information rather than you can do anything to anything, but it
> > may be meaningless or throw errors.
>
> This requires that the client code has to *ask* the current state before
> *telling* something.
?

IOpenPort openPort = closedPort.Open();

in what manner is the client asking for current state?

the client is explicitly being told, "if you open a closed port, you
get an open one".

For the state pattern the would either encode this implicitly in the
order in which methods are invoked, or it would have to *ask*

i.e.

port.Open();
// now I know the port is open....the state of the port is *not*
encapsulated,
// and I am assuming I understand it's internal state transitions i.e.
they
// are exported in the contract.
port.Send("Hello");


or

// OK I wont assume it's open so I'll *ask*
if (port.IsOpen())
{
// actually I am at this point assuming its open
port.Send("hello");
}


> This makes the client code in charge of handling
> state of the server object. This is the exact opposite of "tell, don't ask".

"IOpenPort openPort = closedPort.Open();"

I don't understand how this is tell don't ask.

the opposite seems true, the state patterns forces you into a "ask,
don't know" approach.

.



Relevant Pages

  • Re: Trend CSM 3 config problems
    ... and Internet Explorer opens up, I get the same port 4343 error message on ... the client as well. ... proxy error about the need for port 4343. ... I created an exemption in Internet Explorer. ...
    (microsoft.public.windows.server.sbs)
  • Re: interfaces lo:1 lo:2 lo:3? (for remote ssh tunnels)
    ... shouldn't use a local interface. ... remote tunnelling port X to client ... >able to seize that port of that interface address. ...
    (Debian-User)
  • Re: 30% Discount for all RealThinClient products
    ... GoOut/Accept (only Remote Control opens a Port) ... LetIn/Connect (only Remote Client opens a Port) ... There is no built-in NAT support, but since RTC Remote Tools can work using ...
    (borland.public.delphi.thirdpartytools.general)
  • Re: Socket Looking For Netbios Name
    ... The server only has one interface, ... Everything else is port ... The problem is with the client. ...
    (comp.os.linux.networking)
  • Re: RPC over HTTPS Exchange 2007
    ... I had to open ports 6000 through 6004 from our Exchange server to our Domain ... I configured a client on the LAN and connected up using ... tries to authenticate to our Exchange and opens an enter username and ... the request bouncing off my firewall on port 135. ...
    (microsoft.public.exchange.admin)