Let's say I have a factory class that creates some series of concrete types. I've documented all of these classes in a UML static class diagram. How do you communicate the relationship between the factory class and the types it creates? Normally you'd use aggregation or composition, except that the factory doesn't actually store those created objects as part of its own state (my understanding is this is a requirement). In the past I've done a dotted line with an arrow with a note that says "uses" or "creates". What's a good rule of thumb here?
You've correctly noticed that there's no composition or aggregation in the case of the factory. Luckily, there are many other relationship types:
Source: https://en.wikipedia.org/wiki/File:Uml_classes_en.svg by Yanpas, used under a CC-BY-SA 4.0 license.
The most general kind of relationship is the “association” (“link”) relationship. In your case, it is a directed relationship: the factory uses the product, so the arrow would point from the factory to the product. I use the “association” relationship whenever one class would call methods on the other. A constructor is effectively a static method, so this works. Aggregation and Composition are more specific kinds of aggregation and introduce a sense of ownership that is not applicable here.
A dependency would also be possible, but denotes a weaker relationship. Associations show links that are actually present in your code. If the factory at any point holds a reference to the product, an association is in order – it shows that navigating the link (calling a method) is possible. A dependency relationship is more like a design-time relationship, and makes no statement about the run-time structure of the system.
In any case, you can annotate the relationship with an UML stereotype. The
<<use>> stereotype would be quite general, and
<<create>> would fit better.
To summarize, I would expect something like this:
In a class diagram, you can document all sort of dependencies, with the doted line and arrow: among other there is the
The principle is that the source depends on the target. Indeed, it' the case for your factory. The factory depends on the Product it creates: should a product change, there is a risk that the factory could have to be changed as well (example: new argument in the constructor). The reverse is not necessary true: a Product could in principle be independent of the Factory that creates it.
As you rightly pointed out, associations, aggregations and compositions are structural relationships. This means that there is a durable way to represent the relationship between objects of these classes (e.g. storage, pointer/references, mapping functions, etc...).
Additional web reading: