Class Diagram Anti-Patterns

Unspecified Endpoints

Okay, assuming this is a bi-directional association, how many pilots are associated with a flight? How does a flight refer to its pilots? Are the pilots shared or owned by the flight? What code should be generated? And so many more questions.

Plural Class Names

Does an instance of the Students class represent a single student or a group of students?

Debatable Aggregates

Aggregation (shared and composite) should only be used to emphasize that an association between two classes represents a part-whole relationship:

That said, there are lots of debatable part-whole relationships:

Given that aggregation doesn't make a difference when mapped to reference-based languages like Java, ask yourself if you are creating more confusion or less by using aggregation.

Attributes versus References

Assume we want to build a model for an online ticket vendor. In this model a ticket has two properties: a price of type Money and an event of type Performance.

We can represent these properties as attributes of as references (endpoints of associations).

The following diagram represents event as an attribute and price as a reference:

Both properties are (correctly) mapped to fields by a code generator:

class Ticket {
   Money price;
   Performance event;
   // etc.
}

However, Money is a very general class that might occur in any commercial model, while Performance is more specific to online ticket sales. It would be better to not clutter the diagram with the Money class. It belongs in some reusable commerce package and can appear as an attribute type. On the other hand, the relationship "T is a ticket for performance P" is a significant relationship in the domain of online ticket sales and therefore should be modeled by an association:

Bidirectional versus Multiple Associations

Consider:

Every student is advised by a teacher. Every teacher has a student TA.

Note that advised-by and has-a-TA are two different relationships. One is not the inverse of the other as suggested by this diagram:

(I.e., if Smith is the TA for Prof. Jones, it does not follow that Prof. Jones is Smith's advisor.)

Instead, two associations are needed:

Lazy and Bloated Classes

A bloated class has too many responsibilities. A lazy class doesn't have enough.

In the following diagram Ship, Car, and Airplane are empty classes that ae only used for type identification. All of the behaviors are in Vehicle:

Here's what the Vehicle class might look like:

class Vehicle {
   void fly() {
      if (vehicle.class != Airplane.class) throw new Exception("Only airplanes can fly");
      // etc.
   }
   void drive() {
      if (vehicle.class != Car.class) throw new Exception("Only cars can drive");
      // etc.
   } void sail() {
      if (vehicle.class != Ship.class) throw new Exception("Only ships can sail");
      // etc.
   }
}

Each time a new type of vehicle is added (helicopter, bike, flying saucer) we will need to modify the Vehicle class.

Delegation versus Inheritance

There are two ways an object can make use of another object's methods: delegation and inheritance.

Only use inheritance when the behavior of one object extends the behavior of another.

In this example a stack is a list and therefore inherits all of the operations of a list, even though these operations would allow unwanted access to the stack's data.

In this diagram a stack has a list. The stack operations can delegate to the appropriate list operations without exposing them to clients:

Unnecessary Middleman

A middleman passes messages between a client and a provider. For example:

If the middleman doesn't add value to the exchange (for example by translating messages from one language/protocol to another), then consider getting rid of it.

Overuse of Dependency

Use dependency arrow sparingly in class diagrams.

Class A depends on class B means changes to B might require changes to A. Not much information. Generalization, realization, and association all imply dependency and they give a lot more information.

Representing types as strings

The world is filled with types. Representing them as strings makes comparing types inefficient and error prone.

In this example gender and status are types. Gender is represented as a string, while status is one of two objects.

Representing gender as a string opens the door for a host of possible errors:

member.gender = "mail";
member.gender = "transfluidstraight";
member.gender = "fEmAlE";

Ubiquitous language

Think of a domain model as a glossary of domain terminology. Therefore, names of classes, operations, attributes, and endpoints should reflect the terminology used by domain experts.

For example, if the domain expert says "Dispatchers dispatch trucks" don't model this as:

Do interfaces belong in Domain Models?

Sometimes.

Use interfaces to represent gateways to secondary actors.

In an e-commerce application we may need to communicate with various bank servers (secondary actors). The APIs of these servers could vary, and we may not want to wade into the details during the modeling phase, so we can introduce a gateway interface, then later use the Adapter Pattern to implement it

We might not know how Accounts are represented, so we can use an interface for this, too:

Should visibility be specified in Domain Models?

In my opinion visibility is a design-time decision. It's also a topic that would not be understood by other stakeholders who must understand the domain model. On the other hand, if we are practicing domain-driven design, the domain model becomes the domain layer of the design model, at which point visibility gets added to the model. Also, it's easy to hide visibilities.

Confusing roles or objects with classes

Example: An organization has a parent organization and many subsidiaries.

Wrong

image002

Right

image004

Example: A flight has a departure time and an arrival time.

Wrong

image006

Right

image007

Failure to Pass the Uses-A or Is-A tests

If B is a generalization of A or if A realizes interface B, then the sentence "A is a B" makes sense.

If A is associated with B as if the sentence "A uses-a or has-a B" makes sense.