2. Architectural Design

Overview

Architectural design is the decomposition of a system into its major subsystems and their dependencies. Subsystems are often represented by packages.

Packages

A package is a named set of related classes, interfaces, and sub-packages. A UML package diagram shows a system's important packages and their dependencies. Packages appear in these diagrams as labeled folders called package icons. A dependency is a dashed arrow pointing from an importer package to an exporter package, and indicates that changes to the exporter package may force changes to the importer package.

For example, the following package diagram indicates that components in the Business Logic package import (use) components defined in the Database package, while components in the User Interface package import components defined in the Business Logic package. Of course some of these components may have been imported by the Business Logic package, so the dependency relationship is transitive.

In a unidirectional dependency the exporter is also called the provider or server, while the importer is called the client. In a bi-directional dependency both packages are called peers:

Architectural Patterns

Sometimes the same architectural design recurs in diverse applications. We call this an architectural pattern. Here are a few examples.

Model-View-Control

Client-Server

Reflection

Layers

Package Structures

There are a few other patterns that are used for developing packages. To loosen the coupling between packages, the facade pattern designates one class as the package interface to the others. A package only exports its facade:

The mediator minimizes the connections between classes within a package:

Packages in C++

Packages can be implemented in C++ using names spaces:

namespace Database
{
   class Query { ... };
   class Table { ... };
   // etc.
}

namespace BusinessLogic
{
   using namespace Database;
   class Transaction { ... };
   class Customer { ... };
   // etc.
}

namespace UserInterface
{
   using namespace BusinessLogic;
   class DialogBox { ... }
   class Menu { ... };
   // etc.
}

Packages in Java

Java programmers place a package declaration at the top of every .java file that contains declarations of classes that are to be included in the package:

package NAME;

For example, the Query class is defined in the file Query.java and is part of the dbase package. The first non-comment line of Query.java is the package declaration:

// file: myApp\dbase\Query.java
package dbase; // package declaration

public class Query {
   public Query() {
      System.out.println("Constructing a query");
   }
   public void execute() { /* etc. */ }
   // etc.
}

As the folder-shaped package icon suggests, there is a close correspondence between packages and file system directories. This correspondence is made explicit in Java. All .java files (and corresponding .class files) that belong to the dbase package reside in a directory called dbase. On my (Windows ME) system, dbase is a subdirectory of a directory called myApp, which will contain all of the packages of a mythical application called App.

The Transaction class is defined in a file called Transaction.java and belongs to the business package:

// file: myApp\business\Transaction.java
package business;

public class Transaction {
   public Transaction() {
      System.out.println("Constructing a transaction");
   }
   public void commit() { /* etc. */ }
   // etc.
}

Naturally, Transaction.java and Transaction.class belong to the business subdirectory of the myApp directory.

The user interface package (ui) is more interesting. In addition to classes, it contains two sub-packages: view and control. The view package contains classes that deal with system output, while the control package contains classes that deal with system input. The name of a Java sub-package is always qualified by the name of its super-package:

PACKAGE.SUB-PACKAGE.SUB-SUB-PACKAGE.SUB-SUB-SUB-PACKAGE

For example, the Window class belongs to the ui.view package:

// file: myApp\ui\view\Window.java
package ui.view;

public class Window {
   public Window() {
      System.out.println("Constructing a window");
   }
   public void show() { /* etc. */ }
   // etc.
}

While the (Message) Broker class belongs to the ui.control package:

// file: myApp\ui\control\Broker.java
package ui.control;

public class Broker {
   public Broker() {
      System.out.println("Constructing a message broker");
   }
   public void pumpMessages() { /* etc. */ }
   // etc.
}

All of the ui.view files reside in the view subdirectory of the myApp\ui directory. All of the ui.control files reside in the control subdirectory of the myApp\ui directory.

The entry point for App is the main() method of the App class, which is defined in the App.java file. This class is not part of any named package. Instead, it belongs to the default package:

// file: myApp\App.java
import dbase.*;

public class App {
   private Query q;
   private business.Transaction t;
   private ui.view.Window w;
   private ui.control.Broker b;
   public App() {
      w = new ui.view.Window();
      b = new ui.control.Broker();
      t = new business.Transaction();
      q = new Query();
      w.show();
      b.pumpMessages();
   }
   public static void main(String[] args) {
      App a = new App();
   }
}

Notice that all references to classes outside of the default package must be qualified by the full names of their packages. For example we must write:

private ui.view.Window w = new ui.view.Window();

The exception is the Query class. This appears without qualification because of the import statement at the top of App.java:

import dbase.*;

In effect, this directs javac (the Java compiler) and java (the Java VM) to search the dbase package for all classes not found in the default package.

Of course the translation from a qualified package name to a Windows or Unix path name is trivial (replace '.' by '\' or '/'), but the resulting path name is relative. How do java and javac translate a relative path name into an absolute path name? The answer is that they search for these relative paths in all directories listed in the CLASSPATH and PATH environment variables. Thus, if the absolute paths p1, p2, and p3 appear in the CLASSPATH variable, and if q is a relative path corresponding to a package name, then Windows versions of javac and java will search p1\q, p2\q, and p3\q for the corresponding .class files it requires. Usually ., the working directory is listed in PATH or CLASSPATH, so the default package will automatically be searched, too.

Setting PATH and CLASSPATH

Be careful. There are some discrepancies in this scheme from one platform to another. Some systems ignore the PATH variable. Package names are case sensitive and path names may be case sensitive. Also, make sure your PATH and CLASSPATH variables are correct. In Windows the values of these variables can be seen by typing the DOS set command to a Window's console prompt. The set command can also be used to add new paths to either of these variables:

set CLASSPATH=%CLASSPATH%;c:\java\projects;c:\java.packs

This line can also be placed in a .BAT file for convenience.

JAR Files

Exporting Classes, Methods, and Fields

Only public classes can be exported from a Java package. Within a public class, only public and protected fields and members can be accessed by importers. (Of course protected fields and methods can only be accessed by importers that extend the class.) By default, all classes, methods, and fields have package scope.

Virtual Platforms

Sun's specification of Java is a good example of the use of packages. There are actually three Java platforms: the enterprise edition (J2EE), the standard edition (J2SE), and the micro edition (J2ME). J2EE describes a virtual server platform, J2SE describes a virtual client platform, and J2ME describes a virtual PDA platform. A Java applications is written on top of one of these platforms. There are various physical implementations of these platforms available:

Each virtual platform consists of a virtual machine (VM) capable of executing compiled Java instructions and several virtual subsystems that provide extended platform services such as I/O, multithreading, network access, database access, graphics, and user interface support. The subsystems are specified as a hierarchy of packages. The top level packages are java, javax, and org.omg. The java sub-packages include awt (Abstract Windows Toolkit), beans, applet, io, lang (language extensions), math, net (network access), rmi (remote method invocation), util, etc. The javax package contains experimental sub-packages, including swing and accessibility. The org.omg package contains the CORBA sub-package. 

Programming Notes

Note 2.1: A Taxonomy of Computational Models

Treleaven developed an interesting taxonomy of computational models that can be applied to hardware or software systems. The basic idea is that every system has a control mechanism and a data mechanism. There are two types of control mechanisms: message passing (ME) and memory sharing (ME). There are four types of control mechanisms: Control driven (CO), data driven (DA), demand driven (DE), and pattern driven (PA). The design space consists of all possible combinations of control and data mechanisms:

COSH

COME

DASH

DAME

DESH

DEME

PASH

PAME

OMT Decomposition

Layers, Partitions, and Topology

Identifying Concurrency

Data Store Management

Handling Global Resources

Choosing a Control Mechanism

Procedure Driven
Event Driven

Handling Boundary Conditions

Initialization
Termination
Failure

Common Architectural Frameworks

Batch
Interactgve
Dynamic Simulation
Real-Time
Transaction Manager

Problems

Problem 2.1

Create a package called nature that contains a class called Cloud and two sub-packages called tree and food. Put a class called Apple in the tree package and another class called Apple in the food package. Write an application in the default package that creates instances of each of these classes.

Problem 2.2

Modula 2 distinguishes between interface packages and implementation packages. (Packages are called MODULEs in Modula 2). An interface package contains only public interfaces, while an implementation package contains package scope implementations of these interfaces plus any supporting classes:

Sketch an example of this in Java, where Exporter contains UI interfaces such as Window, Menu, and Dialog, Exporter Impl 1 contains X Windows implementations, and Exporter Impl 2 contains MS Windows implementations, where X and MS Windows are mythical UI libraries.

Problem 2.3

Create a package called molecule that contains classes named Water, Sugar, Carbon Dioxide, and SulfuricAcid, and a sub-package named molecule.atom. The atom sub-package contains classes named Carbon, Hydrogen, Oxygen, and Sulfur. Each class in the atom package should encapsulate the atomic number and mass of the atom, together with its symbol. (See http://www.dayah.com/periodic/ for a table of the elements.) The classes in the molecule package should contain the correct number and types of atoms as components. For example, one type of sugar has the formula C6H12O6: 6 atoms of carbon, 12 of hydrogen, and 6 of oxygen. Each class should re-define the inherited toString() and equals() methods. The toString() method should return a string describing the object. The equals method should test for chemical, not literal equality. Naturally, each class should have a test driver named main().

Create a class called MoleculeTest-- in the default package-- that calls the test driver for each class in the molecule and atom packages. As a final requirement: don't use import statements. All classes should be referred to by their fully qualified name.

Add a method called "describe()" to each class in the molecule package. The describe() method should display a description of the properties of instances of the class. For example: "water is wet", "sulfuric acid burns", etc. Add a test harness to MoleculeTest that expects an array of molecules as input. (Of course this will have to be typed as an array of Objects). The test harness traverses the array, displays each molecule and calls its describe() function. Are there any operations you must perform on objects in the array before you can call their describe() methods? Do you need to perform these same operations before you call their toString() functions? Why?

Add a class called Molecule to the molecule package. This class should have a method called describe() that returns the empty string. Re-do problem #2 only this time re-type the array as an array of Molecule. Does this allow you to simplify the test harness?