Build user interfaces for object-oriented systems, Part 2: The visual-proxy architecture

A scalable architecture for building object-oriented user interfaces

1 2 3 4 5 6 7 Page 6
Page 6 of 7

Standard output

The final loose end is the Std class that I use to write to standard output Listing 9. The Std class is a factory that generates Singleton instances of the readers and writers that you need to access the console. I discussed this in depth in a previous article, but I've included it here for the sake of completeness.

Listing 9: Std.java
001  package com.holub.tools;
002  import java.io.*;
003  import com.holub.asynch.JDK_11_unloading_bug_fix;
004  

/*****************************************

Convenience wrappers that takes care of the complexity of creating Readers and Writers simply to access standard input and output. For example, a call to

    Std.out().println("hello world");

is identical in function to:

    new PrintWriter(System.out, true).println("hello world");

and

    String line = Std.in().readLine();

is identical in function to:

    String line;
    try
    {   line = new BufferedReader(new InputStreamReader(System.in)).readLine();
    }
    catch( Exception e )
    {   throw new Error( e.getMessage() );
    }

Equivalent methods provide access to standard error and a "bit bucket" that just absorbs output without printing it.

All of these methods create "singleton" objects. For example, the same PrintWriter object that is created the first time you call Std.out() is returned by all subsequent calls. This way you don't incur the overhead of a new with each I/O request.

@see com.holub.tools.P

@see com.holub.tools.R

@see com.holub.tools.E

*/
005  public final class Std
006  {
007    static{ new JDK_11_unloading_bug_fix(Std.class); }
008  
009    private static BufferedReader input;        //= null
010    private static PrintWriter    output;       //= null
011    private static PrintWriter    error;        //= null
012    private static PrintWriter    bit_bucket;   //= null
013  
/*****************************************
A private constructor, prevents anyone from manufacturing an instance.
*/
014    private Std(){}
015  
/*****************************************
Get a BufferedReader that wraps System.in
*/
016    public static BufferedReader in()
017      {   if( input == null )
018              synchronized( Std.class )
019              {   if( input == null )
020                      try
021                      {   input = new BufferedReader(
022                                          new InputStreamReader(System.in));
023                      }
024                      catch( Exception e )
025                      {   throw new Error( e.getMessage() );
026                      }
027              }
028          return input;
029      }
030  
/*****************************************
Get a PrintWriter that wraps System.out.
*/
031
032    public static PrintWriter out()
033      {   if( output == null )
034              synchronized( Std.class )
035              {   if( output == null )
036                      output = new PrintWriter( System.out, true );
037              }
038          return output;
039      }
040  
/*****************************************

Get a PrintWriter that wraps System.err.

*/
041    public static PrintWriter err()
042      {   if( error == null )
043              synchronized( Std.class )
044              {   if( error == null )
045                      error = new PrintWriter( System.err, true );
046              }
047          return error;
048      }
/*****************************************

Get an output stream that just discards the characters that are sent to it. This convenience class makes it easy to write methods that are passed a "Writer" to which error messages or status information is logged. You could log output to standard output like this:

    x.method( Std.out() );  // pass in the stream to which messages are logged

You could also cause the logged messages to simply disappear by calling:

    x.method( Std.bit_bucket() );   // discard normal logging messages
*/
049    public static PrintWriter bit_bucket()
050      {   if( bit_bucket == null )
051              synchronized( Std.class )
052              {   if( bit_bucket == null )
053                      bit_bucket = new Bit_bucket();
054              }
055          return bit_bucket;
056      }
058  
/*****************************************

The Bit_bucket class overrides all methods of PrintWriter to do nothing.

/
058  private static final class Bit_bucket extends PrintWriter
059      {
060  private Bit_bucket()
061          {   super( System.err ); // Never used, but must pass something legal.
062          }
063  
064  public void close() {}
065  public void    flush() {}
066  public void  print(boolean b) {}
067  public void    print(char c) {}
068  public void    print(char[] s) {}
069  public void    print(double d) {}
070  public void    print(float f) {}
071  public void    print(int i) {}
072  public void    print(long l) {}
073  public void    print(Object o) {}
074  public void    print(String  s) {}
075  public void    println() {}
076  public void    println(boolean b) {}
077  public void    println(char c) {}
078  public void    println(char[] s) {}
079  public void    println(double d) {}
080  public void    println(float f) {}
081  public void    println(int i) {}
082  public void    println(long l) {}
083  public void    println(Object o) {}
084  public void    write(char[] buf) {}
085  public void    write(char[] buf, int off, int len)  {}
086  public void    write(int c) {}
087  public void    write(String buf) {}
088  public void    write(String buf, int off, int len)  {}
089      }
090
/****************************************

A small test class, reads a line from standard input and echoes it to standard output and standard error. Run it with:

java com.holub.tools.Std\$Test

(Don't type in the \ when using a Microsoft-style shell.)

/
091    static public class Test
092      {
093        static public void main( String[] args ) throws IOException
094          {   String s;
095              while( (s = Std.in().readLine()) != null )
096              {   Std.out().println( s );
097                  Std.err().println( s );
098                  Std.bit_bucket().println( s );
099              }
100          }
101      }
102  }

Conclusion

So that's a start at showing you how to implement the theory that I discussed back in July. I hope that I've shown you that you can build flexible UIs with minimal coupling relationships between subsystems, and with no get/set functions. In the visual-proxy architecture that I described, the only potentially strong coupling relationships are between the class that represents an attribute and its proxy, but even here, you can use generic classes like Document and JTextField to display an attribute. Moreover, the architecture achieves the main goal of MVC -- the decoupling of the view and the model -- without the concommitant problems caused by the direct access to the model mandated by most MVC implementations. Forms can change radically without impacting the model and vice versa. Moreover, all changes to a model-level class are now concentrated in a single place, with no need to search the program for places where attributes of that class are displayed.

I plan to write a few more UI-related articles to round out the topic. Future columns will show you how to build adaptive widgets that change the way they display themselves based on the amount of screen real estate available. I'll also present a more elaborate application that demonstrates how a proxy can change its appearance without the rest of the system knowing that it's happened.

As usual, feel free to send me any questions or comments (or flames -- design topics seem to ignite a large number of them). I can't promise to answer all of your notes, but I'll probably address your issues in future columns, and I do appreciate the input.

Allen Holub has been working in the computer industry since 1979. He is widely published in magazines (Dr. Dobb's Journal, Programmers Journal, Byte, MSJ, among others). He has seven books to his credit, and is currently working on an eighth that will present the complete sources for a Java compiler written in Java. After eight years as a C++ programmer, Allen abandoned C++ for Java in early 1996. He now looks at C++ as a bad dream, the memory of which is mercifully fading. He's been teaching programming (first C, then C++ and MFC, now object-oriented design and Java) both on his own and for the University of California Berkeley Extension since 1982. Allen offers both public classes and in-house training in Java and object-oriented design topics. He also does object-oriented design consulting and contract Java programming. Get information, and contact Allen, via his Web site http://www.holub.com.
Related:
1 2 3 4 5 6 7 Page 6
Page 6 of 7