The first pattern concerns separation of a role such as clerk, customer, nurse, patient, etc. from the actor who plays the role. This separation solves synchronization problems that arise when the same actor plays many roles within a domain. Sometimes doctors are patients and clerks are customers.
Another observation: actors can be individuals or corporations.
class Role
{
Actor* actor;
// etc.
};
class Actor
{
string name;
Phone *phone;
Address *address;
// etc.
};
class Person: public Actor
{
char gender; // 'M', 'F', or 'U'
string ssn;
string title;
// etc.
};
class Organization: public Actor
{
Person* contact;
// etc.
};
class Role {
Actor actor;
// etc.
}
class Actor {
String name;
Phone phone;
Address address;
// etc.
}
class Person extends
Actor {
private char gender; // 'M', 'F', or
'U'
private String ssn;
private String title;
// etc.
}
class Organization extends Actor {
Person contact;
// etc.
}
An organizational model of an enterprise such as a business, government, or university must take into account two things:
1. Types of organizational units
2. Organizational rules
Assume a particular business is organized into divisions. Each division is organized into departments, and each department is organized into offices. Thus, the types of organizational units are divisions, departments, and offices. The organizational rules are:
1. Departments are subsidiaries of divisions.
2. Offices are subsidiaries of departments.
We might try to capture this information with the following class diagram:
The problem with this model is that both the rules and types of organizations change from time to time and from enterprise to enterprise. It's also not clear how programmers should enforce the constraint attached to the affiliation association.
We can improve the model by modeling organizational types by objects rather than classes:
Now Division, Department, and Office are just sample instances of the OrgType class. If the types of organizational units change, we only need to delete some old OrgType objects and create some new ones.
Of course OrgType objects can also be factories that create properly typed organizations as their products:
// factory method:
Organization* OrgType::makeOrganization()
{
Organization* product = new
Organization();
product->setType(this);
return product;
}
A rule is a general constraint, such as:
Departments are subsidiaries of divisions.
A fact is a specific constraint, such as:
The sales department is a subsidiary of the marketing division.
We can think of the above fact as an instance of the above rule. This suggests that we can think of a rule as the type of a fact. This further suggests that we can explicitly represent rules as classes, and facts as objects:
Thus the facts:
f1: (3 = 4 && 4 = 3)
f2: (4 = 5 && 5 = 4)
would be instances of the symmetry rule:
Modeling rules (i.e., fact types) as classes will produce a massive and difficult to maintain hierarchy of classes. A better approach is to model rules as objects:
If we take this approach in our organizational model, then we notice that affiliation facts are simply the instances of the affiliation association. Since a fact must store its rule/type, we'll need to represent links between parent and subsidiary organizations as link objects, instances of an association class named Affiliation. An interesting observation is that in this case instances of our rule class-- AffilType-- are link objects that link OrgType instances, making AffilType an association class, too:
Organizations are now modeled by object diagrams rather than class diagrams. For example, at most universities:
Rule: Departments are subsidiaries of colleges.
Fact: The Math and Physics departments are subsidiaries of the College of Science.
We can represent this organization using the following object diagram:
class Org
{
friend class OrgType;
private:
Org(string nm = "???",
OrgType* t = 0)
:name(nm), type(t)
{} //default constructor
Org(const Org& o) {} //copy constructor
string name;
OrgType* type;
list<Affiliation*> parents,
subsidiaries;
public:
//getters:
string getName() const { return name; }
string getType() const { return
type->getName(); }
//utility functions:
void addParent(Affiliation* a) {
parents.push_back(a); }
void addSubsidiary(Affiliation* a) {
subsidiaries.push_back(a); }
// etc.
};
class OrgType
{
private:
string name;
list<AffiliationType*> parents,
subsidiaries;
public:
OrgType(string nm =
""):name(nm) {} //default constructor
Org* makeOrg(string temp); //factory method
//getters:
string getName() const {return name;}
//utility functions:
void addParent(AffiliationType* a) {
parents.push_back(a); }
void addSubsidiary(AffiliationType* a)
{ subsidiaries.push_back(a); }
};
class Affiliation
{
private:
friend AffiliationType;
AffiliationType* type;
Org *parent, *subsidiary;
Affiliation(Org* p, Org* s)
:parent(p), subsidiary(s)
{
p->addSubsidiary(this);
s->addParent(this);
}
Affiliation(const Affiliation& a)
{} //copy constructor
public:
//getters:
AffiliationType* getType() {return
type;}
string getParent() {return
parent->getName();}
string getSubsidiary() {return
subsidiary->getName();}
};
class AffiliationType
{
private:
OrgType *parent, *subsidiary;
public:
AffiliationType(OrgType* p, OrgType*
s):parent(p), subsidiary(s)//constructor
{
p->addSubsidiary(this);
s->addParent(this);
}
Affiliation* makeAffiliation(Org* p,
Org* s) //factory method
{
Affiliation* temp = new
Affiliation(p, s);
temp->type = this;
return temp;
}
//getters:
string getParent() {return
parent->getName();}
string getSubsidiary() {return
subsidiary->getName();}
};
Accountability structures model the relationships between roles. Basically, every role has commissioner roles and responsible roles. For example, a doctor is commissioned by a patient. Nurses are a doctor's responsibles because they are commissioned by doctors.
This is similar to the parent-subsidiary relationship between organizations, but commissioners and responsibles can be any type of role. For example, the CEO of a company is responsible to the company's board of directors, the vice president of the marketing division is responsible to the CEO, the marketing division is responsible to its vice president. Clearly there are many links between commissioners and responsibles and the rules between them can be complicated and they can change frequently.
To cope with this volatility, we can use a version of our reusable affiliation model:
Accountability is called chain of command in military organizations. Here's a bit of the the Star Fleet chain of command:
Rule: Generals command captains.
Rule: Captains command ensigns.
Fact: General Hershey commands Captain Picard.
Fact: General Hershey commands Captain Janeway.
Fact: Captain Janeway commands Ensign Pulver.
Our object model:
An account manager, like Quicken, allows users to open and close accounts, transfer funds from one account to another, view account statements, and view transaction histories.
Users can create two types of accounts: internal and external. External accounts are accounts controlled by external actors such as credit card accounts and employer payroll accounts. External accounts can have negative balances. Internal accounts are accounts controlled by the user, such as checking accounts and saving accounts. These types of accounts may never have negative balances. The sum of the balances of all accounts controlled by an account manager must always be 0 USD. (Although all account balances are in US dollars, users may transfer funds in different currencies.)
Initially, users create named accounts using the external and internal commands:
-> external visa
done
-> internal checking
done
-> external employer
done
-> internal savings
done
Users transfer funds between accounts using the transfer command followed by the source account, destination account, and the quantity to be transferred:
-> transfer employer checking 5000 USD
done
-> transfer checking visa 200 DM
done
-> transfer checking savings 6000 USD
error: insufficient funds
-> transfer checking savings 200 JY
done
Users can use the statement command to see a printout of all entries associated with an account as well as its current balance:
-> statement checking
entry 1: 4.20.2001 12:00 5000 USD from employer
entry 2: 4.20.2001 12:01 -200 DM to visa
entry 3: 4.20.2001 12:02 -200 JY to savings
current balance = 4948.32 USD
-> statement employer
entry 1: 4.20.2001 12:00 5000 USD to checking
current balance = -5000 USD
The transaction command shows all transactions that have taken place:
-> transactions
transaction 1: 5000 USD from employer to checking
transaction 2: -200 DM from checking to visa
transaction 3: -200 JY from checking to savings
class Entry
{
public:
Entry(Quantity amt = 0)
{
amount = amt;
booked = time(0);
}
Time getBooked() { return booked; }
Quantity getQuantity() { return amount;
}
private:
Quantity amount;
Time booked;
};
class Account
{
public:
Account(Holder* h = 0, string pswd =
"???", bool a = true)
{
assetAccount = a;
balance = 0;
password = pswd;
holder = h;
if (holder) holder->add(this);
}
vector<Entry> getStatement()
{
if (!authorized())
throw runtime_error("Access
denied!");
return statement;
}
friend class Transaction;
private:
bool authorized();
void deposit(Quantity amt);
void withdraw(Quantity amt);
Quantity balance;
bool assetAccount; // 0 <= balance
vector<Entry> statement;
string password;
Holder* holder; // an actor
};
class Transaction
{
public:
Transaction(Account* fa = 0, Account*
ta = 0, Quantity amt = 0)
{
fromAccount = fa;
toAccount = ta;
amount = amt;
}
bool commit();
private:
Account* fromAccount;
Account* toAccount;
Quantity amount;
};
void Account::withdraw(Quantity amt)
{
if (!authorized())
throw runtime_error("Access
denied!");
if (assetAccount && balance
< amt)
throw runtime_error("Sorry,
insufficient funds!");
statement.push_back(Entry(-amt));
balance -= amt;
cout << "current balance =
$" << balance << endl;
}
bool Transaction::commit()
{
try
{
fromAccount->withdraw(amount);
}
catch(runtime_error e)
{
cerr << e.what() <<
endl;
return false;
}
try
{
toAccount->deposit(amount);
}
catch(runtime_error e)
{
cerr << e.what() <<
endl;
fromAccount->deposit(amount); //
roll back
return false;
}
return true;
}
An account manager is a simple example of a transaction processor (TP). Generally speaking, a TP is a server that allows multiple clients to create and commit transactions. These transactions can be abstractly understood as transferring quantities from one account to another. The quantities might be money, data, or items.
A TP must guarantee the ACID properties:
A = Atomic: Committing a transaction is an all or nothing situation. If the transaction can't be completed, then the database must be rolled back to its state before the transaction began.
C = Consistency: the database must be consistent after each transaction is committed. For example, the sum of all balances must be 0 before and after each transaction.
I = Indivisible: Transactions can't be interrupted. For example, a wife withdrawing the last $20 from a joint account can't be interrupted by a husband who also withdraws the last $20.
D = Durable: Once committed, the effects of a transaction can't be undone except by a compensating transaction.
An inventory system is a collection of line items. A line item is essentially an account:
We often need to represent important actions that take place in the application domain such as transactions and observations. It is often important to keep track of when and where an action took place. It is also important to keep track of who participated in the action.
Actions can be represented using state diagrams:
Some actions consist of a sequence of subactions. For
example, "drive to work" might involve the sequence of subactions:
"start the car", "turn right at Main", "turn left at Elm",
"park the car", etc. A sequence of actions is called a plan. A routine plan is called a process or workflow.
We could simply model a plan as both an aggregation of
Action and a specialization of Action:
This isn't such a great idea, because each action needs to
keep track of the actions it depends on (dependants) and the actions that
depend on it (consequences). However, the same action might appear in many
plans. In each of these plans it may have different dependants and consequences.
For example, a doctor might devise a treatment plan for a patient:
1. measure Smith's white cell count
2. administer anti-biotic to Smith
3. wait 24 hours and repeat if necessary
Action 2 might also appear in a nurse's workflow:
1. administer seditive to Jones
2. administer anti-biotic to Smith
3. administer analgesic to Johnson
We can solve this problem by introducing an association class between Plan and Action:
class Plan
{
list<ActionRef*> actions;
// etc.
};
class Action
{
list<ActionRef*> plans; // i.e.,
the plans I am part of
// etc.
};
class ActionRef
{
Plan* myPlan;
Action* myAction;
list<ActionRef*> dependants,
consequences;
// etc.
};
Peter Coad, Eric Lefebvre, and Jeff De Luca; Java Modeling in Color with UML; Prentice Hall;1999.
Peter Coad with Mark Mayfield; Object Models: Strategies, Patterns, and Applications; Prentice Hall; 1997.
Martin Fowler; Analysis Patterns; Addison-Weslet; 1996.
1. The 97th Infantry Division of the U.S. Army was active in Europe near the end of World War II. The history and organization of this division is documented on the web at:
http://www.plaisted.org/brothers/97THID.HTM
Using the organizational model developed in class, draw an object diagram that shows objects representing the A, B, and C companies of the 303rd and 386th regiments. Your diagram should include all parent organizations that are above these companies in the organizational hierarchy.
2. Translate your model into C++ or Java.
3. The accountability structure of San Jose State University is documented on the web at:
http://www.sjsu.edu/pres/orgchart.pdf
Using the accountability model developed in class, draw an object diagram that shows objects representing the head coach of the football team, the chairman of the Anthropology department, the chairman of the Mathematics/Computer Science department, and all of their commissioners.
4. Translate your model into C++ or Java
5. The National Football League (NFL) is divided into the National Football Conference (NFC) and the American Football Conference (AFC). Each conference is divided into Western, Central, and Eastern Divisions. Each division currently consists of five teams. Using the organization model developed in class, draw an object diagram that shows the following teams as well as their conferences, divisions, and league. (You can find out which divisions these teams belong to at Yahoo or at www.nfl.com). Translate your diagram into a C++ program.
Tennessee Titans
San Francisco 49ers
Oakland Raiders
Arizona Cardinals