Overview

4 Good class design to build the application right

This chapter positions design as a disciplined, iterative activity that spans most of the development lifecycle, complementing requirements, prototyping, and implementation. Initial classes emerge from the requirements, then evolve through development iterations, with external black-box testing and internal testing proceeding in parallel. Project milestones (such as requirements and feature freeze) guide progress but may shift; the overarching aim is to ensure we build the application right through thoughtful design and continuous verification.

Good class design centers on two goals: cohesion and loose coupling. Cohesion is achieved by giving each class a single, primary responsibility (as illustrated by separating operational, maintenance, and cleaning concerns of an automobile into distinct classes), which limits reasons for change. Loose coupling follows the Principle of Least Knowledge: classes depend on others through minimal, well-defined interfaces, reducing ripple effects from change. The chapter shows how UML class diagrams document classes and their relationships at varying levels of detail, covering dependency (including transient dependencies), aggregation versus composition (and multiplicity), and generalization via inheritance. It also explains how abstract classes and interfaces define required behaviors, enforce contracts on subclasses, and guide extension points.

To capture runtime behavior, the chapter introduces UML state diagrams for modeling how an object’s state evolves in response to events, and UML sequence diagrams for mapping object interactions across a use case—both helping validate that responsibilities and relationships are correctly assigned. All design artifacts (class, state, and sequence diagrams) belong in a design specification aimed at current and future developers, often accompanied by an Internal Test Plan. Unit and regression tests verify that the application is implemented correctly and remains stable across iterations, while the design specification is frozen at a milestone yet updated prudently as the system evolves.

Creating an application involves many activities. This diagram shows when the activities start relative to each other and how they overlap in time. However, it does not show their relative durations. The major project milestones are presented at the bottom. Most of the application design occurs during the development iterations. The design specification documents our design. This diagram shows these activities with heavy borders. Their dotted timespans suggest that their freeze dates must be flexible.
Possible UML class diagrams for class Catalogue that include different amounts of information. A plus sign (+) indicates what is intended to be public, and a minus sign (-) indicates what is intended to be private. How much information to include in a class diagram depends on whether the diagram is in high-level or low-level design documentation of the application.
A UML class diagram for class Attributes that includes all three parts: its name, instance variables, and methods. Private method _equal_ignore_case() is underlined to indicate that it is a Class relationships determine runtime interactions
A UML class diagram that shows an application’s multiple classes and their relationships. The relationship between two classes determines how their instantiated objects can interact at run time.
A UML diagram that shows (left to right) class Catalogue, a relationship, and class Book. The dependency relationship arrow going from class Catalogue to class Book indicates that class Catalogue depends on class Book. But class Book does not depend on class Catalogue.
Diagrams that show more design detail. Class Catalogue depends on class Book via its private booklist instance variable. The multiplicity indicators show that a single (1) Catalogue object can contain zero or more (*) Book objects.
Class Catalogue has a transient dependency relationship on class Attributes, as shown by the dashed dependency arrow. The add() method of class Catalogue receives a reference to an Attributes object and the find() method is passed a reference to an Attributes object. The dependency exists only during calls to these methods.
Class Catalogue aggregates class Book. Each Catalogue object contains zero or more Book objects in a list instance variable named booklist. The open diamond at the tail of the dependency arrow is next to the diagram for the container class Catalogue.
Class Book is composed of class Attributes. Each Book object has one Attributes object. According to the logic of the application, an Attributes object cannot exist without being a part of a Book object.
The difference between aggregation and composition. In this diagram, each Bookshelf object can contain 0 through 25 Book objects, and Book objects can exist outside of Bookshelf objects. Each Bookcase object is composed of 1 through 5 Bookshelf objects, but a Bookshelf object cannot exist without being a part of a Bookcase object.
Superclass Book has subclasses Fiction, Cookbook, and Howto. Also, superclass Attributes has subclasses FictionAttrs, CookbookAttrs, and HowtoAttrs. This figure shows two ways of drawing generalization diagrams.
Class MotorVehicle is abstract, so its name is in a slanted font. Its methods start_engine() and shut_off_engine() are abstract, and so they are also in a slanted font. Subclasses Car and Truck must implement these abstract methods. The relationship arrow with the open triangle points to the superclass.
The label «interface» appears above the name of interface MotorVehicleInterface. All of its methods are abstract, and it contains no instance variables. The dashed relationship arrow with the open triangle indicates that subclasses Car and Truck implement the interface, and therefore, they must provide code for all the methods of the interface.
Table 4.1 Relationship lines and arrows in UML class diagrams
A UML state diagram that shows the state changes of a Car object during run time. Each box represents a state, and each arrow represents a transition from one state to another. Each transition is labeled with the behavior (event) that causes the transition. Such a diagram allows us to visualize what happens to a particular object during run time and helps to ensure that we designed the class with the correct instance variables and methods.
A state diagram for a student enrolling in a course. The filled-in diamonds are the decision branch and merge points. The heavy bars indicate where the transitions fork and join. Such a diagram would help to design a Student class with the correct instance variables and methods in a hypothetical school simulation application.
A UML sequence diagram for a customer searching for books that match target book attributes. At a high level, it shows the runtime interactions among objects involved in this use case as time goes from top to bottom. Such a diagram helps to ensure that we designed the class relationships correctly.
An example of a UML sequence diagram for the use case of a customer withdrawing cash from a bank ATM in a hypothetical banking application

Summary

  • A project to develop an application consists of multiple activities performed in parallel by a team of developers. Design occurs throughout nearly the entire project, and mostly during the design–code–test development iterations.
  • Well-designed classes are cohesive and follow the Single Responsibility Principle.
  • Well-designed classes are loosely coupled and follow the Principle of Least Knowledge.
  • An abstract class has at least one abstract method that its concrete subclasses must implement. It can also have instance variables and non-abstract methods.
  • An interface class is an abstract class that has only abstract methods. It serves as a blueprint for all the behaviors that its concrete subclasses must implement.
  • A UML class diagram provides information about an application’s classes and their relationships. Each class diagram shows the class name and, optionally its instance variables and methods. The amount of information to show depends on whether the diagram is part of a high-level or a low-level description of the application’s design.
  • Different types of arrows drawn between two class diagrams indicate the relationship between the classes: dependency, aggregation, composition, or generalization.
  • A UML state diagram focuses on the state changes of a single object at run time. An event causes an object to make a transition from one state to another, and the events are defined by the object’s behavior. Visualizing what happens to a particular object during run time helps to ensure that the object’s class is well-designed with correct instance variables and methods.
  • A UML sequence diagram shows the high-level interactions among objects at runtime. How an object behaves during an interaction is determined by its methods. Visualizing these dynamic interactions helps to ensure that the classes are well-designed with correct methods and proper relationships.
  • A design specification describes and justifies an application’s design for its current and future developers. It contains the UML class, sequence, and state diagrams. Furthermore, it can include a test plan for internal testing.
  • For the verification part of software V&V, software developers perform internal testing that includes unit and regression tests during the development iterations.

FAQ

When does application design happen in the development process?Throughout the project. After gathering requirements (via use cases and prototypes) and writing the Functional Specification, you pick initial classes. Most design work happens during development iterations where classes are expanded and refined. External (black-box) and internal testing run in parallel. Milestones like “Requirements Freeze” and “Design Freeze” guide progress, but late changes may be approved at schedule risk.
What are the two primary goals of good class design?Cohesion and loose coupling. Cohesion (Single Responsibility Principle) means each class has one primary responsibility and one reason to change. Loose coupling (Principle of Least Knowledge) means classes minimize dependencies and knowledge of each other’s internals, which localizes the impact of change and supports encapsulation.
What is cohesion and the Single Responsibility Principle (SRP)?A cohesive class has one primary responsibility. If a class mixes unrelated responsibilities, split it. Example: splitting an overgrown Automobile into Automobile (driving operations), Garage (maintenance), and CarWash (cleaning). Each then has a single reason to change, improving maintainability and clarity.
What is loose coupling and the Principle of Least Knowledge?Loose coupling minimizes dependencies between classes. With the Principle of Least Knowledge, a class should know as little as possible about others’ internals. Example: Catalogue depends on Attributes only through parameters (transiently) and delegates matching via is_match(); Attributes has no dependency back. This reduces ripple effects from changes.
What does a UML class diagram include and how is visibility shown?At minimum, the class name; optionally instance variables (with types) and methods (signatures: name, parameter types, return type). Use “+” for public and “-” for private. You can omit constructors and trivial getters/setters. Static/class members may be underlined. Choose diagram detail based on audience (high-level vs low-level).
How are dependencies and multiplicity shown in UML class diagrams?A line shows a dependency; an arrowhead points from dependent to dependency. Label the line with the referring member (e.g., _booklist) and show multiplicity near each end (e.g., 1, 0..*, +, or ranges like 2..5). A dashed arrow indicates a transient dependency (e.g., parameters/local variables used only during a method call).
What is the difference between aggregation and composition?Both are “has-a” relationships. Aggregation (open diamond at the container) means the part can exist independently of the whole (e.g., Catalogue aggregates many Book objects). Composition (filled diamond) is stronger: the part cannot logically exist outside the whole (e.g., Book is composed of exactly one Attributes). Choose composition when lifecycle/ownership is exclusive.
How are inheritance, abstract classes, and interfaces modeled and implemented?Generalization (inheritance) uses an arrow with an open triangle pointing to the superclass. Abstract classes in Python derive from abc.ABC and define @abstractmethod members; you cannot instantiate them, and concrete subclasses must implement all abstract methods. In UML, abstract class and method names appear in italics. An interface is an abstract class with only abstract methods and no instance variables; UML labels it «interface», and implementing classes connect via a dashed arrow with an open triangle.
Which UML diagrams show runtime behavior, and how do they help?State diagrams show how a single object’s state (based on its instance variables) changes in response to events (method calls), including decisions, forks, and joins (e.g., Car states, Student enrollment). Sequence diagrams show time-ordered interactions among objects in a use case using lifelines, activation bars, and messages (e.g., book search, ATM withdrawal). Both validate that methods, data, and relationships support the required behavior.
What is a design specification and how does internal testing fit in?The design specification is a developer-focused document that explains and justifies the design, typically including UML class, state, and sequence diagrams. It may include an Internal Test Plan for unit tests that stress specific code paths and regression tests to catch accidental breakage after changes. This supports verification (the “built it right” part of V&V).

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free