Re: Text terminal rendering design



Responding to Guild...

(Also note that there is no generalization in the GoF Facade
pattern structure.)


I am aware of that. Are you intentionally suggesting that no instance of the Facade pattern should have a Facade class that is a subclass of some other class? The GoF don't seem to say I cannot do it that way. In general for patterns, when a pattern does not show relationships that the participants have with classes outside the pattern, it does not mean that no such relationships should exist in a particular instance of the pattern.

Conceivably a Facade could be subclassed when dealing with multiple interfaces to different subsystems. But that subclassing would of the /interface/, not the implementation.

The entire Terminal generalization is an implementation artifact. You are exposing that artifact in the subsystem interface directly to clients. The fact that you have to hand the client a Terminal object that provides a specific implementation of the [Terminal] responsibilities for the actual hardware in hand is a direct demonstration that the [Terminal] generalization is an implementation artifact. The is-a works both ways: a PDCurses object is a Terminal object and the Terminal object in hand is a PDCurses object.

What you defined was a generalization relation with Terminal as
the superclass whose responsibilities directly implemented the
subject matter solution. All I can suggest is that you pick up any
OOA/D for an explanation of generalization and its implications.
There is nothing more I can say on this issue that won't be in any
OOA/D book.


As a practical matter, you could comment on the example I carefully constructed to illustrate my point and why it so totally failed to convince you. I am sure I would not find that in any OOA/D book, at least not for my particular example. On the other hand, if you do know of a book that deals with a similar example, I would be happy for a reference to that book.

I admit that you have far greater experience and theoretical knowledge, and perhaps you know things about the implications of generalization that I cannot immediately see, but in this case I really don't need to see any implications of generalization. I only need to see one implication that generalization does not have: it does not cause coupling in my design.

But I have already identified several ways that the coupling is manifested (e.g., you need to change the superclass name, you need to delegate display(...) to another class, etc.). Your answer was that they could easily be fixed by introducing a real Facade that kept the [Terminal] class name. The fact that you would have to do that at all to avoid changing the Client implementation says that the coupling already exists in your design.

I have proven that there is no coupling in the only way that really matters, by practical experience. I know there is no coupling simply because I can change my design in any way I like without changing the client. If your theory says I can't, then your theory must be wrong just as any theory must be wrong if it contradicts empirical facts.

I'm afraid you have not proven that at all. All you have proven is that inclusion polymorphism can substitute /implementations/ of responsibilities transparently to clients. The coupling exists because the entire [Terminal] generalization is an implementation artifact of your solution.

If you remove the PDCurses subclass and have Terminal.init invoke PDCurses.init in a standalone [PDCurses] object, THEN [Terminal] is a Facade for the subsystem. But so long as PDCurses is a subclass of [Terminal], then [Terminal] is an implementation of the subject matter.

Think about this. Suppose the only terminal type you needed to support was a simplistic text terminal and you chose to implement it with PDCurses. Then you would not need subclassing for terminal flavors. If you don't need subclassing, you don't need a superclass. Now you have only one class that happens to implement with PDCurses. You would probably name that class PDCurses, just like the subclass in the current [Terminal]. Do you now see that it would be a Bad Idea to make the PDCurses interface the subsystem interface?

No matter how many books I read that tell me that I should have coupling; it will not cause coupling to appear where there is none. Cover my desk with books from the finest OOA/D scholars and I will still be able to radically change my design without the slightest change to my client.

The coupling exists so long as [Terminal] is an implementation object. It is an implementation object so long as it is a generalization whose subclasses implement subject matter rules and policies.

You seem to regard a superclass as something that can be divorced from the objects that are are defined by the generalization. That is not possible. A superclass defines a set of objects and the specific responsibilities that those object have in common. The subclasses provide implementations for those responsibilities. If those implementations implement subject matter rules and policies, the generalization itself is an implementation artifact, including the superclass.

On nonfunctional requirements:

[...]

If you have to get into issues like kerning on a high-end graphics
terminal, it might be a problem if all you have is 20 us. But I
would wait until a profiler demonstrated that.


But isn't it good design practice to create a design that is flexible enough to easily accommodate whatever the profiler might say needs to change? In order to do that, whenever something is time critical I should avoid a design that forces me to do work that is technically unnecessary at that moment, even if I strongly suspect that I will have plenty of time to spare when everything is implemented.

The initial design is around functional requirements and those won't change if there is a performance problem. If a performance problem is demonstrated, then one has a new design problem. That new design problem will be constrained by the structure needed to resolve the <unchanged> functional requirements, but it is still a new and largely orthogonal problem.

Just having a nonfunctional requirement does not mean you have to immediately design a solution for it. There is only a need to design a solution if the initial design to resolve the functional requirements fails to meet the nonfunctional requirement.

It also becomes a time contraint for an editor when a paragraph
is filling the screen and the user is typing new words into the
top of the paragraph. In this case, the entire screen is changing
but not scrolling. I have to perform (A) 2000 times, which is my
biggest worry about how trivial (A) really is. Something that is
trivial when done once is not necessarily trivial when done 2000
times.

But the rest of the screen is already in video RAM. All that is
being added is what the user is typing and that takes so long the
user is never going to notice the conversions.


But if you think about the example I described, you might notice that even though the screen is in video RAM, that RAM needs to be updated with modified contents. Perhaps I have not described it properly, but when I type at the top of a paragraph it is not merely inserting new letters in one little corner of the screen; it has a cascading effect down the entire screen. The video RAM will not help me with that; I need to get the new layout of the text from the client.

But you are still only adding a relatively few characters that need to be converted, not all 2K that are currently visible.

If you load all the terminal-ready Symbols from a DB at startup (or
they are linked in), then they are fixed so you don't need a
collection that if efficient at adding/removing entries and can
focus on best search performance. In any event, the differences
between them will be relatively small.


It seems I have also failed to properly describe why I cannot load all the terminal-ready symbols at startup. Let me try again from a different direction.

The client has some problem space concept that needs to be displayed as a symbol on a 2D array of symbols on the screen. We've been calling that concept an Icon for lack of a better name and I imagine a client with an actual class called Icon for such objects.

Ideally, the client would be able to send my subsystem an Icon and target it at some coordinates on the screen and my subsystem would draw it. In order for that to happen, my subsystem needs some way of finding a terminal-ready symbol for each Icon that it is given. I can see two ways to do that: (A) I can use the value of the Icon object, whatever the client has stored in that object to represent the semantics of the icon, or (B) I can use the identity of the Icon object as represented by some value that only this Icon holds, such as its position in memory.

The data structure that I need does not hold just the terminal-ready symbols; alone those are useless. Actually, the data structure is a mapping from Icon values to terminal-ready symbols, whether I use (A) or (B). While it is relatively easy to know all terminal-ready symbols at startup, knowing all the icon values at startup presents some problems.

If I use (A), I am coupling my subsystem to the client representation of Icons, which is surely not a good thing. I am also forcing it to announce all of its icons in advance of starting.

If I use (B), I am not coupling my subsystem to anything, but I am forcing the client to announce all its icon identities in advance of starting, which seems even more problematic. They cannot know the location in memory of each Icon object before starting, especially if they ever want to dynamically allocate or delete an Icon. So memory location cannot be used as identity, even though that is a popular option. In practice I expect I will be forcing the client to keep track of a special identity value for each Icon object, for the sole purpose of keeping my subsystem happy.

I don't see anything in the explanation that precludes eliminating /all/ conversions by having the terminal-ready Symbols already in memory. Having them already resident actually simplifies the processing by eliminating the (A) vs. (B) decision. IOW, (A) and (B) go away and there is only one processing path: mapping the client's icon identity to the terminal-ready Symbol in memory and loading it into the hardware at the right coordinates.

[The DB's terminal-ready Symbols can supply all the defaults. Then if the client provides some additional customizing information about background color and whatnot, that just modifies the default data on a "current Symbol" copy of the defaults from the memory resident Symbols.]

Probably the biggest single factor will the form of identity of
the icons. If you can possibly avoid it (i.e., you have some say
over how the client identifies icons), you should avoid lengthy
ASCII keys or compound keys. Since ASCII and unicode have
convenient consecutive integer keys I would do that lookup with a
simple array and the search becomes O(1).


Unfortunately, the client will want more than mere ASCII or Unicode. The client will also want to specify color, at least. I want to support pure ASCII, but that is not the expected use for this subsystem.

Fine. But those are just attributes of the terminal-ready Symbol. The one in memory provides all the defaults for the terminal de jour. If the client provides customization, one just copies the terminal-ready Symbol to make it "current" and overrides the defaults with whatever values the client actually supplies.

If the symbol to be displayed is a true icon, that identifier is
likely to be ASCII so I would keep that lookup separate. Then all
you need to know is whether the identifier in hand is for a
character or an icon. Since the client would naturally know that
you can insist that the client supply that information with the
request (e.g., calling displayCharacter or displayIcon).


That seems reasonable, but how am I supposed to figure out what should be displayed when I am given nothing but a mere identifier? Worst of all, it is a client chosen identifier, so my subsystem has no means of determining what the identifier means, and surely you expect the subsystem to treat it as a handle.

Then you can't display it no matter how you implement the UI. There has to be an unambiguous identity mapping between the client's view and the display view.

I suppose you are assuming that all the mapping information between identifiers and symbols is loaded at startup, so it is not an issue. However, in real life I cannot reasonably expect to load all the terminal-ready symbols at startup, despite the usually small number of available symbols for any given hardware.

Not necessarily. As I explained, for a true graphical icon you need two things: a mapping to pure text characters to use on a dumb text terminal and a way to find a representation of the <arbitrary> graphic details in a library that the <graphics> terminal in hand understands. Either way, the identity the client defines must map unambiguously to both of those data stores.

The only thing you can load at startup are text-based symbols and a lookup table to map graphic icon identity into text characters. Any pure graphic icons are going to live elsewhere as terminal-ready Symbols when the terminal supports graphics. You don't even have access to the internals unless you are prepared to parse the graphic format, which would make your UI a graphics engine for a graphics pane. But you still need a mapping of client identity to find them wherever they live.


In practice, I will need the client to somehow describe each symbol in messages, and some handle that my subsystem cannot analyze does not seem helpful with that.

Why? The client just needs to supply an identity that you can map into a terminal-ready Symbol in memory (text) or a library (graphics). I wouldn't expect the client to know anything at all about the rendering of a graphic icon on a graphics terminal. Handling those display details is why the client hired the UI subsystem.

If you keep all the terminal-ready Symbols in memory, then things
like frequency of access are not going to matter. That stuff only
becomes of interest if you can't keep all the Symbols in memory at
once. And for ASCII/unicode all you need is a single index lookup
in an array.


I agree, but it is far more likely that ASCII/unicode will be the symbol value that I send to the hardware than the values that I get from the client. In other words, ASCII describes the values stored in the array rather than the index.

I don't follow. ASCII provides consecutive integer identifiers 0..255. The attributes one needs to load the character into the hardware live in a terminal-ready Symbol in an array that you index into with the ASCII identifier. For a really dumb character terminal you might need /only/ the character code so there wouldn't be any terminal-ready Symbols. But other than that trivial case, I would think there is significant default information in a terminal-ready Symbol.

The character or icon identity that the client provides must be
unique. There may be different messages that carry an identifier,
but they all go to the same table lookup.


How exactly do you picture the icon identity being used? Would it be something as simple as 'display(x,y,iconID)'? In that case, how is my subsystem to know the meaning of the identifier? It seems more likely that I would be getting messages of the form

display(x,y,complexValueDescribingAllTheDetailsOfTheDesiredSymbol)

What kind of details will the client know about the display that can be customized? You only need the display details to customize text characters in the event that are going on a high end graphics terminal. I would expect those properties to be pretty limited.

For true graphic icons, the client would only specify the identifier and you would look up the representation. Since a graphic icon format (GIF et al) specifies /everything/ about the icon display, there is no reason to customize it. (If the client is creating new icons or modifying existing ones in a WYSIWYG mode, then that is a graphics application and requires a whole different sort of graphics pane rendering.)


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



.



Relevant Pages

  • Re: Text terminal rendering design
    ... change to my client. ... a client with an actual class called Icon for such objects. ... finding a terminal-ready symbol for each Icon that it is given. ... should be displayed when I am given nothing but a mere identifier? ...
    (comp.object)
  • Re: Text terminal rendering design
    ... The Terminal generalization is abstracted from specific facades that could be given to the client to handle messages. ... It could be that the nonfunctional requirements that you are advocating my subsystem resolve are so completely trivial because takes no time at all. ... tracking terminal-ready Symbols in memory after they are initially ... If the symbol to be displayed is a true icon, that identifier is likely to be ASCII so I would keep that lookup separate. ...
    (comp.object)
  • Re: Text terminal rendering design
    ... I expect that the real facade class would always be a subclass of the class shown to he client in practice, especially if this facade is acting as a subsystem interface. ... But I don't see how having terminal-ready symbols already in memory helps in eliminating conversions. ...
    (comp.object)
  • Re: Text terminal rendering design
    ... I expect that the real facade class would always be a subclass of the ... class shown to he client in practice, ... acting as a subsystem interface. ... But I don't see how having terminal-ready symbols already in memory ...
    (comp.object)
  • Re: Text terminal rendering design
    ... \\ the client directly, against common wisdom. ... I just tell the client that has a Terminal pointer and let it think it is talking to a facade. ... We want to change the name of the subsystem interface. ... Certainly within the UI subsystem I am free to do whatever I want even with my current design, including the exposed terminal-ready symbols. ...
    (comp.object)