SE450: Lecture 3 (Architectural Patterns)

Contents [0/16]

Refactoring [1/16]
Principles and Patterns [2/16]
UML Sequence Diagrams [3/16]
Inner Class: Basics [4/16]
Inner Class: Link Names [5/16]
Inner Class: Anonymous [6/16]
Inner Class: Lambda [7/16]
Inner Class: Extending classes [8/16]
Factory: Shapes [9/16]
Factory: Static Factory Method [10/16]
Factory: Benefit [11/16]
Factory: Static Factory Class [12/16]
Homework 2 and 3 [13/16]
How to test a class in isolation [14/16]
MVC Lite [15/16]
Tracing software [16/16]

Refactoring [1/16]

Refactoring: Changing the structure of code without changing its functionality.

file:links-main

Smells to refactorings

Principles and Patterns [2/16]

Read the first/second link for next week.

Principles

Summary of the Principles of OOD

(By Uncle Bob)

How I explained OOD

(Someone attempts to explain these principles. Please ignore the sexism!)

Book Length version

(Buy here) (Available free on Safari)

Individual article-length versions

(Partially copied below)
Patterns

How I explained Design Patterns

(Please ignore the sexism!)

Holub Design Patterns Summary

(A great two page summary of each pattern)

Sourcemaking

(A short summary of each pattern, with non-code examples)

Whenever we bring up on our screens a nasty batch of tangled legacy code, we are experiencing the results of poor dependency management. Poor dependency managment leads to code that is hard to change, fragile, and non-reusable. Indeed, I talk about several different design smells in the PPP book, all relating to dependency management. On the other hand, when dependencies are well managed, the code remains flexible, robust, and reusable. So dependency management, and therefore these principles, are at the foudation of the -ilities that software developers desire.

The first five principles are principles of class design. They are:

SRP The Single Responsibility Principle A class should have one, and only one, reason to change.
OCP The Open Closed Principle You should be able to extend a classes behavior, without modifying it.
LSP The Liskov Substitution Principle Derived classes must be substitutable for their base classes.
ISP The Interface Segregation Principle Make fine grained interfaces that are client specific.
DIP The Dependency Inversion Principle Depend on abstractions, not on concretions.

The next six principles are about packages. In this context a package is a binary deliverable like a .jar file, or a dll as opposed to a namespace like a java package or a C++ namespace.

The first three package principles are about package cohesion, they tell us what to put inside packages:

REP The Release Reuse Equivalency Principle The granule of reuse is the granule of release.
CCP The Common Closure Principle Classes that change together are packaged together.
CRP The Common Reuse Principle Classes that are used together are packaged together.

The last three principles are about the couplings between packages, and talk about metrics that evaluate the package structure of a system.

ADP The Acyclic Dependencies Principle The dependency graph of packages must have no cycles.
SDP The Stable Dependencies Principle Depend in the direction of stability.
SAP The Stable Abstractions Principle Abstractness increases with stability.

UML Sequence Diagrams [3/16]

These show the evolution of a system over time.

         +------------+
         | <<static>> |
         |  SFactory  |
         +------------+
    newS()      |
-------------->+-+  new()   +-----+
               | |--------->| x:S |
               | |          | --- |
               | |          +-----+
               | |  x         | |
   x           | |<- - - - - -+-+
<- - - - - - - +-+             |
                |              |
                |              |
                |              |

Inner Class: Basics [4/16]

An inner-class instance has a link to the outer-class instance that created it. Thus it can reference the fields of the outer object.

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package basics.inner.one;
public class Main {
  private Main() {}
  static public void main (final String[] args) {
    //stdlib.Trace.graphvizShowSteps (true); stdlib.Trace.run ();
    C mc1 = new C(42);
    C mc2 = new C(36);
    mc1.f();
    mc2.f();
  }
}

interface Print { void print(); }

class C {
  int cx;
  C(int x) { cx = x; }
  class P implements Print {
    int py = 27;
    public void print() {
      System.out.println(" cx=" + cx + " py=" + py);
    }
  }
  void f() {
    Print p = new P();
    p.print();
  }
}


Inner Class: Link Names [5/16]

The outer class link has a funny name:

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package basics.inner.three;
public class Main {
  private Main() {}
  static public void main (final String[] args) {
    //stdlib.Trace.graphvizShowSteps (true); stdlib.Trace.run ();
    C mc1 = new C(42);
    C mc2 = new C(36);
    mc1.f();
    mc2.f();
  }
}

interface Print { void print(); }

class C {
  int cx;
  C(int x) { cx = x; }
  void f() {
    Print p = new Print() {
      int py = 27;
      public void print() {
        System.out.println(" cx=" + C.this.cx + " py=" + this.py);
      }
    };
    p.print();
  }
}

Inner Class: Anonymous [6/16]

Inner classes that are used once do not need to be named.

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package basics.inner.two;
public class Main {
  private Main() {}
  static public void main (final String[] args) {
    //stdlib.Trace.graphvizShowSteps (true); stdlib.Trace.run ();
    C mc1 = new C(42);
    C mc2 = new C(36);
    mc1.f();
    mc2.f();
  }
}

interface Print { void print(); }

class C {
  int cx;
  C(int x) { cx = x; }
  void f() {
    Print p = new Print() {
      int py = 27;
      public void print() {
        System.out.println(" cx=" + cx + " py=" + py);
      }
    };
    p.print();
  }
}

Inner Class: Lambda [7/16]

Unnamed inner classes that just have a single function can use a shorthand.

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package basics.inner.five;
public class Main {
  private Main() {}
  static public void main (final String[] args) {
    C mc1 = new C(42);
    C mc2 = new C(36);
    mc1.f();
    mc2.f();
  }
}

interface Print { void print(); }

class C {
  int cx;
  C(int x) { cx = x; }
  void f() {
    Print p = () -> System.out.println(" cx=" + cx);
    p.print();
    
    G g = (int x) -> {
      cx = cx + x;
      cx = cx + x;
      cx = cx + x;
    };
    g.run(3);
  }
}

interface G { void run(int x); }

Inner Class: Extending classes [8/16]

An anonymous inner class may extend a class, rather than implement an interface.

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package basics.inner.four;
public class Main {
  private Main() {}
  static public void main (final String[] args) {
    //stdlib.Trace.graphvizShowSteps (true); stdlib.Trace.run ();
    C mc1 = new C(42);
    C mc2 = new C(36);
    mc1.f();
    mc2.f();
  }
}

abstract class Print {
  int py;
  Print(int y) { py = y; }
  abstract void print();
}

class C {
  int cx;
  C(int x) { cx = x; }
  void f() {
    Print p = new Print(27) {
      public void print() {
        System.out.println(" cx=" + cx + " py=" + py);
      }
    };
    p.print();
  }
}

Factory: Shapes [9/16]

The simplest version exports the concrete class names.

file:Shape.java [source] [doc-public] [doc-private]
01
02
03
04
05
package factory.shape1;
import java.awt.Graphics;
public interface Shape {
  void paint(Graphics g);
}

file:Circle.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
package factory.shape1;
import java.awt.Graphics;
public class Circle implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}

file:Square.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
package factory.shape1;
import java.awt.Graphics;
public class Square implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
package factory.shape1.main;
import factory.shape1.Shape;
import factory.shape1.Square;
import factory.shape1.Circle;
public class Main {
  public static void main (String[] args) {
    Shape[] a = new Shape[2];
    a[0] = new Circle();
    a[1] = new Square();
  }
}

Factory: Static Factory Method [10/16]

A factory allows the concrete class names to remain hidden.

file:Shape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
package factory.shape2;
import java.awt.Graphics;
public interface Shape {
  void paint(Graphics g);
}
class Circle implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}
class Square implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}

file:ShapeFactory.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
package factory.shape2;
public class ShapeFactory {
  private ShapeFactory() {}
  static public Shape newInstance(String selector) {
    if ("Circle".equals(selector)) return new Circle();
    if ("Square".equals(selector)) return new Square();
    throw new IllegalArgumentException();
  }
}

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
package factory.shape2.main;
import factory.shape2.Shape;
import factory.shape2.ShapeFactory;
public class Main {
  public static void main (String[] args) {
    Shape[] a = new Shape[2];
    a[0] = ShapeFactory.newInstance("Circle");
    a[1] = ShapeFactory.newInstance("Square");
  }
}

Factory: Benefit [11/16]

Changing names does not affect client code.

file:Shape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
package factory.shape3;
import java.awt.Graphics;
public interface Shape {
  void paint(Graphics g);
}
class Ellipse implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}
class Rectangle implements Shape {
  public void paint(Graphics g) { /* ... */ }
  // ...
}

file:ShapeFactory.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
package factory.shape3;
public class ShapeFactory {
  private ShapeFactory() {}
  static public Shape newInstance(String selector) {
    if ("Ellipse".equals(selector))   return new Ellipse();
    if ("Circle".equals(selector))    return new Ellipse();
    if ("Rectangle".equals(selector)) return new Rectangle();
    if ("Square".equals(selector))    return new Rectangle();
    throw new IllegalArgumentException();
  }
}

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
package factory.shape3.main;
import factory.shape3.Shape;
import factory.shape3.ShapeFactory;
public class Main {
  public static void main (String[] args) {
    Shape[] a = new Shape[2];
    a[0] = ShapeFactory.newInstance("Circle");
    a[1] = ShapeFactory.newInstance("Square");
  }
}

Factory: Static Factory Class [12/16]

An alternative is to use multiple factory methods.

file:Shape.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package factory.shape4;
import java.awt.Graphics;
public interface Shape {
  void paint(Graphics g);
}
class Ellipse implements Shape {
  Ellipse(int radius1, int radius2) { /* ... */ }
  public void paint(Graphics g) { /* ... */ }
  // ...
}
class Rectangle implements Shape {
  Rectangle(int height, int width) { /* ... */ }
  public void paint(Graphics g) { /* ... */ }
  // ...
}

file:ShapeFactory.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package factory.shape4;
public class ShapeFactory {
  private ShapeFactory() {}
  static public Shape newEllipse(int radius1, int radius2) {
    return new Ellipse(radius1, radius2);
  }
  static public Shape newCircle(int radius) {
    return new Ellipse(radius, radius);
  }
  static public Shape newRectangle(int height, int width) {
    return new Rectangle(height, width);
  }
  static public Shape newSquare(int height) {
    return new Rectangle(height, height);
  }
}

file:Main.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
package factory.shape4.main;
import factory.shape4.Shape;
import factory.shape4.ShapeFactory;
public class Main {
  public static void main (String[] args) {
    Shape[] a = new Shape[2];
    a[0] = ShapeFactory.newCircle(1);
    a[1] = ShapeFactory.newSquare(1);
  }
}

Homework 2 and 3 [13/16]

Javadoc from the homeworks:

How to test a class in isolation [14/16]

file:myhw3/command/CommandHistoryTEST.java [source] [doc-public] [doc-private]
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
package myhw3.command;

import static org.junit.Assert.*;
import org.junit.Test;

public class CommandHistoryTEST {
  @Test
  public void testEmptyExceptions() {
    CommandHistoryObj h = new CommandHistoryObj();
    assertSame(null, h.topUndoCommand());
    assertSame(null, h.topRedoCommand());
    assertFalse(h.undo());
    assertFalse(h.redo());
  }

  private void checkStacks(CommandHistoryObj h, Command topUndo, Command topRedo) {
    assertSame(topUndo, h.topUndoCommand());
    assertSame(topRedo, h.topRedoCommand());
  }

  @Test
  public void topIsSetByAddUndoAndRedo() {
    CommandHistoryObj h = new CommandHistoryObj();

    class CmdSuccess implements Command {
      public boolean run() { return true; }
      public void undo() { }
      public void redo() { }
    }

    Command x1 = new CmdSuccess();
    Command x2 = new CmdSuccess();
    Command x3 = new CmdSuccess();

    h.add(x1);          checkStacks(h, x1,   null);
    h.undo();  checkStacks(h, null, x1);
    h.redo();  checkStacks(h, x1,   null);

    h.add(x2);          checkStacks(h, x2,   null);
    h.undo();  checkStacks(h, x1,   x2);
    h.undo();  checkStacks(h, null, x1);
    h.redo();  checkStacks(h, x1,   x2);
    h.redo();  checkStacks(h, x2,   null);

    h.undo();  checkStacks(h, x1,   x2);
    h.add(x3);          checkStacks(h, x3,   null);
    h.undo();  checkStacks(h, x1,   x3);
    h.undo();  checkStacks(h, null, x1);
    h.redo();  checkStacks(h, x1,   x3);
    h.redo();  checkStacks(h, x3,   null);

    h = new CommandHistoryObj();
    h.add(x1);          checkStacks(h, x1,   null);
    h.add(x2);          checkStacks(h, x2,   null);
    h.undo();  checkStacks(h, x1,   x2);
    h.redo();  checkStacks(h, x2,   null);
    h.add(x3);          checkStacks(h, x3,   null);
    h.undo();  checkStacks(h, x2,   x3);
    h.undo();  checkStacks(h, x1,   x2);
  }

  // these must be fields so that they can be changed by the instances
  // of the inner class TestThatMethodsArePerformed.
  private boolean didRun;
  private boolean didUndo;
  private boolean didRedo;

  @Test
  public void methodsArePerformed() {
    CommandHistoryObj h = new CommandHistoryObj();

    class MockCommand implements Command {
      // Using "CommandHistoryTEST.this" to make references to
      // outer class instance explicit
      public boolean run() {
        CommandHistoryTEST.this.didRun = true;
        return true;
      }
      public void undo() {
        CommandHistoryTEST.this.didUndo = true;
      }
      public void redo() {
        CommandHistoryTEST.this.didRedo = true;
      }
    }

    Command x = new MockCommand();

    didRun = didUndo = didRedo = false;
    h.add(x);
    assertTrue(!didRun && !didUndo && !didRedo);

    didRun = didUndo = didRedo = false;
    h.undo();
    assertTrue(!didRun && didUndo && !didRedo);

    didRun = didUndo = didRedo = false;
    h.redo();
    assertTrue(!didRun && !didUndo && didRedo);
  }
}

MVC Lite [15/16]

We will discuss a weak form of MVC using homework 3 as an example.

See the code for hw3.

MVC links

J2EE MVC

Swing MVC

Microsoft MVC

RogueWave paper on MVC in Enterprise Systems

Wiki MVC

What's a Controller, Anyway?

Alan Holub's discussion of OO User Interfaces

Parts 2, 3, and 4.

Presentation Abstraction Controller

Tracing software [16/16]

I will discuss the use of this tracing class: file:stdlib/Trace.java [source] [doc-public] [doc-private]

Some useful option include the following:

Trace.graphvizShowSteps (true);
Trace.graphvizShowStepsOfMethod ("methodName");
Trace.showBoxedPrimitivesAsPrimitive (false);
Trace.showStringsAsPrimitive (false);
Trace.setGraphizOutputFilenamePrefixRelativeToUserHome ("Desktop/Trace/trace-");
Trace.run ();
Trace.draw ();

Revised: 2008/01/29 17:36