Course: CS181C
Name: Andrew Wooster
Project Due Date: 03/04/2002
Date: 03/04/2002
Homework Number: 2
Subject: Logical and Physical Design


Logical and physical designs for three solutions for the Keyword in Context (KWIC) problem are given below. The Keyword in Context problem is stated as follows:
The KWIC index system accepts an ordered set of lines, each line is an ordered set of characters. Any line may be "circularly shifted" by repeatedly removing the first word and appending it at the end of the line. The KWIC index system outputs a listing of all circular shifts of all lines in alphabetical order.

Solution 2: Abstract Data Types
This solution consists of five modules. Data is not shared directly, but instead each data module provides a public interface by which other components can access that data, keeping the actual data structure hidden.
Abstract Data Types
Please note the following code isn't pure java. Some of it is shorthand or psuedocode to represent what I want to do.

// MasterControl.java
// uses Input, Characters, CircularShift, AlphabeticShifts, Output
public class MasterControl
{
    /**
     * The InputStream used as a data source.
     */
    private InputStream in;
    
    /**
     * The OutputStream to write the results to.
     */
    private OutputStream out;

    /**
     * Creates the MasterControl object given an InputStream to
     * get lines from and an OutputStream to output lines on.
     */
    public MasterControl (InputStream in, OutputStream out) {...}
    
    /**
     * Performs a number of operations in batch style to accomplish
     * the goals of the program.
     */
    public void kwic () 
    {
        // Note that here ch should be the same object used 
        // across all functions (ie, pass by reference, or whatever
        // Java's equivalent is [my Java is hazy]).
        Characters ch = new Characters();
        
        Input in = new Input(this.in, ch);
        while (in.read() >= 0) {}
        
        CircularShift cshift = new CircularShift(ch);
        cshift.setup();
        
        AlphabeticShifts ashifts = new AlphabeticShifts(cshift);
        ashifts.alph();
        
        Output out = new Output(this.out, ashifts);
        out.write();
    }
}
    
// Input.java
// uses Characters
import java.io.InputStream;

public class Input
    extends InputStream
{
    /**
     * The Characters object Input is putting lines in.
     */
    private Characters ch;
    
    /**
     * Creates the Input object inits the Characters object to point
     * to the object it's using.
     */
    public Input (InputStream in, Characters ch) {...}
    
    /**
     * Reads a character from the InputStream. Returns -1 if no more characters
     * to read, or a number greater than or equal to 0 if a character was read.
     */
    public int read () {...}
}
    
// Characters.java
public class Characters
{
    /**
     * The data structure. Multidimensional array. Lines, Words, Chars.
     */
    private char ReallyBigArray[][][];

    /**
     * Creates the Characters data structure.
     */
    public Characters () {...}

    /**
     * Sets cth character of wth word of rth line to character d.
     */
    public void setchar(int r, int w, int c, char d) {...}
    
    /**
     * Gets the cth character in the rth line, wth word.
     */
    public char char (int r, int w, int c) {...}
    
    /**
     * Get the number of words in line r.
     */
    public String word (int r) {...}
}

// CircularShift.java
// uses Characters
/**
 * "The module creates the impression that we have created a line 
 *  holder containing not all of the lines but all of the circular
 *  shifts of the lines." - Parnas
 *
 * This module doesn't actually hold all the data. It doesn't need to,
 * as it can generate them on the fly using the Characters object.
 */
public class CircularShift
{
    /**
     * Creates the CircularShift data structure and this object.
     */
    public CircularShift (Characters ch) {...}

    /**
     * Called before the other functions have their values.
     */
    public void setup() {...}

    /**
     * Sets cth character of wth word of rth circular shift to 
     * character d.
     */
    public void setchar(int r, int w, int c, char d) {...}
    
    /**
     * Gets the cth character in the rth circular shift, wth word.
     */
    public char char (int r, int w, int c) {...}
    
    /**
     * Get the number of words in the rth circular shift.
     */
    public String word (int r) {...}
}

// AlphabeticShifts.java
// uses CircularShift
/**
 * Provides access to an alphabetized version of the circular
 * shifts. May not necessarily need to store the alphabetized
 * ordering locally (Parnas didn't in his original paper).
 */
public class AlphabeticShifts
{
    /**
     * Creates the AlphabeticShifts data structure and this object.
     */
    public AlphabeticShifts (CircularShift cshift) {...}

    /**
     * Called before the other function (ith) will have a 
     * defined value. Should alphabetize the circular shifts.
     */
    public void alph() {...}

    /**
     * Get the index of the circular shift which comes ith in the
     * alphabetized ordering.
     */
    public int ith(int i) {...}
}

// Output.java
// uses AlphabeticShifts
import java.io.OutputStream;

public class Output
    extends OutputStream
{
    /**
     * Creates the Output object.
     */
    public Output (OutputStream out, AlphabeticShifts ashifts) {...}
    
    /**
     * Writes all the alphabetized circular shifts using 
     * AlphabeticShifts.
     */
    public void write () {...}
}
    
The logical view according to the previous framework looks like this:
Logical View
The physical view corresponding to this logical view looks like this:
Physical View

Solution 3: Reactive Integration
Reactive Integration
Looking at the above diagram of the original Reactive Integration solution, I see a problem. The solution isn't levelizable because there is a cyclic dependency between the first Lines structure and CircularShift. To make it easier, and to make the Lines structure more reusable, Input, rather than Lines, should be invoking the call to CircularShift when a new line is inserted in the structure. So, just imagine that dashed line is between Input and CircularShift rather than Lines.insert and CircularShift. So, the new architecture looks like this:
New Reactive Integration
Of course, Lines is a class, not an object. There will be two Lines objects used in the actual program.
The implementation below is naive, but naivety seems to be what the designers were going for.

// MasterControl.java
// uses Input, Characters, CircularShift, AlphabeticShifts, Output
public class MasterControl
{
    /**
     * Creates the MasterControl object given an InputStream to
     * get lines from and an OutputStream to output lines on.
     */
    public MasterControl (InputStream in, OutputStream out) {...}
    
    /**
     * Performs a number of operations in batch style to accomplish
     * the goals of the program.
     */
    public void kwic () 
    {
        Lines alphLines = new Lines();
        Input in = new Input(this.in, alphLines);
        while (in.read() >= 0) {}
        
        Output out = new Output(this.out, alphLines);
        out.write();
    }
}
    
// Input.java
// uses Lines (contains?), calls CircularShift
import java.io.InputStream;

public class Input
    extends InputStream
{
    /**
     * The Lines object Input is putting lines in.
     */
    private Lines iLines;
    
    /**
     * The alphabetized circular shifted lines.
     */
    private Lines alphLines;
    
    /**
     * The current line.
     */
    private String line;
    
    /**
     * The CircularShift object that has methods called on it by
     * Input when a new line is inserted into iLines.
     */
    private CircularShift cshift;
    
    /**
     * Creates the Input object and creates the new Lines member object.
     */
    public Input (InputStream in, Lines alphLines) {...}
    
    /**
     * Reads a character from the InputStream. Returns -1 if no more characters
     * to read, or a number greater than or equal to 0 if a character was read.
     * If the character is a line terminator or eof, the current line is 
     * inserted into the lines structure and CircularShift is called.
     */
    public int read () {...}
}
    
// Lines.java
public class Lines
{
    /**
     * The data structure. Multidimensional array. Lines, Words, Chars.
     */
    private char linesArray[][][];

    /**
     * Creates the Lines data structure.
     */
    public Lines () {...}

    /**
     * Inserts a new line into the data structure.
     */
    public void insert(string line) {...}
    
    /**
     * Deletes the ith line from the data structure.
     *
     * @return Boolean True if successful, false if line not found.
     */
    public Boolean delete (int r, int w, int c) {...}
    
    /**
     * Get the ith line.
     */
    public String ith (int i) {...}
}

// CircularShift.java
// uses Lines, calls Alphabetizer
/**
 * Keeps adding lines to a Lines object, and calls Alphabetizer on that
 * Lines object whenever a new circular shift is added.
 */
public class CircularShift
{
    /**
     * The input lines.
     */
    private Lines inLines;
    
    /**
     * The alphabetized circular shifted lines.
     */
    private Lines alphLines;
    
    /**
     * The Alphabetizer for alphabetizing alphLines.
     */
    private Alphabetizer alph;

    /**
     * Creates the CircularShift object.
     */
    public CircularShift (Lines inLines, Lines alphLines) {...}

    /**
     * Tells CircularShift the ith line was just inserted. CircularShift
     * then goes ahead and inserts all the circular shifts of the line in the 
     * alphabetized Lines structure and calls Alphabetizer.newline().
     */
    public void newLine(int i) {...}
}

// Alphabetizer.java
// uses Lines
/**
 * Alphabetizes the circular shifts in a data structure passed to it by
 * CircularShifts.
 */
public class Alphabetizer
{
    /**
     * The alphabetized circular shifted lines.
     */
    private Lines alphLines;
    
    /**
     * Creates the Alphabetizer object.
     */
    public Alphabetizer (Lines alphLines) {...}

    /**
     * Alphabetizes the alphabetized Lines structure.
     */
    public void alph() {...}
}

// Output.java
// uses Lines
import java.io.OutputStream;

public class Output
    extends OutputStream
{
    /**
     * The alphabetized Lines object to be outputted.
     */
    private Lines alphLines;

    /**
     * Creates the Output object.
     */
    public Output (OutputStream out, Lines alphLines) {...}
    
    /**
     * Writes all the alphabetized circular shifts using alphLines.
     */
    public void write () {...}
}
    
The logical view of the preceding framework is:
Logical View
The physical view is:
Physical View

Solution 4: Dataflow
Dataflow
This solution uses streams to pass the lines through various stages of computation. Since I don't have my book on Java Networking here, and can't find any good tutorials online, I don't really know the exact syntax for this, so I'm going to use very rough psuedocode.

Basically, all the stream writing and reading functions that allow the Lines object to be passed along are inherited from the base class Dataflow. The inheritance tree thus looks like:
Dataflow Inheritance

// Dataflow.java
// All classes on the stream will inherit from this, gaining the
// ability to read/write Lines to a stream.
public class Dataflow
{
    // Lines we're reading or writing to.
    private Lines lines
    
    // InputStream to read from
    private InputStream in
    
    // OutputStream to write on
    private OutputStream out
    
    Dataflow() // Constructor
    
    public Lines readLines() // Read Lines from in
    
    public void writeLines() // Write Lines to out
}
    
// Lines.java
public class Lines
{
    /**
     * The data structure. Multidimensional array. Lines, Words, Chars.
     */
    private char linesArray[][][];

    /**
     * Creates the Lines data structure.
     */
    public Lines () {...}

    /**
     * Inserts a new line into the data structure.
     */
    public void insert(string line) {...}
    
    /**
     * Deletes the ith line from the data structure.
     *
     * @return Boolean True if successful, false if line not found.
     */
    public Boolean delete (int r, int w, int c) {...}
    
    /**
     * Get the ith line.
     */
    public String ith (int i) {...}
}


// Input.java
// writes to CircularShift
public class Input
    extends Dataflow
{   
    /**
     * The current line.
     */
    private String line;
    
    /**
     * The CircularShift object that we eventually write to.
     */
    private CircularShift cShift;
    
    /**
     * Creates the Input object and creates the new Lines member object.
     * Reads all data from InputStream passed as parameter and puts it into
     * its inherited Lines object.
     */
    public Input (InputStream inputMedium) {...}
    
    /**
     * The function to make this object execute.
     * Reads all data from InputStream inputMedium and puts it into its
     * inherited Lines object. Then, writes lines out.
     */
    public void run()
    {
        // Read all data from InputStream
        ...
        
        this.writeLines();
    }
    
    /**
     * Writes lines to an OutputStream passed to CircularShift.
     */
    public void writeLines ()
}

// CircularShift.java
// uses Lines, extends Dataflow
/**
 * Generates all circular shifts of a set of lines given in a Lines object
 * read from an InputStream and writes them out to an OutputStream pointed
 * at an Alphabetizer.
 */
public class CircularShift
    extends Dataflow
{
    /**
     * The input lines.
     */
    private Lines inLines;
    
    /**
     * The Alphabetizer for alphabetizing lines.
     */
    private Alphabetizer alph;

    /**
     * Creates the CircularShift object.
     */
    public CircularShift (InputStream inputMedium) {...}

    /**
     * Runs this object, reading all lines from the inputgenerating all circular shifts of the lines in inLines
     * and putting them in inLines.
     */
    public void run() {...}
    
    /**
     * Write the lines to the Alphabetizer alph.
     */
    public void writeLines ()
}

// Alphabetizer.java
// uses Lines, extends Dataflow
/**
 * Alphabetizes the circular shifts in a data structure passed to it by
 * CircularShifts.
 */
public class Alphabetizer
    extends Dataflow
{
    /**
     * The alphabetized circular shifted lines.
     */
    private Lines alphLines;
    
    /**
     * The Output object to pass the Lines to.
     */
    private Output output;
    
    /**
     * Creates the Alphabetizer object.
     */
    public Alphabetizer (InputStream inputMedium) {...}

    /**
     * Alphabetizes the alphabetized Lines structure and call writeLines.
     */
    public void run() {...}
    
    /**
     * Write the lines to the Output.
     */
    public void writeLines ()
}

// Output.java
// uses Lines, extends Dataflow
public class Output
    extends Dataflow
{
    /**
     * The alphabetized Lines object to be outputted.
     */
    private Lines alphLines;

    /**
     * Creates the Output object.
     */
    public Output (InputStream in) {...}
    
    /**
     * Writes all the alphabetized circular shifts using alphLines.
     */
    public void write () {...}
}

    
The logical view of the preceding framework is:
Logical View
The physical view is:
Physical View

Comparison of Solutions
All three solutions follow the idea of hiding the data from other modules. Classes are also kept small. However, the first two solutions have God classes that control the execution of the entire program. This is a small system, and the Master Control module may be excusable by the natural modeling heuristic, but looking at Solution 4 it really seems as if the God class can be avoided relatively easily. None of the inheritance heuristics really are applicable except for Solution 4, which only uses a base class because of both shared data and behavior. It might almost be better to make each solution one class and have the modules be private member classes, just so that the related data and behavior these classes exhibit could be kept in one place.

The CCD metric is a bit misleading. Solution 2 has the "best" CCD but is a confusing, hard to understand system. Personally, I think that Solution 4 makes the best sense for the problem at hand, as it most closely follows the natural modeling technique whereby one models a system based as closely on nature (in this case, the problem statement) as possible. Since the problem is described as a series of operations on an input, and then the output of the result, Solution 4 most closely models this. This makes the system easier to understand. Also, since all of the public interfaces are roughly the same for the classes, the interfaces are minimized. This is a direct consequence of these classes deriving from the base class DataFlow. Additionally, at each step, related data and behavior are kept in the same place and each class captures one and only one key abstraction. So, despite its high CCD, Solution 4 seems to have more positive qualities than the other two. There are two exceptions to this, however. Solution 4 requires more space than the other solutions (the lines must be copied multiple times), and also prevents an interactive system from being implemented. In such cases where this is important, one of the other two designs would be preferable.