CSCI 370 Lecture 14: UML Diagrams, Builder Pattern, State Pattern, Decorator Pattern

Thursday, April 3


Unified Modeling Language (UML) Diagrams: Visualizing Software Architecture

UML diagrams serve as a foundational tool in software engineering for visualizing, specifying, constructing, and documenting the artifacts of a system. They allow developers to communicate system structure and behavior in a consistent and clear way.

During this lecture, the top three most frequently used UML diagrams were covered:

1. Class Diagrams

Class diagrams are arguably the most commonly used UML diagram type. They:

Example use case: In a banking application, a class diagram might illustrate the Account, Customer, and Transaction classes, including the methods each supports and how they interrelate.

2. Sequence Diagrams

These diagrams model dynamic behavior by showing how objects interact over time:

Example use case: Representing a login scenario in a web application where the user enters credentials, which are validated by the authentication service, and either a success or failure is returned.

3. Use Case Diagrams

Use case diagrams show functional requirements and how users (actors) interact with the system:

These diagrams typically include users (actors) and the system’s capabilities (use cases), showing which users can perform which actions.

Example: A file upload system might have a use case diagram showing Upload Document, Delete Document, and View Document, with actors such as User, Admin, and Guest, each with different access.

Summary: Why UML Matters

UML diagrams simplify communication, planning, and documentation. They allow developers to:

Without UML, onboarding new team members or managing large projects becomes significantly more difficult.


Builder Pattern: Controlled Object Construction with Validation

The Builder Pattern provides a flexible solution to object creation problems when a class has numerous fields. Rather than having a constructor with dozens of parameters, the builder allows step-by-step creation with validation along the way.

Builder Pattern Goals

  1. Prevent creation of invalid objects by encapsulating validation logic.
  2. Make object creation more readable and self-documenting, with method names indicating required and optional fields.

Key Concepts

Code Architecture (From Lecture)

We had the following structure:

Person.java (simplified structure):

public abstract class Person {
    protected String name;
    protected int age;
    protected String phone;
    protected String street;
    protected String category;
    public Person() {}
}

PersonBuilder.java

public class PersonBuilder extends Person {
    public PersonBuilder setImportantInfo(String phone, String name) { ... }
    public PersonBuilder setOptionalInfo(int age, String street) { ... }
    public PersonBuilder setCategoryAthruZ(String category) { ... }

    private boolean validate() {
        return !(phone.isBlank() || name.isBlank());
    }

    public Person build() {
        if (!validate()) return null;
        Person p = new _Person();
        // Copy all fields into new object
        return p;
    }

    private class _Person extends Person {}
}

Building the Object

PersonBuilder pb = new PersonBuilder();
Person p = pb.setImportantInfo("12888", "FRED")
             .setOptionalInfo(0, "")
             .setCategoryAthruZ("D")
             .build();

If phone or name is blank, the object is not created (returns null).

Benefits in Practice

This approach also conveys developer intent directly through method names (e.g., setImportantInfo vs. setOptionalInfo).


State Pattern: Modeling Object Behavior Across States

The State Pattern is used to allow an object to change its behavior based on its internal state. Each state is represented as a class that defines specific behavior, and the object delegates actions to its current state.

Key Features

Differences from Strategy Pattern

State Pattern Strategy Pattern
Behavior depends on current internal state Behavior selected externally, passed by client
States often change dynamically Strategy typically stays constant
Example: Game characters switching modes Example: Choosing payment method

Example: Car Transmission

In this example, the Car class has multiple driving modes (Park, Drive), and behavior changes based on the mode.

Class Hierarchy:

DriveState.java:

public abstract class DriveState {
    public DriveState prev, next;
    public abstract void drive();
    public DriveState shiftUp() { return (next != null) ? next : this; }
    public DriveState shiftBack() { return (prev != null) ? prev : this; }
}

Car.java:

public class Car {
    DriveState park = new Park();
    DriveState drive = new Drive();
    DriveState currentState = park;

    public Car() {
        park.next = drive;
        drive.prev = park;
    }

    public void shiftUp() { currentState = currentState.shiftUp(); }
    public void shiftBack() { currentState = currentState.shiftBack(); }
    public void drive() { currentState.drive(); }
}

Main.java:

Car car = new Car();
car.drive();       // Output: "VROOM I cannot move"
car.shiftUp();
car.drive();       // Output: "Hi I am cruising at 30 MPH"
car.shiftBack();
car.drive();       // Output: "VROOM I cannot move"

Benefits

This approach is also particularly useful in games, UI workflows, or any application with clearly delineated modes.


Decorator Pattern: Dynamic Behavior Extension

The Decorator Pattern is a structural pattern that allows behavior to be added to individual objects, dynamically and transparently, without affecting other objects.

Key Concepts

Use Case from Lecture: Logging

The system logs messages to multiple destinations:

Logger.java:

public interface Logger {
    void log(String message);
}

LogToFile.java, LogToDataBase.java, LogToMail.java:

Each implements Logger, wraps a Logger instance, and delegates logging.

Chaining Decorators:

Logger logger = new LogToMail(
                     new LogToFile(
                         new LogToDataBase(null)));
logger.log("abc");

Output:

log to database
logging to file
send email

This layered approach allows us to combine behaviors flexibly at runtime.

SendLog.java:

public void logIt(Logger logger, String message) {
    logger.log(message);
}

Practical Benefits


Summary of Design Patterns Covered

Pattern Purpose & Use Case
Strategy Encapsulates interchangeable algorithms (e.g., payment methods)
Observer Notifies subscribers about changes (e.g., UI updating on data changes)
Factory Centralizes object creation logic based on input or config
Builder Step-by-step object creation with internal validation
Singleton Ensures one instance only; used for config, cache, shared resources
Decorator Wraps objects to extend behavior dynamically (e.g., multi-level logging)
State Models context-specific behavior that changes over time (e.g., UI modes, character states)

Instructor Notes and Course Context


Final Takeaway

Understanding these patterns is crucial not only for writing clean, maintainable code, but also for interviewing, system design, and collaborative software development.

These examples, particularly when coupled with UML diagrams, offer a clear roadmap for applying object-oriented principles effectively.