🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Bootcamp:Chapter 9
This page was created by Hans.karlsen on 2022-10-23. Last edited by Wikiadmin on 2026-07-29.

This chapter teaches you how to restructure the car-ownership model with UML inheritance and automate a sale by using a state machine on Car.

This is Chapter 9 of the MDriven Bootcamp. Continue from Chapter 8, or return to Chapter 1.

What you will build

You will replace the earlier ownership relationships on Person with a shared abstract CarOwner type. A person, dealer, factory, and scrap yard can then participate in car ownership through the same associations.

You will also give each Car a state machine with this sale lifecycle:

BrandNew --InitiateSale--> InOwnershipTransaction --CloseSale--> OwnershipStable
                                      ^                    |
                                      |----InitiateSale----|

When a sale begins, the model creates a CarTransferOwnershipDocument. The sale can close only after the document has a buyer. When it closes, the car moves from the seller's current cars to the buyer's current cars, while the seller retains ownership history.

Before you begin

  1. Open the model you completed in Chapter 8.
  2. Save frequently with Ctrl+S.
  3. Run the web application after each major model or ViewModel change. This makes association-end and AutoForm errors easier to isolate.

Refine the existing layout

Continue the placing-container work from the previous chapter.

  1. Remove the 20-pixel margin from the buttons.
  2. Select the relevant placing container and set Align Items to end so that the controls align at the bottom line.
  3. Test combinations of Justify Content and Align Items in the web application.

For example, change the browser width after each setting. Observe whether the buttons are distributed along the main layout direction by Justify Content and aligned across that direction by Align Items. Keep the configuration that gives the intended bottom alignment without relying on button margins.

Model a common car owner

Add the inheritance hierarchy

Create a common owner type so that people and organizations can own cars.

  1. Add the classes CarDealer and CarOwner.
  2. Add generalization arrows from CarDealer to CarOwner and from Person to CarOwner.
  3. Add the attribute Name:String to CarOwner and save the model.

A generalization makes the class at the arrow source a subclass and the common class a superclass. Here, Person and CarDealer are subclasses; CarOwner is their superclass. Read UML Inheritance before continuing if this distinction is unclear.

Resolve the duplicate Name attribute

After adding CarOwner.Name, MDriven Designer reports Name as a doublet because Person already has that attribute and inherits from CarOwner.

  1. Remove Name from Person.
  2. Keep Name:String on CarOwner.
  3. Save and verify that the duplicate-name error is gone.

For example, a Person can still display its name because it inherits CarOwner.Name; you maintain that attribute in one place.

Move ownership history to CarOwner

The existing historical-ownership relationship must work for all kinds of owners, not only people.

  1. Move the multi-link association between Car and Person—the association that has the HistoricOwnership association class—to Car and CarOwner.
  2. Rename the Car association end from CarsPersonUsedToOwn to CarsOwnerUsedToOwn.
  3. Apply the changes and inspect the reported errors.
  4. Recreate and refresh the affected AutoForms to update references to the old association end.
  5. Select CarOwner. In the Property Inspector, set IsAbstract to True.

An abstract class is a shared type that you do not create directly. In the diagram, CarOwner is shown in italics. You will create a Person, CarDealer, CarFactory, or ScrapYard instead.

Model current ownership and sale documents

Add current ownership

  1. Add a new association between CarOwner and Car.
  2. Name the Car association end CurrentlyOwnedCars.
  3. Delete the old relationship that represented the currently owned car.
  4. Update the AutoForms, save the model, and resolve any remaining errors.
  5. In the Person form, delete the ComboBox that displayed the removed current-car relationship.

For example, a factory can have several values in CurrentlyOwnedCars. This is why current ownership belongs on CarOwner, rather than being limited to Person.

Add a transfer document

  1. Add a class named CarTransferOwnershipDocument.
  2. Add an association from Car (0..1) to CarTransferOwnershipDocument (*).
  3. Add an association between CarOwner and CarTransferOwnershipDocument. Name the multi-valued end DocumentsForSales (*) and the single-valued end Seller (0..1).
  4. Add another association between CarOwner and CarTransferOwnershipDocument. Name the multi-valued end DocumentsForPurchase (*) and the single-valued end Buyer (0..1).
  5. Add an association between CarTransferOwnershipDocument and Car. Name the single-valued document end CurrentTransaction. Set the Car end to not navigable.

The document records the two roles separately. For example, BMW in Munich can be the Seller, while a Person is the Buyer.

Add more owner types

  1. Add CarFactory and make it a subclass of CarOwner.
  2. Add ScrapYard and make it a subclass of CarOwner.

Add the Car state machine

A state machine defines the valid states of an object and the transitions that move it between states.

  1. Add a state machine to class Car.
  2. Add the state BrandNew.
  3. Add the state InOwnershipTransaction.
  4. Add a transition from BrandNew to InOwnershipTransaction and name its trigger InitiateSale.
  5. Add the state OwnershipStable.
  6. Add a transition from InOwnershipTransaction to OwnershipStable and name its trigger CloseSale.
  7. Add a transition from OwnershipStable to InOwnershipTransaction. Reuse the InitiateSale trigger.

Use the spelling InOwnershipTransaction consistently. Earlier exercise text used InOwnerShipTransaction; do not create two differently named states.

Add actions and guards

An entry action runs when an object enters a state. A guard is an OCL expression that must evaluate as true before a transition may run. Learn the expression syntax in OCL.

Create the transaction when a sale starts

  1. Select InOwnershipTransaction.
  2. In the Property Inspector, click the ellipsis button for Entry to open the Action Editor for that state.
  3. Add the course-provided Action Language expression that:
    1. creates a CarTransferOwnershipDocument;
    2. adds it to the car's collection of ownership-transfer documents; and
    3. assigns the new document's Seller from the car's current owner.
  4. Extend that entry action to assign the created document to CurrentTransaction.

The result should be that triggering InitiateSale creates one transaction document for that car and identifies its seller.

Require a buyer before closing

  1. Select the CloseSale transition.
  2. Open its Guard OCL editor.
  3. Enter:
self.CurrentTransaction.Buyer->notempty

This guard prevents the sale from closing while the current transaction document has no buyer.

Transfer current ownership and retain history

  1. Select OwnershipStable and open its Entry Action Editor.
  2. Add the course-provided Action Language expression that removes the car from the seller's CurrentlyOwnedCars and adds it to the buyer's CurrentlyOwnedCars.
  3. Extend the same entry action with the course-provided update that records the seller's CarsOwnerUsedToOwn history.

For example, after a sale closes, the buyer sees the car in CurrentlyOwnedCars. The seller no longer sees it there, but the historical ownership relationship still records that the seller previously owned it.

Prevent a sale without an owner

  1. Select the transition from BrandNew to InOwnershipTransaction.
  2. Open its Guard OCL editor.
  3. Enter:
self.CarOwner->notempty

This guard means a car needs a CarOwner before the initial sale transaction can begin.

Add trigger actions

  1. On the InitiateSale trigger, add a ClassAction for the trigger.
  2. On the CloseSale trigger, add a ClassAction for the trigger.
  3. Refresh the AutoForms.
  4. Save the model and test it in the web application.

Test the complete ownership flow

  1. Create a CarFactory object.
  2. Set its name to BMW in Munich.
  3. Add a Car to that factory.
  4. Trigger InitiateSale for the car. Verify that a CarTransferOwnershipDocument is created.
  5. Assign a buyer on the CarTransferOwnershipDocument.
  6. Trigger CloseSale.
  7. Verify that current ownership moved from the factory to the buyer and that historical ownership was retained.
  8. Set the default representation of CarOwner to:
self.Name

The default representation makes owner selections readable in generated forms: users see names such as BMW in Munich rather than an internal object representation.

Troubleshooting

Symptom Check
Name is reported as a doublet Remove Name from Person. It inherits Name from CarOwner.
AutoForms report errors after an association rename or move Refresh or recreate the affected AutoForms. They can still reference the removed Person-based association end.
CloseSale is unavailable Open the current transaction and assign Buyer. The guard requires self.CurrentTransaction.Buyer->notempty.
InitiateSale is unavailable from BrandNew Ensure the car has a CarOwner; the transition guard requires self.CarOwner->notempty.

Next chapter

Continue to Chapter 10.

See also