Monday, October 21, 2013

UML 2 - Class Diagrams

This is just my take from the books I've read and the videos I've watched on the subject.  So I might have some concepts wrong. The important thing, though, is that you can translate a class diagram to actual code. Otherwise, as Robert C. Martin puts it in his book Agile Principles, Patterns, and Practices in C#, "...you're just building castles in the air."  (page 158)

Classes are represented by rectangles, with the name of the class placed at the top and centered.
public class Klass {
}


The class box is typically divided into three parts.  The top part contains the name of the class.  The middle part contains the fields and the bottom part contains the methods, including constructors.
public class Klass {
   private Object obj;

   public Object getObj() {
      return obj;
   }

   public void setObj(Object obj) {
      this.obj = obj;
   }

   public Object fooBar() {
      Object result;

      // ... implementation details ...

      return result;
   }
}


There's a couple things to take note of here.  The first thing you should notice is that even though the class box does not declare any accessor or mutator methods, they still appear in the translated code.

Here's how you'd explicitly declare a field as read-only:
public class Klass {
   private Object obj;

   public Object getObj() {
      return obj;
   }
}


Likewise, to explicitly declare a field as write-only here's what you'd do:
public class Klass {
   private Object obj;

   public void setObj(Object obj) {
      this.obj = obj;
   }
}


The general syntax for fields goes like this:

visibility name : type [= defaultValue]

The following symbols are used to indicate visibility:
  • + public
  • - private
  • # protected
  • ~ package
These should be self-explanatory for most Java developers.  The only one you might not have seen before is the package scope.  This is the default scope for the fields and methods of a class.  Fields with this access modifier are only accessible from within it's own package.

See Also: The Java Tutorials: Controlling Access to Members of a Class

The default value is in square brackets because it's optional.

Recall our previous example.
public class Klass {
   private Object obj;

   public Object getObj() {
      return obj;
   }

   public void setObj(Object obj) {
      this.obj = obj;
   }

   public Object fooBar() {
      Object result;

      // ... implementation details ...

      return result;
   }
}


The second thing you should notice from the above example is that the fooBar method is missing part of its body.  That's because class diagrams aren't meant to show implementation details.  They're only meant to give a general skeleton of the class.

The general syntax for methods goes like this:

visibility name( [parameters] ) : returnType

Again, the square brackets mean that the parameters are optional.

If the method doesn't return anything, you can either specify the return type as void...
public class Klass {
   public void fooBar( ) {
   }
}


...or you can simply omit it.
public class Klass {
   public void fooBar( ) {
   }
}


You can also specify the direction for parameters.  In my opinion, though, this feature is pretty useless since Java doesn't support the concept of pointers like C++ does.  I'll briefly describe them here for completeness.

There are three types of directions you can specify:
  • The in direction indicates that the argument is set before the method is called.
  • The out direction indicates that the argument is set inside the method.
  • The inout direction indicates that not only is the argument set before the method is called, but is also changed by the method.
Here's the best example I could come up with to demonstrate each of these:

fooBar(in zipCode : int, out city : City, inout address : Address) : void

Then inside the fooBar method you might have the following:

city = City.getByZipCode(zipCode);
address.setCityName( city.getName( ) );

Obviously this violates the Single Responsibility Principle and is just one more reason not to specify direction for method parameters.

In Class Diagrams, you underline static fields and static methods.
public class Klass {
   public static Object obj;

   public static void fooBar( ) {
   }
}


Interfaces can be shown using either ball notation...
public interface Comparable {

   public int compareTo(Object obj) {
      int result;

      // ... implementation details ...

      return result;
   }
}


...or you can use stereotypes. The "<<" and ">>" symbols are called guillemets.
public interface Comparable {

   public int compareTo(Object obj) {
      int result;

      // ... implementation details ...

      return result;
   }
}


You'll also see stereotypes used when diagramming enums.
public enum DaysOfTheWeek {
   Sunday,
   Monday,
   Tuesday,
   Wednesday,
   Thursday,
   Friday,
   Saturday;
}


Abstract classes and methods are written in italics.
public abstract class NonPlayableCharacter {

   public abstract void interactWith(Player player);
   public abstract String getName();
}


Since class diagrams will mostly be drawn on white boards and writing in italics is pretty hard to do, Robert C. Martin recommends you place the word abstract inside curly braces.  (page 189, Agile Principles, Patterns, and Practices in C#)
public abstract class NonPlayableCharacter {

   public abstract void interactWith(Player player);
   public abstract String getName();
}


To show that a class implements an interface or extends another class you'll use an open triangle on a line that connects the two. 
public class Bread extends Item implements Eatable, Health {

   @Override
   public String getName() {
      String result;

      // ... implementation details ...

      return result;
   }
}


Notice that the open triangle points to the "parent" class.

An open circle with a cross through the middle is used to represent nested classes.
public class Outside {
   public class Inside {
   }
}


Packages are represented by a tabbed file folder.
package items.interfaces;

public interface Defensive {
   public boolean useOn(BadGuy badGuy);
}


The last two concepts I'll cover here deal with whole/part relationships.

You'll use aggregation when you want to show one class is composed of another.  The best example I can give for this concept is the decorator pattern.
public abstract class Decorator extends Component {
   protected Component component;

   public Decorator(Component component) {
      this.component = component;
   }
}


See Also: doFactory's article on Decorator Pattern

Composition is similar to aggregation, except that the part can't be shared between wholes.  The lifespan of the part is also completely dependent upon the whole.  For example, most people have one head, one body, two arms, and two legs.
public class Human {
   private Head head = new Head();
   private Body body = new Body();
   private Arm[] arms = new Arm[2];
   private Leg[] legs = new Leg[2];
}


See Also: Russ Jackson's blog article "UML Class Diagram Relationships, Aggregation, Composition"

No comments:

Post a Comment