UML um...limited?

  • Follow


So after having using UML many times, now I am using it for some real,
as in a real information system with real complexity.

I find myself stumped as how to model the architecture that handles
maintaining a history of object instances.

Say I have a person, visit, account, order and charge classes.

Charges are created on order and associated with an account, order and
person.

Orders are placed on a visit.  An account is a many to many association
with a visit.  i.e. a visit can span multiple accounts or a single
account can be used for many visits.

The classes are something like:

class person {  private String name; }
class visit {
  private int visitNbr;
  private int accountNbr;
 }
class account { private int accountNbr; }
class order{ private int visitNbr; private int orderNbr; }
class charge{
  private int chargeNbr;
  private int accountNbr;
  private int visitNbr;
}

On the first day my object diagram has
 person[name=Tim]
 visit[ visitNbr=1,  accountNbr=1]
 account[ accountNbr=1]
 order[ visitNbr=1, nbr=10]
 charge [visitNbr=1, accountNbr=1, chargeNbr = 100]

On the second day we have a new account no charges
 person[name=Tim]
 visit[ visitNbr=1,  accountNbr=2]
 account[ accountNbr=2]

On the third day we have a new visit, account and charge
 person[name=Tim]
 visit[ visitNbr=2,  accountNbr=3]
 account[ accountNbr=3]
 order[ visitNbr=2, nbr=11]
 charge [visitNbr=1, accountNbr=1, chargeNbr = 101]

Now the user issues the query - show me the accountNbr for orderNbr=1

So the order object instance with orderNbr=1 references visitNbr=1 but
the visit with visitNbr=1 now points to account with accountNbr=2

So this would be wrong... and the only thing to do would be to lookup
the account using the charge (terrible dependency on an unrelated
object) or have the first account reference a 'specific instance of
visit' instead of 'lookup up a visit by visit nbr'

I know this sounds complicated but it is a very situation - somewhat
easier to model in the database - keeping a history of changing rows.

To my question:  how on earth to you model this to communicate how it
should work?

0
Reply timasmith (248) 7/3/2006 12:19:09 AM

timasmith@hotmail.com wrote:

> So after having using UML many times, now I am using it for some real,
> as in a real information system with real complexity.
> 
> I find myself stumped as how to model the architecture that handles
> maintaining a history of object instances.
> 
> Say I have a person, visit, account, order and charge classes.
> 
> Charges are created on order and associated with an account, order and
> person.
> 
> Orders are placed on a visit.  An account is a many to many
> association with a visit.  i.e. a visit can span multiple accounts or
> a single account can be used for many visits.

	The post below was typed by just following your m:n relationship
remark, however at the end I realized that visit - account isnt an m:n
relationship per se. What's odd is that you state 'a visit can span
multiple accounts. Perhaps I associate 'visit' with something which can
only be done by a single account, so I may make a mistake there. In
case I do and it is an m:n relation, please read on. If it's not,
please forget the stuff below ;)

> The classes are something like:
> 
> class person {  private String name; }
> class visit {
>   private int visitNbr;
>   private int accountNbr;
>  }

	If visit - account is m:n you do need an intermediate entity, or you
have to model accounts as a list/collection in visit. You now have
modelled it as visit m:1 account.

	So I'd do:
class accountvisit {
	private int visitNbr;
	private int accountNbr;
}

	and visit then becomes:
class visit {
	private int visitNbr;
	private Collection accountVisits;
	private Collection accounts;
}

	and account then becomes:
class account {
	private int accountNbr;
	private Collection accountVisits;
	private Collection visits;
}

	I used 'Collection' as the type for the collection of accountvisit
objects. This should be a typed collection in the language of your
choice, which seems to be Java but I'm not that familiar with the
semantics of Java nor do I know if you use Java 5 or not so I skipped
the generics ;)

	visit.accounts is a readonly collection which contains the results of
the traversion of all the accountVisits.account objects in
accountVisits. account.visits contains the same about visits related to
account.

	It's readonly because m:n relationships are really a result of a
'snapshot' or 'view' on the current set of intermediate entities
representing the relationships betweeen a given instance of visit and a
given instance of account.


	This is just one solution, see below for another one.

> class account { private int accountNbr; }
> class order{ private int visitNbr; private int orderNbr; }
> class charge{
>   private int chargeNbr;
>   private int accountNbr;
>   private int visitNbr;
> }
> 
> On the first day my object diagram has
>  person[name=Tim]
>  visit[ visitNbr=1,  accountNbr=1]
>  account[ accountNbr=1]
>  order[ visitNbr=1, nbr=10]
>  charge [visitNbr=1, accountNbr=1, chargeNbr = 100]
> 
> On the second day we have a new account no charges
>  person[name=Tim]
>  visit[ visitNbr=1,  accountNbr=2]
>  account[ accountNbr=2]
> 
> On the third day we have a new visit, account and charge
>  person[name=Tim]
>  visit[ visitNbr=2,  accountNbr=3]
>  account[ accountNbr=3]
>  order[ visitNbr=2, nbr=11]
>  charge [visitNbr=1, accountNbr=1, chargeNbr = 101]
> 
> Now the user issues the query - show me the accountNbr for orderNbr=1
> 
> So the order object instance with orderNbr=1 references visitNbr=1 but
> the visit with visitNbr=1 now points to account with accountNbr=2
> 
> So this would be wrong... and the only thing to do would be to lookup
> the account using the charge (terrible dependency on an unrelated
> object) or have the first account reference a 'specific instance of
> visit' instead of 'lookup up a visit by visit nbr'
> 
> I know this sounds complicated but it is a very situation - somewhat
> easier to model in the database - keeping a history of changing rows.

	Why would it be significantly different in classes? You should take a
step back and think in entities: what's a visit, what's an account,
what are their relationships? Remember, that's on an abstract level
which is not projectable in all cases to a class diagram.

	Above I've used an intermediate entity, which in fact suggests an
objectified relationship between visit and account. You also could have
done:
class visit {
	private int visitNbr;
	private Collection accounts;
}

class account {
	private int accountNbr;
	private Collection visits;
}

	this also implies an m:n relationship between visit and account.
However, there's no intermediate entity which physically represents the
relationship: the relationship is formed when an account object is
placed in the visit.accounts collection.

	This could work, it only makes the code a bit more complex to
understand: the presence of an entity in a collection then suggests an
m:n relationship, which is information obtained after understanding
that the related entity also has a collection for the other entity. You
can't see from
class visit {
	private int visitNbr;
	private Collection accounts;
}

	that visit -account is an m:n relation, you can only know that for
sure if you ALSO inspect account. Otherwise from the class above, visit
- account could also be an 1:n relation. With a present intermediate
entity this is more clear, there's no ambiguity when reading the code:
what's there is what's defined.

	Mapping the solution without an intermediate entity onto a database
schema will also make it harder, as you then have to create the
intermediate entity under the hood. This could work, however when you
want to add another attribute to the intermediate entity which isn't
part of the uniquely identifying attribute set (PK), you're faced with
the fact that all of a sudden the entity becomes unhidden, which could
force you to change your code.

	FB

-- 
------------------------------------------------------------------------
Lead developer of LLBLGen Pro, the productive O/R mapper for .NET
LLBLGen Pro website: http://www.llblgen.com
My .NET blog: http://weblogs.asp.net/fbouma
Microsoft MVP (C#) 
------------------------------------------------------------------------
0
Reply Frans 7/3/2006 8:38:42 AM


Responding to Timasmith...

> So after having using UML many times, now I am using it for some real,
> as in a real information system with real complexity.
> 
> I find myself stumped as how to model the architecture that handles
> maintaining a history of object instances.
> 
> Say I have a person, visit, account, order and charge classes.
> 
> Charges are created on order and associated with an account, order and
> person.
> 
> Orders are placed on a visit.  An account is a many to many association
> with a visit.  i.e. a visit can span multiple accounts or a single
> account can be used for many visits.

I agree with Bouma that you need to put some more words around these 
requirements.  Please indicate where the following model differs from 
your problem.  [Note that you imply that [Visit] is an associative 
object, but I don't see why.  Nor do I see a need a *:* relationship.]

          1        R1        owns *
[Person] ------------------------- [Account]
+ name                              + accountNo
     | 1                                 | 1
     |                                   | made against
     | R2                                |
     |                                   |
     | makes                             |
     | *                                 |
[Visit]                                 | R5
+ visitNo                               |
     | 1                                 |
     | associated with                   |
     |                                   |
     | R3                                |
     |                                   |
     | * 1                               |
[Order] --------------------------------+
+ OrderNo
   1 |
     |
     | R4
     |
     | composed of
     | *
[Charge]

Now the user wants to know the account number for an [Order] identified 
as '1'.  To get that one finds the Order that has identity '1' and then 
navigates the R5 relationship to get the account number.

One can do that by a static Find on the [Order] class and "walking" a 
pointer in [Order] to get to the relevant Account.  So let's look at 
something more complicated.  Let's say the user wants the account 
numbers of all the Orders a Person (Tim) made associated on his third visit.

First one must find the Person identified as 'Tim', which is the 
starting point of the query.  Once that is done, it is a simple case of 
relationship navigation: R2 -> R3 -> R5.  One finds the Visit identified 
as '3' in the R2 collection.  Then one "walks" each R3 collection member 
for that Visit.  For each Order member of the R3 collection, one "walks" 
the R5 pointer to get the Account number and add it to a list.  When all 
the relationships have been navigated, one returns the list of account 
numbers to the user.

The point here is that so long as one instantiates the relationships 
correctly with pointers and collections, one can service any query 
unambiguously.  (The collection classes implement what I believe you 
mean by 'history'.)  The problem you have in your code is that you are 
trying to implement with RDB-style referential attributes rather than 
the more natural (for a program in memory) pointers and collections. 
That leads to problems for any relationship with a '*' multiplicity 
because you overwrite your single identity attribute with each new 
member of the collection.

Thus I think your "history" problem lies in trying to merge two 
different paradigms: OO relationships and RDB relationships.

Whenever there are multiple participants in a relationship, you need to 
create a list (collection) of the relevant participants if you want to 
navigate the relationship from the 1-side.   [In an RDB there is a 
single referential attribute, but it is on the '*'-side.  So to access, 
as you do, from the 1-side you have to search to find the right one 
(i.e., find the tuple on the '*'-side having a referential attribute 
with the same value as the tuple key on the 1-side).]  You are 
attempting to avoid that by keeping the identity on the 1-side as a 
single attribute but it gets written over as participants are added.

Another problem is that you are letting the individual objects know too 
much about their context rather than letting the query just navigate the 
relationships.  For example, in my model above [Charge] does not need to 
know anything about visits.  It only needs relationship infrastructure 
to reach the relevant Order and the relevant Account (and probably not 
even Order).  The context of [Visit] -> [Order] -> [Charge] is handled 
by relationships, not the object semantics of [Charge].


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions  -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
Pathfinder is hiring: 
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



0
Reply h.lahman (3484) 7/4/2006 6:33:12 PM

So Visit is an associative object because when orders are placed, they
are placed on a visit not an account.  Accounts are associated with a
visit but the trouble is the account may change during a visit.

I guess one could have a collection of accounts on a Visit, but I dont
think that helps answer the question 'which account were orders placed
against'.

If I understand Bouma I could add an associative object AccountOrder
perhaps but it seems very icky to add such objects - seems too
contrived in a pure OOP model.

But I totally agree I need to go back to the requirements definition
and rethink this.

H. S. Lahman wrote:
>
> I agree with Bouma that you need to put some more words around these
> requirements.  Please indicate where the following model differs from
> your problem.  [Note that you imply that [Visit] is an associative
> object, but I don't see why.  Nor do I see a need a *:* relationship.]
>
>           1        R1        owns *
> [Person] ------------------------- [Account]
> + name                              + accountNo
>      | 1                                 | 1
>      |                                   | made against
>      | R2                                |
>      |                                   |
>      | makes                             |
>      | *                                 |
> [Visit]                                 | R5
> + visitNo                               |
>      | 1                                 |
>      | associated with                   |
>      |                                   |
>      | R3                                |
>      |                                   |
>      | * 1                               |
> [Order] --------------------------------+
> + OrderNo
>    1 |
>      |
>      | R4
>      |
>      | composed of
>      | *
> [Charge]
>
> Now the user wants to know the account number for an [Order] identified
> as '1'.  To get that one finds the Order that has identity '1' and then
> navigates the R5 relationship to get the account number.
>
> One can do that by a static Find on the [Order] class and "walking" a
> pointer in [Order] to get to the relevant Account.  So let's look at
> something more complicated.  Let's say the user wants the account
> numbers of all the Orders a Person (Tim) made associated on his third visit.
>
> First one must find the Person identified as 'Tim', which is the
> starting point of the query.  Once that is done, it is a simple case of
> relationship navigation: R2 -> R3 -> R5.  One finds the Visit identified
> as '3' in the R2 collection.  Then one "walks" each R3 collection member
> for that Visit.  For each Order member of the R3 collection, one "walks"
> the R5 pointer to get the Account number and add it to a list.  When all
> the relationships have been navigated, one returns the list of account
> numbers to the user.
>
> The point here is that so long as one instantiates the relationships
> correctly with pointers and collections, one can service any query
> unambiguously.  (The collection classes implement what I believe you
> mean by 'history'.)  The problem you have in your code is that you are
> trying to implement with RDB-style referential attributes rather than
> the more natural (for a program in memory) pointers and collections.
> That leads to problems for any relationship with a '*' multiplicity
> because you overwrite your single identity attribute with each new
> member of the collection.
>
> Thus I think your "history" problem lies in trying to merge two
> different paradigms: OO relationships and RDB relationships.
>
> Whenever there are multiple participants in a relationship, you need to
> create a list (collection) of the relevant participants if you want to
> navigate the relationship from the 1-side.   [In an RDB there is a
> single referential attribute, but it is on the '*'-side.  So to access,
> as you do, from the 1-side you have to search to find the right one
> (i.e., find the tuple on the '*'-side having a referential attribute
> with the same value as the tuple key on the 1-side).]  You are
> attempting to avoid that by keeping the identity on the 1-side as a
> single attribute but it gets written over as participants are added.
>
> Another problem is that you are letting the individual objects know too
> much about their context rather than letting the query just navigate the
> relationships.  For example, in my model above [Charge] does not need to
> know anything about visits.  It only needs relationship infrastructure
> to reach the relevant Order and the relevant Account (and probably not
> even Order).  The context of [Visit] -> [Order] -> [Charge] is handled
> by relationships, not the object semantics of [Charge].
>
>
> *************
> There is nothing wrong with me that could
> not be cured by a capful of Drano.
>
> H. S. Lahman
> hsl@pathfindermda.com
> Pathfinder Solutions  -- Put MDA to Work
> http://www.pathfindermda.com
> blog: http://pathfinderpeople.blogs.com/hslahman
> Pathfinder is hiring:
> http://www.pathfindermda.com/about_us/careers_pos3.php.
> (888)OOA-PATH

0
Reply timasmith (248) 7/4/2006 9:09:30 PM

Responding to Timasmith...

> So Visit is an associative object because when orders are placed, they
> are placed on a visit not an account.  Accounts are associated with a
> visit but the trouble is the account may change during a visit.

My pushback is: Why are Accounts associated with a Visit rather than 
with an Order or a Charge?

[BTW, in some contexts I've seen an Account is associated with an 
individual line item in an order rather than the order itself if an 
'order' is something like a purchase order.  That's because the PO 
collects items for a vendor but each item may be purchased for different 
internal reasons that correspond to different internal accounts.]

In fact, I am fuzzy about what a 'visit' is in the problem space, 
especially if accounts change /during/ a visit.

One generally uses an associative object to manage a relationship 
between two other objects.  It is mandatory for *:* relationships 
because there is no simple relationship infrastructure like a collection 
class that can deal with the complexity of matching up participants. 
One can also use associative objects for 1:* or even 1:1 relationships 
if there are complicated business rules about individual assignments of 
participants.  But in the end the associative object exists to manage 
some other association's participants.  Without a better description of 
what a 'visit' is, I don't see how that applies.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions  -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
Pathfinder is hiring: 
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



0
Reply h.lahman (3484) 7/5/2006 3:42:56 PM

On 2006-07-02 19:19:09 -0500, timasmith@hotmail.com said:

> So after having using UML many times, now I am using it for some real,
> as in a real information system with real complexity.

> [snip]

> To my question:  how on earth to you model this to communicate how it
> should work?

I don't think your problem has anything to do with UML.  What you need 
to do is understand your requirements.

I suggest that you write down a sequence of scenarios (not in UML) and 
describe how the system behaves in each from the point of view of the 
user.   You might use decision tables for this purpose.

There is a wonderful book called: "Fit for Developing Software" by Rick 
Mugridge and Ward Cunningham, that describes how to use decision tables 
to explore scenarios and describe requirements.
-- 
Robert C. Martin (Uncle Bob)��| email: unclebob@objectmentor.com
Object Mentor Inc.� � � � � ��| blog:��www.butunclebob.com
The Agile Transition Experts��| web:���www.objectmentor.com
800-338-6716� � � � � � � � ��|



0
Reply unclebob2 (2724) 7/6/2006 1:31:15 PM

5 Replies
24 Views

(page loaded in 0.134 seconds)

5/19/2013 4:44:46 PM


Reply: