Re: Text terminal rendering design



H. S. Lahman wrote:
The subsystem interface is designed around the client's needs as
best as I've been able to so far, and as it happens that
interface can directly and easily be implemented in the interface
objects themselves. The client will never know the difference;
I'm losing exactly zero hiding by doing it this way.

The client will know the difference as soon as you need to modify
the interface of one of those objects in response to requirements
changes that only apply within the UI subject matter.

You seem to be suggesting that I might need to modify the interface
to a subsystem interface class even though I don't require something
new from the client. Of course that would be a horrible thing, but
why would it ever happen? It seems impossible.

Just because there happens to be a 1:1 mapping between subsystem
interface messages and object method calls does not give one
license to expose those methods.

But surely there is always a 1:1 mapping between subsystem interface
messages and object method calls. How else can the client send the
subsystem messages but through method calls?

So typically the Facade only does exotic things in two situations:
(A) to resolve syntactic mismatches in reuse situations when the
new client wants to see a different interface than the subsystem
provides and (B) to make changes transparent to the client. IOW,
the price of good decoupling is the indirection. The benefit is
realized when the indirection ceases to be 1:1.

Of course indirection is a good thing. (I was shocked to read below
that you are thinking I am using absolutely no indirection.) But once
you have even basic indirection, you can add more indirection within
the subsystem as needed according maintenance.

In other words, no matter what methods the client is invoking, those
method invocations are messages being sent to the subsystem and what
the subsystem does with those messages can be manipulated freely by
the subsystem maintainer.

The only thing that can prevent that would be physical coupling, as
you correctly pointed out, but it would surely be foolish to
physically couple the client and the subsystem together. I am
surprised that you would even consider that.

The Terminal object is how the client issues terminal-related
commands such as "Initialize yourself, I'm about to give you some
symbols to display" or "Shutdown, I don't need you anymore," or
"I'm doing giving you symbols for a moment, display what you
have."

Who is this client?

The client is a piece of software that has things to display to the
user in the form of an array of symbols.

Why would it need to know that icons were displayed on terminals or
that terminals needed to be initialized?

That depends on what you take the word 'terminal' to be. I take it to
be nothing more than an array of symbols. The client needs to know
that because I cannot think of any way more abstract than that to
describe it. If the client didn't even know the symbols were being
displayed in an array, how could it provide coordinates?

I suppose that it doesn't really need to know that terminals need to
be initialized, but as it happens they do. The initialization message
is mostly just a courtesy to the subsystem to help it provide an
efficient implementation by not having to guess too much about what
the client will do.

The same applies to 'refresh' as I have talked about in earlier
posts. The terminal could work without a 'refresh' message, but it
wouldn't work as well.

Why would it make any difference to the client if the icons were
displayed on a computer or with Navajo smoke signals?

I rather think that it wouldn't make any difference at all. In fact,
that is the point of all this abstraction that I am introducing and
the object-oriented design. If I wanted the client to be forced to
use one particular implementation I would just have it use PDCurses.

Clearly your UI software exists so that the client can be
indifferent to what particular terminal the display is on. What I
am arguing is that the client shouldn't even care if the display
is on a computer. It just wants the icon displayed.

I understand and agree with you on that point. What I don't see is
why you think I am harming that sort of flexibility. Although your
ideas about physical coupling suggest one rather unexpected
explanation.

If your client directly accesses the objects that implement your
subsystems then there is no isolation, there is no encapsulation,
and there is no decoupling of implementations. None. Nada. Zip.

That is amazing! And where did all the wonderful encapsulation that
all objects naturally have disappear to?

And that is just the coupling issues. What about the level of
abstraction? How can the client focus on how an 'icon' relates to
the customer's problem if it also has to get mired in the minutia
of initializing terminals?

There really isn't much minutia, just a single message with no data
that tells the subsystem that symbols will be arriving for display
soon. That's not my fault for allowing the client access to
implementation objects; that's my fault for thinking that the client
wouldn't mind sending a message of that sort to the subsystem.

It is completely unexpected how you suddenly assume that just because
a few implementation objects are exposed that the entire
implementation is suddenly exposed to the client, and worse, that the
client is forced to deal with it. On the contrary, none of the
implementation is exposed to the client.

But I suppose what you are really saying is that the id should be
supplied by the client as the clients representation of the
symbol and then the UI subsystem is responsible for figuring out
what the client means by that id. In other words, the client
gives the symbol IDs which are the clients natural representation
of the symbols and the client somehow communicates the necessary
conversion procedure through messages.

Exactly -- up to the "necessary conversion procedure". That
procedure lives in the UI software alone and the client doesn't
need to know anything about it.

Except that if the client doesn't provide the conversion procedures
then the subsystem has no way of knowing what to do with a value that
the client has given. The value has no semantics for the subsystem
unless the client explains the semantics.

What if we want to do some highlighting and draw a green A? We'd
better not call factory.setColor('A',green), because that would
turn every 'A' on the screen green. As the client, we would have
to come up with a new identifier for a green A.

If the client is some sort of WYSIWYG display designer we are
really in a different ball game. I was assuming the client just
had stuff to display in certain positions and was relying on the
UI subsystem to map that stuff into particular terminals. In that
case I would expect some set of defaults to be in place and the
client could override them.

Yes, I am afraid that it is a difficultly with the subsystem
interface. The client will naturally want to have a way to display
text without worrying about the font or color sometimes, while other
times controlling it with fine detail.

But if the client is talking to Terminal and SymbolFactory
directly, it necessarily knows how the display is done.

This sounds immensely bizarre. I know that I said that the facade was
both exposed and hidden in a previous post and that sounded strange.
What you've just said above sounds just as strange. I invoke methods
all the time without knowing how they do what they do.

Just knowing the classes reflects some knowledge of the
implementation.

What does "knowing the class" mean exactly? Does it mean knowing the
implementation of the class? In that case what you are saying is of
course true, but the client has no such knowledge. Does it mean
knowing the public methods of the class? In that case the client does
have that knowledge, but what you are saying is clearly false.

If the display was smoke signals then the relevant objects would
be Fireplace, Blanket, and SignalTimer instead of Terminal,
Symbol, and SymbolFactory.

Terminal and SymbolFactory are abstract classes which are meant to
have various implementations. Some of those implementations could use
smoke signals. Why not?

Note, though, that all the client sees is Compile (input, output).
It has no knowledge at all of Scanner, Parser, and whatnot.

Considering all the rest of the things you have said in that post, I
am surprised that you think that anything at all is hidden from a
class that has access to the Compile method.

The facade always hides the implementation objects, even if the
Facade is one of those implementation objects.
[...]
Please take a look at that first sentence again. B-) It's an
oxymoron. If the Facade is one of the implementation objects, how
can it hide itself?

I suppose I was speaking figuratively, but I stand by it. The client
knows that the object exists, but the client cannot know what the
object does. In that sense, the object is hidden. It is opaque, a
black box.

More to the point, during maintenance any one of those objects,
relationships, and collaborations can change without changing the
clients' interactions with that subsystem. You can delete objects,
add objects, change the names of objects, modify message data
packets, and a host of other things within the subsystem so long
as the DbC contracts with the clients are still satisfied. But you
can't do any of that if the objects that you are modifying are
exposed to the client because that will break the client
interaction.

That just means that the changes have to happen beneath the level of
the subsystem interface. The exposed objects can change, so long as
they change in an interface that is not exposed to the client.
Objects that are not exposed the the client can always change freely.
Nothing at all about exposing objects to the client prevents me from
adding new objects that the client cannot see.

Suppose I give you my Terminal class with its 'initialize',
'shutdown' and 'refresh' messages. If I didn't tell you that
those messages were being implemented directly in the objects of
that class, how would you know? What is actually being exposed
here?

To compile the client the header file needs to be available if
Terminal is invoked directly. That header exposes the entire
implementation of the Terminal class, down to offsets to attribute
values and the jump table entries. In C++ just adding a private
variable to Terminal would require recompilation of the client.
Any change to the public interface that client invoked would
require code changes in client.

At last we come to what I think is probably the real issue. This was
horribly shocking for me. Thank you; your posts are almost always a
joy to read.

I really had no idea that you would suppose I would give such a
header file to the client. I'm talking about encapsulation and how I
haven't really exposed anything and you are thinking that I've pulled
down the pants of all my classes like this.

Maybe I'm not putting in as much indirection as you would recommend,
but that hardly means that I am ignoring even the most basic forms of
indirection. Let me describe what I think is a more likely picture of
the header files that I give to the client.

The header file contains a class definition, of course, but it has no
private members. In fact, it doesn't really have any public members,
at least not directly. The members are denoted as being pure virtual,
which means to C++ folk that they are not implemented here. (If not
for your assumption to the contrary, I would have assumed this was
normal for defining classes that interface directly with the client!)

To give the client access to a particular instance of one of my
classes, I will probably have a pointer to that class declared to be
external in the header file. Your header file design seems to be
based around the idea that I am giving the object to the client as a
value, while I actually choose to use the most basic form of
indirection: a pointer.

I hope you will notice that this allows me to give the client direct
access to an implementation object without actually exposing
anything.

It is getting late so I will have to think about the rest of your
ideas later. It's seems a bit tricky to dig the important ideas out
from the ideas based upon your curious guess about my header files!
.



Relevant Pages

  • Re: Text terminal rendering design
    ... Which is exactly what would happen in your case when the interface of the implementation object changes. ... But then you need a new object to own the actual responsibility within the subsystem implementation. ... I can pop in a facade in a completely painless manner without being forced to rename or kludge anything. ... Facade to avoid touching the client. ...
    (comp.object)
  • Re: Text terminal rendering design
    ... subsystem subject matter so we need an interface to encapsulate them. ... The public members are the interface of that object and taken together all the interfaces of all the revealed objects becomes the interface of the subsystem. ... Your client is invoking specific ...
    (comp.object)
  • Re: Help on choosing a valid pattern: composite or not?
    ... But that class would only exist in the UI subsystem in my ... In the GUI subsystem where GfxNode might live I am expressing an entirely different set of paradigms for display and I might have a handy MVC infrastructure to make the code simpler, which is where GfxNode comes into the picture. ... I can go directly to the View from the subsystem interface. ... Let's say I have defined a Facade pattern class for the interface called, for lack of anything better, GUIInterface. ...
    (comp.object)
  • Re: What doesnt lend itself to OO?
    ... The whole idea that a subsystem is just ... > If the clock service has identity then the client looks like... ... The first line exists in the server. ... external interface is the traditional input interface whose ...
    (comp.object)
  • Re: Text terminal rendering design
    ... free to give it any object that satisfies that interface. ... giving it a real facade object if I choose. ... Facade to avoid touching the client. ... completely incompatible with this subsystem interface. ...
    (comp.object)