Loading learning content...
Not every relationship between classes is structural. Sometimes, one class merely uses another temporarily—to perform a calculation, to convert data, to send a message. The user doesn't store the used class as an attribute; it simply invokes its services when needed and moves on.
Yet even this transient usage creates a coupling—a change to the used class might break the using class. This weak but real connection is called a dependency.
Dependency is the weakest relationship in UML, but understanding it is crucial for managing coupling in your designs. Too many dependencies create fragile, hard-to-change systems. Strategic use of dependency reveals your architecture's structure.
By the end of this page, you will understand what dependency represents and how it differs from association, how to draw dependency using the dashed arrow notation, the different types of dependencies (usage, creation, parameter), how dependencies affect system coupling and maintainability, and when to show dependencies in your diagrams.
Dependency is a relationship indicating that one class (the client or dependent) uses another class (the supplier) in some way, without storing a permanent reference to it. If the supplier changes, the client may be affected.
The defining characteristics of dependency:
How dependencies arise in code:
| Dependency Type | Code Pattern | Example |
|---|---|---|
| Parameter dependency | Method receives type as parameter | void process(DataValidator validator) |
| Return type dependency | Method returns type | PaymentResult charge() |
| Local variable dependency | Type used within method | var formatter = new DateFormatter() |
| Static method dependency | Calls static methods | StringUtils.trim(input) |
| Exception dependency | Throws or catches exception type | throws InvalidInputException |
If class A stores a reference to class B as an attribute, that's an association. If class A merely uses class B (as a parameter, return type, or local variable) without storing it, that's a dependency. Association is structural; dependency is behavioral.
In UML, dependency is represented by a dashed line with an open arrowhead pointing from the dependent class (client) to the class it depends on (supplier).
Basic dependency visualization:
Key elements of dependency notation:
| Element | Description |
|---|---|
| Dashed line | Indicates non-structural, transient relationship |
| Open arrowhead | Points toward the supplier (depended-upon class) |
| Direction | Arrow points from client to supplier |
| Label (optional) | Describes the nature of dependency (<<create>>, <<use>>, etc.) |
Stereotyped dependencies:
UML allows adding stereotypes to clarify the type of dependency:
| Stereotype | Meaning | Example |
|---|---|---|
| <<use>> | Client invokes supplier's methods | Service uses Logger |
| <<create>> | Client instantiates supplier | Factory creates Product |
| <<call>> | Client calls supplier's operations | Controller calls Validator |
| <<instantiate>> | Client creates instances of supplier | Builder instantiates Components |
| <<parameter>> | Supplier appears as method parameter | Handler receives Event |
In practice, showing every dependency would clutter diagrams. Most diagrams omit dependencies on ubiquitous classes (String, List, Date) and focus on significant domain or service dependencies. Include dependencies that reveal architectural decisions.
The difference between dependency and association is fundamental but sometimes subtle. Understanding when to use each improves diagram accuracy and design clarity.
| Aspect | Dependency (- - - >) | Association (—) |
|---|---|---|
| Reference storage | No stored reference | Stored as attribute |
| Lifetime | Transient (method scope) | Persistent (object scope) |
| Coupling strength | Weakest | Stronger |
| Line style | Dashed line | Solid line |
| Navigability | Always one direction | Can be bidirectional |
| Multiplicity | Not applicable | Specified at both ends |
Dependency Example:
class ReportGenerator {
// Does NOT store DateFormatter
public String generate(Data data) {
DateFormatter fmt = new DateFormatter();
return fmt.format(data.getDate());
}
}
ReportGenerator depends on DateFormatter but doesn't hold a reference to it beyond the method call.
Association Example:
class ReportGenerator {
// STORES reference to Logger
private Logger logger;
public void generate(Data data) {
logger.log("Generating...");
}
}
ReportGenerator has an association with Logger—it maintains a reference as part of its state.
Ask: 'Does the client store the supplier as an instance variable/attribute?' If yes, use association. If the supplier only appears in method signatures, local variables, or return types, use dependency.
Dependencies arise in various contexts. Understanding the different types helps you identify them during design and manage them appropriately.
1. Parameter Dependencies
A class that receives another class as a method parameter depends on it:
123456789
public class OrderProcessor { // Depends on Order through parameter public void process(Order order) { // Uses Order's interface if (order.isValid()) { order.markProcessed(); } }}2. Creation Dependencies
A class that creates instances of another class depends on it:
123456
public class OrderFactory { // Depends on Order through creation public Order create(Customer customer, List<Product> items) { return new Order(customer, items); // Creates Order }}3. Return Type Dependencies
A class that returns another class from a method depends on it:
1234567
public class PaymentService { // Depends on PaymentResult through return type public PaymentResult processPayment(PaymentRequest request) { // Process and return result return new PaymentResult(true, "Success"); }}4. Static Method Dependencies
A class that calls static methods on another class depends on it:
123456
public class DataProcessor { public String process(String input) { // Depends on StringUtils through static call return StringUtils.trim(input); }}Each dependency type creates coupling. If Order's constructor changes, OrderFactory breaks. If PaymentResult is removed, PaymentService fails to compile. Recognizing dependencies helps you manage coupling consciously.
Dependencies are inevitable, but their direction and number significantly affect system maintainability. Well-managed dependencies lead to loosely coupled, testable, and flexible designs.
The Dependency Inversion Principle:
High-level modules should not depend on low-level modules. Both should depend on abstractions. This principle suggests introducing interfaces between dependent classes, allowing implementations to vary without affecting clients.
Before: Concrete dependency
After: Abstraction-based dependency
Beyond class-level diagrams, dependencies appear at the package and component level, revealing architectural structure.
Package dependencies:
Healthy dependency patterns:
| Pattern | Description | Benefit |
|---|---|---|
| Acyclic | No circular dependencies between packages | Packages can be deployed independently |
| Stable dependencies | Depend on things that change less often | Reduces ripple effects from changes |
| Layered | Higher layers depend on lower layers only | Clean separation of concerns |
| Interface boundaries | Cross-package dependencies through interfaces | Loose coupling between modules |
In the diagram above, presentation depends on business, not vice versa. If business depended on presentation, you couldn't reuse business logic in a different UI (mobile app, API). Always consider which direction dependencies should flow.
Not every dependency deserves space in your diagrams. Including too many creates noise; too few hides important coupling. Here's guidance on what to include:
Show dependencies when:
Remember that diagrams serve a purpose. Ask: 'What decisions will this diagram inform?' Include dependencies that matter for that decision. A diagram for onboarding new developers might show different dependencies than one for analyzing performance bottlenecks.
We've explored dependency as the weakest relationship in UML. Let's consolidate the key takeaways:
Module Complete:
With dependency, we've completed our exploration of all major UML relationship types. You now understand:
These relationships form the vocabulary for expressing object-oriented designs precisely and communicating them clearly to your team.
You've mastered all five major UML relationship types! You can now accurately represent object relationships in class diagrams, understanding when each relationship type is appropriate and how to notate them correctly. This foundation enables precise design communication.