Re: Text terminal rendering design



H. S. Lahman wrote in news:lDD7i.65778$UD2.8168@trnddc05:

Responding to Guild...

Unfortunately, I do not exactly see the parallel with getters and
setters. Are you talking about the choice between using getters
and setters versus making the data public? That doesn't seem easy
to fix at all, since you would have to edit every place where the
data is accessed.

Which is exactly what would happen in your case when the interface
of the implementation object changes. Every client would have to
change. The only alternative you have is to provide a true Facade
with the same object and responsibility name. But then you need a
new object to own the actual responsibility within the subsystem
implementation. What do you call that, TheOtherSymbolFactory? IOW,
you have to kludge your implementation of the subsystem.

Reading your post, I get the feeling that you are confusing the
implementation and the facade. I'm also completely aware that it is
my fault, since putting the implementation and the facade into the
same object actively encourages that confusion in all who look at the
design. On the other hand, if you understand the situation then it is
all perfectly simple. (So simple that it is trivial and hardly worthy
of discussion. If I had seen what an issue this would become, I would
never have mentioned that I was doing this, but it is good that I now
know.)

You're saying that in order to introduce a facade I will be forced to
change the name of the implementation object, but that is simply a
product of our misunderstanding. I can change the name of the
implementation object freely without affecting the client, but I can
also keep it the same.

The trick is that the facade object doesn't actually need a name.
It's class needs a name, of course, but not the actual object, at
least not one that the client will be told. The fact that one facade
has been replaced by another that happens to implement the same
interface is invisible to the client, without even any naming issues
at all.

I had hoped that I could explain this clearly in words, but I think I
need a more formal illustration. Try this, in simplified C++ style:

\\ The Facade interface class that the client gets in a header file
\\ All the methods are pure virtual, of course
class Terminal {
init()
shutdown()
display(x,y,symbol)
}

\\ An implementation class for an object that is being given to
\\ the client directly, against common wisdom.
class PDCurses : public Terminal {
init()
showdown()
display(x,y,symbol)
private:
...implementation stuff...
}

Now I give the client a pointer to an instance of PDCurses and the
messages that it sends go through the virtual function table to the
appropriate implementations that do whatever is needed to properly
handle the messages. Note that I don't tell the client that the
pointer is actually a pointer to a PDCurses object. I just tell the
client that has a Terminal pointer and let it think it is talking to
a facade.

Now suppose I find that I need a facade for some reason. I snap my
fingers and here it is:

\\ A class for a Facade object to handle the messages
class TerminalFacade : public Terminal {
init()
shutdown()
display(x,y,symbol)
private:
PDCurses *implementation
}

Now I give the client a pointer to TerminalFacade. The client won't
know the difference. There are zero changes required in the client.
And in some sort of amazing miracle, the implementation class
PDCurses doesn't need to change either! The facade keeps a pointer to
the exact same PDCurses object that was handling the message before,
but now there is a facade in the middle, invisible to both the client
and the subsystem.

Now that I've got this facade, PDCurses could change in any way that
I want it to change without affecting the client. It could become
many objects, it could disappear entirely, or any other thing that
you could imagine.

I think you did one of those two things. "Popping in a Facade" is
the second option. But -- at a minimum -- that creates a namespace
conflict so you have to kludge the implementation.

I'm not sure what you mean by a namespace conflict in this context. I
don't really see one in my above illustration, but it might be
subtle. At a minimum, I know that the implementation didn't have to
change, so at least I've got that.

on the specific case of renaming the implementation object:

But we had already determined that name is no longer accurate
when we did the rename, so there is something wrong with that
picture.

But surely there is something wrong with that thinking. After
all, what we actually wanted to rename was the implementation
object, right? If we had wanted to rename the Facade then we are
in trouble no matter what we do. So why would you complain that
the implementation is changing names while the Facade keeps the
old name?

Remember you justified using the implementation object as a Facade
because it was a fundamental responsibility of the subject matter.
If you change the name to match some new semantics and the
original semantics was so important, why isn't the Facade name
changing?

This is where it becomes critically important to keep the separation
between the facade and the implementation clear in our minds. The
very possibility of such confusion is probably a far better reason
for keeping them as separate objects than anything that has been
suggested so far. Still, if we can keep the ideas apart, we don't
actually need them to be separate objects.

When it comes to changing names, there are three possibilities. (A)
We want to change the name of an implementation class. (B) We want to
change the name of the subsystem interface. (C) We want to change the
name of both.

In case (A) it means that we are shifting to a fundamentally
different implementation and we need new classes with different
names, but specifically this change is not connected to the
interface; as far as the other subsystems are concerned this change
is of no consequence. That is why the Facade name is not changing.

In case (B) it means that we have found a problem in the interface,
much like the issues you point out to me in my interface ideas, but
the implementation is still fundamentally the same. In this case all
we need is a new facade class to lay over the subsystem and change
the interface.

In case (C) we have what you seem to be talking about, a fundamental
change in the subsystem responsibilities that affects both the
messages that it handles and the implementation. In that case we are
really talking about an entirely new subsystem with a potentially
completely different design, so maintenance issues are of little
importance.

In an OO context encapsulation and implementation hiding are never
trivial; one should always practice them, even when not practicing
them does not seem to have a high cost.

It is not clear to me that I am violating encapsulation.

The purpose of the paradigm is to get the developer out of the
business of making those judgments because decades of painful
experience has determined that they often end up being wrong.

If their experience was really so painful, then they couldn't have
been doing it the way I am doing it. My way is 100% guaranteed to be
pain-free.

The big maintenance problems will come when you change the design
strategy of the subsystem. In that case your two simple tasks may
become three simple tasks. Is the client going to be responsible
for triggering the third task as well?

Not unless it needs to be. I certainly hope not, but I cannot really
say without knowing the nature of that third task. I cannot imagine a
third task that would serve a purpose in the UI subsystem interface,
but I cannot guarantee that it won't happen.

Will you use a scheme where creating Symbols is unnecessary? Then
SymbolFactory goes away entirely.

That doesn't make sense. Creating symbols is always necessary for any
implementation of the subsystem; it is one of the fundamental jobs of
this sort of UI subsystem. I am not saying that there will always be
a Symbol object in the subsystem implementation, but symbols will
still be created in one way or another. A UI subsystem of this nature
without symbols is like a paragraph without words.

SymbolFactory won't go away. More precisely, the messages that
SymbolFactory accepts will always be accepted because they do not
represent responsibilities of the subsystem. Even if the
responsibilities change, the messages can still be accepted because
they are nothing more than announcements from the client about the
client's needs. If a radical new subsystem implementation wants to
ignore those announcements, it is free to do so without changing the
client.

So I don't see how SymbolFactory disappearing is an issue at all. The
real problems come in when the UI subsystem needs more messages from
the client, not fewer.

If the subsystem's responsibility is simply to display an icon for
the client, then all those problems are irrelevant to the client.
From the client's perspective all that is transparent and you can
change the tasks, modify the sequence in which they are executed,
add and remove objects, and whatnot to you heart's content. For
that transparency all you have to do is define the level of
abstraction of the UI service properly during application
partitioning.

I think I can safely say that is the UI subsystem's responsibility,
or at least very close to it. Certainly within the UI subsystem I am
free to do whatever I want even with my current design, including the
exposed terminal-ready symbols. There are not tasks that I cannot
change or sequence that I cannot reorder, so long as it keeps
fulfilling the subsystem responsibility.

The reason is that subsystem messages are not connected with
responsibilities; they are nothing more than announcements of the
desires of the client. If the client sends me messages in a certain
sequence, I can respond to them in an entirely different sequence.
Why not?

Show me how easy it will be if one of those implementation objects
goes away completely. Or you decide that intermediate conversions
are so important they become a peer task to the initial icon
conversion and terminal hardware control tasks. Or you decide you
need to break up Terminal processing into two steps.

I think my above illustration of how I can freely introduce a facade
object without changing the implementation or the subsystem interface
should make that clear.

Or to address nonfunctional requirements you have make decisions
about whether to use an existing terminal-ready Symbol or create
one.

These nonfunctional requirements are a real killer. In order to make
that sort of decision, my subsystem will have to get dirty with all
sorts of client problem space details. If those details are not given
through the subsystem interface then they have to be built into the
subsystem, which effectively means that this subsystem is only useful
for one client or a small set of very similar clients.

It's quite surprising for me to find you pushing me into that sort of
coupling rather than helping me avoid it.

That will only be easy if you provide a Facade that emulates the
original design, which is now obsolete.

That's odd. It sounds as if you are saying that only my original
design can make it easy to address nonfunctional requirements. That
is my feeling on the issue, but I know you don't agree so I must be
misunderstanding you.

For one thing, I don't understand the notion of a Facade doing
emulation. A Facade is only responsible for taking messages from the
client, not for actually doing anything. Presumably the messages that
the facade accepts are fixed with the interface design and never need
to change, so it hardly seems like an issue.

Then you are hoisted on the petard of your original vision that
those tasks were fundamental public responsibilities of the
subsystem.

The public responsibilities of a subsystem do not change once the
subsystem is complete, just like most public things. I have a feeling
that you are mistakenly thinking that I am making some things public
because I want to avoid using a facade. Actually, all of the problems
you see with my subsystem interface are motivated entirely by my
inability to imagine how the subsystem could be implemented well
without those problems, with or without a facade.

I am not pushing back against your suggestion that I should use a
facade. (I happen to be pushing back against your assertion that the
facade is important for its own sake, but that is an entirely
separate issue). I am fine with using a facade. What I don't want is
to contaminate my UI subsystem with client problem space concerns;
that is the motivation for the 'createSymbol' message that allows the
UI subsystem to avoid the trouble of guessing the future symbol needs
of the client.

The fundamental problem here is that in defining the subsystem
during application partitioning you have hard-wired a particular
design vision into the fundamental subsystem responsibilities.

It really doesn't seem so bad to me. What I am hard-wiring is pretty
fundamental to the nature of the problem, not the sort of thing that
seems likely to ever change, no matter the particular implementation
of the subsystem.

On the other hand, you are suggesting that I hard-wire the client
into the the subsystem, and that seems far more likely to pose a
maintenance issue in the future. Of course, I could modify the
subsystem to work with the new client, but is it so much to ask that
my subsystem could work without modification for a wide variety of
clients?

There is nothing to prevent supporting clients that can provide
lots of additional information as well as those that can't. To do
that you just needs a Facade that provides multiple client
interfaces. Then the Facade simply dispatches to whoever handles
each situation within the UI subsystem so that missing defaults can
be supplied.

That is good advice; thank you. This is really the fundamental issue
for the design of this subsystem: what is the best way to allow it to
work with a wide variety of clients with a wide variety of needs. It
is already intended to be rather generic UI tool for arranging
symbols in arbitrary patterns, perfect for a wide range of
applications. The only difficulty is dealing with all the various
symbol sets that applications tend to use and finding a way to
support as many as possible as easily as possible. Some sort of
system of defaults is definitely called for.

Here [Symbol Manager] takes care of the ugly business of memory
management for symbols for the application. Symbol manager is
exposed to application problem space knowledge that helps it
decide when a symbol is no longer needed, as well as the memory
constraints of the overall software, things that the UI really
should not have to deal with.

Who are the clients and services here? Surely the Symbol Manager
subsystem is not invoking the application?

That's a good question. Since subsystems are sending messages in both
directions, I'm not sure how to decide which is the service and which
is the client. I expect it should look like this:

[Symbol Manager]<------[Application]------->[UI]

If there is a nonfunctional performance requirement on how fast the
UI displays icons, that requirement is clearly the UI's problem.

It looks like the UI's problem on the surface, until we start to
think about it and then realize that in order to solve that problem
the UI needs a range of vaguely defined knowledge that clearly rests
firmly in the subject matter of [Application]. In fact, solving the
problem needs very little knowledge from the UI subject matter
compared to the large amount of knowledge it needs from the
Application subject matter.

I am not sure what I should do about that, but I know what I don't
want to do about it: I don't want to start filling [UI] with
[Application] stuff.

You keep pushing this idea that somehow the client should be
managing objects that UI alone creates.

It would be manipulating them as handles. It's not ideal, but far far
better than exposing client Icon objects into the UI subsystem as
real, understood objects that do not just encode display information
but actually have semantic content from the client problem space that
the UI is now required to understand.

The Memory Manger, if needed at all, will be an element of the
overall design strategy for resolving the UI's nonfunctional
requirements. That will likely require some degree of knowledge
about the client problem space and how clients use the UI. As I
already pointed out, the necessary view of that problem space is
probably not part of the clients' view because the clients are
tailored to a specific problem. At best the clients may already
have some data that might be useful but even then it may not be
accessible.

That all makes sense and seems to be unfortunately true, but just
because the Application subsystem such as Emacs of Vi doesn't have
what I need does not mean that I am forced to put it into the UI
subsystem. The UI subsystem really doesn't want to have to deal with
what it is displaying; it just wants to know what it is supposed to
look like.

Look at this quote from your blog:
The second most important characteristic of subject matter is
cohesiveness. A subsystem encapsulates a particular collection of
knowledge and behavior. The elements of that collection should be
intimately related within the context of the problem being solved.
In general I would prefer to err on the side of identifying too
many subsystems rather than too few when trying to identify the
boundaries of cohesiveness. The more narrowly that subject matter
is defined, the easier it becomes to provide generic interfaces
that will be invariant as the details of the implementation change.
As a fairly general rule it is easier to combine trivial subsystems
than it is to subdivide complex ones once there is substantial
design investment.

So I see that the Application doesn't have what the UI subsystem
wants and the UI subsystem shouldn't violate cohesion by trying to
figure out the application's subject matter to get the information
itself. The obvious answer suggested by the above quote is to create
yet another subsystem, one who is responsible for understanding the
needs of the Application even when the Application does not
understand them. It is better to have too many subsystems than too
few, right?

So the design strategy for addressing nonfunctional requirements
is pretty much up to the UI designer.

Unless I create another subsystem and put those responsibilities
there.

If the understanding of the client's problem space is limited, then
the design strategy will be limited in proportion.

I suppose the real issue is that the understanding of the client's
problem space is my decision. I could create this subsystem for one
client only and give myself total understanding of the client's
problem space, but your blog recommends reusable subsystems and I
strongly agree.

At best all you can do is provide some exotic learning process that
will monitor the clients' actual access and build a strategy over
time.

I am not so certain that is the best I can do. Why isn't my plan of
putting a subsystem between the Application and the UI subsystem
another option?

I doubt that it is possible to create something smart enough to build
an effective strategy without having more client knowledge than I
would like to expose the UI subsystem to. I could devise some complex
scheme for it, but then I face the other problem: this scheme would
need to be working right when the nonfunctional requirements say that
every clock cycle is critical. I don't want to be looking things up
in complex data structures, even very efficient ones, unless it is
absolutely necessary.

Now I have been inspired by the thoughts you have suggested about
Emacs into creating this third alternative:
[Application]------[Symbol Manager]------[UI]

I should probably specify arrows for this little diagram. I'm not
really sure what is best, but I guess it should look like this:

[Application]------>[Symbol Manager]------->[UI]

This is classic syntactic mismatch common in large scale reuse.
The interface that Emacs wants to use is an output interface in
the Bridge Model (that I talk about in the Application
Partitioning category of my blog).

I didn't actually find anything about a Bridge Model in that part of
your blog.

Bottom line: syntactic mismatches between the client and the UI
can be handled completely in the bridge between the subsystems.

However, you should notice that we have a bigger problem to solve
than just a syntactic mismatch.

Therefore, I propose that Symbol Manager be put in the middle as
an Adapter, but for subsystems rather than for classes. On one
side of Symbol Manager we have an interface much like what you
have been suggesting, but even more tailored for the specific
application. On the other side, we have an interface like I have
been suggesting for a UI subsystem that doesn't know what it is
appropriate to keep terminal-ready symbols in memory and when to
recreate them later.

That's fine, but then it is just a Facade wrapper for the client's
preferred output interface and doesn't need to know anything about
managing the UI heap.

It doesn't need to know, but someone needs to know. I have to define
that responsibility somewhere, and this is the only place where no
one has pointed out any problems. That responsibility seems to fit
quite nicely here.

Is there some difficulty that you see with that? Unless putting that
responsibility in this wrapper will cause some problem that is at
least as bad or worse than the troubles of putting it in the client
or the UI subsystem, then it seems to be the best choice.

If different clients have quite different profiles of usage, you
are not going to be able to address nonfunctional requirements
uniformly well. If you need a high degree of optimization, then
you have two choices. You can differentiate your product market by
servicing only a selected group of clients (e.g., character
editors and word processors) whose problem spaces are quite
similar. Alternatively you can serve the general market by
providing different implementations (e.g., DLLs) for the UI for
different groups of clients with similar problem spaces.

Neither of those two options is ideal. I have plenty of reason to
want to look for something better, so unless you can suggest some
problem with the third option that I suggested, why would you expect
me to accept that there are only these two options?

On display problem spaces:
[...]
My notion of Terminal does not contain the word 'computer'. I
have abstracted the concept of a Terminal from what it is often
used to mean to just its essential features. These features could
be awkwardly emulated by smoke signals. It would be very awkward,
but it could be done.

I still contend that any common notion of 'terminal' in a display
context is not something you will readily find in a smoke signals
problem space. The goal is to map the problem space naturally and
intuitively, not awkwardly.

The awkwardness of smoke signals comes directly from the fact that my
abstraction uses two dimensional coordinates and smoke signals are
fundamentally a one dimensional medium. But now that I think about
it, that is not really as awkward as I imagined. All we would have to
do would be to output the symbols one line at a time with some sort
of 'STOP' symbol at the end of each line and a 'CLEAR' symbol to go
back to the top-left corner.

On the other end, Emacs expects to use a terminal. I see no
awkwardness there.

I guess I must admit that I don't really understand your point. The
Facade has got to have some name and that name is never going to
perfectly fit with all possible implementations if those include
smoke signals and computer terminals. Complaining that 'Terminal' is
a bad name for a smoke signal interface is not going to get us
anywhere.

We are talking about the subsystem interface, not the
implementation. A notion of Terminal that could be implemented by
smoke signals actually reduces the need for change by the
developers when the problem space changes, such as if we lose our
physical terminal and are forced to use smoke signals.

Actually I think we are. Terminal is fundamentally an
implementation abstraction. You are just trying to make it serve
double duty as a subsystem interface as well.

No, I actually think it works as an interface abstraction. The fact
that it is also an implementation abstraction is just a feature of my
most recent implementation attempt. Why would I be trying to make it
serve double duty when I obviously have so many more important goals
to achieve first?

Your whole argument here is that you /can/ use implementation
object interfaces as the subsystem interface while I am arguing
that you shouldn't.

That is certainly not my whole argument. I tried in a previous post
to separate out two very distinct issues, but it seems I have failed.
That argument is really rather trivial compared to the bigger issue
of what the subsystem interface should look like.

All this stuff -- one could argue the entire OO paradigm -- plays
together to ensure that the client doesn't care anything about how
the subsystem is implemented. So if this is a big surprise, then I
have utterly failed in trying to make my case.

You can rest assured that none of those things you listed are any
surprise to me. The thing that is curious to me is why you think you
need to argue those things to someone who certainly agrees with them.
I have tried to make it clear with the example at the top of this
post how my design does not violate any of those important OO design
ideas, at least on the issue of facades versus directly exposed
implementation objects.

That would be fine if Terminal really were a Facade (though it
would be an awful choice of name). But your Terminal is not a
Facade; it is an abstraction that is required to implement the UI
subject matter.

No, it really is a Facade, and I guess it really is an awful
choice of name. I don't particularly like UI either, though.

Then what object is handling the actual rendering of a Symbol to
the hardware within the implementation?

Some other object. With all we have discussed, I am having to start
the implementation design from scratch, so I really don't know the
answer at this moment.

If you are using Terminal for the subsystem Facade at that high
level of abstraction, then why do you also need SymbolFactory in
the Facade?

I am surprised that you would ask me that! After all our discussion,
I thought I had made my reasons for wanting to receive a
'createSymbol' message from that client as clear as they could be,
and I thought we both knew that you didn't think my reasons were good
ones. I have no problem with giving it another try in another way, in
the hopes of making it more clear, if it is really necessary.

The purpose of the SymbolFactory facade is to take the client's
abstract view of a symbol in such a way as to greatly simplify the
nonfunctional requirements of the UI subsystem. It allows the client
to specify symbols in advance of when they should be displayed so
that the complex conversion from the abstract symbols given in the
messages to the required concrete symbols can be performed in
advance.

It supplies the client with a handle onto that concrete symbol so
that when the client actually wants the symbol to be displayed it can
happen instantly, with minimal computations.

Without the SymbolFactory facade or a similar facade, the UI
subsystem would be absolutely forced to perform the conversion
calculations at the moment a symbol is displayed for the first time,
which is violating nonfunctional requirements, and then when the
symbol is displayed again the UI subsystem would have to recognize
the abstract form of the symbol as something it has seen before and
then perform some sort of table-lookup to by-pass the usual
calculations, as well as managing the size and contents of that
table.

The way around this is to have the UI subsystem know in advance what
symbols the client will be using, but that requires the UI subsystem
to be tailored to the client's problem space and that is
inappropriate for reuse.

Perhaps "need" is too strong a word, but that is why I want a
SymbolFactory in my facade.

I am suggesting I can design your UI without a Symbol abstraction.
I'm not talking about just renaming it to Icon or DisplayData; I'm
talking about a design that doesn't need that notion of 'symbol'
at all.

Maybe you can design the subsystem without a symbol abstraction, but
you cannot do it with concrete symbols.

For example:

[Screen]
| 1
| organizes [Message]
| A
| R1 <<ordered>> | R3
| +-------+-------+
* | 1 R2 derived from 1 | |
[Cell] ---------------------- [WithDefaults] [Raw]
| | 0..1 | 1
| | derived from |
[Converter] | R4 |
A +------------------+
| R5
...

The closest thing here to a Symbol is [Cell] but the semantics are
quite different because it is the actual video RAM image.

It sure looks like a symbol to me. It is an object that represents
the hardware data needed to display a symbol. What else did you
suppose the semantics of Symbol were?
.



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: 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: 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: Newbie Modelling Interface Question
    ... >>different interfaces for different client contexts, ... >>Facade wrapper, ... How do these approaches expose the subsystem ... > point of an interface was to prevent exposing the implementation. ...
    (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)