Re: Strategy Pattern for Multiple Type Codes
From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 02/11/05
- Next message: Robert C. Martin: "Re: Is it better to pass objects or discrete properties to methods?"
- Previous message: H. S. Lahman: "Re: Is it better to pass objects or discrete properties to methods?"
- In reply to: Clint Shank: "Strategy Pattern for Multiple Type Codes"
- Next in thread: topmind: "Re: Strategy Pattern for Multiple Type Codes"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 11 Feb 2005 17:40:59 GMT
Responding to Shank...
> Please bear with the contrived example... Suppose we have an Employee
> class whose functionality depends on several type codes,
> manager/engineer and hourly/salary. Now, any Employee can be any
> combination (manager/hourly, manager/salary, engineer/hourly,
> engineer/salary). Some of the behavior depends on just the
> manager/engineer type code. Some of the behavior depends on the
> hourly/salary type code. And some of the behavior depends on all four
> cases. Specifically,
Basically the GoF Strategy pattern would be applied as:
* role for 1
[Employee] ------------------- [Strategy]
+ doIt() R1 + doIt()
A
| R2
+------------+--------...
| |
[Engineer] [Manager]
Where the [Employee]/[Strategy] relationship is instantiated dynamically
based upon the context. So all you need is:
class Employee
{
private:
Strategy* myStrategy;
...
public:
void setStrategy (Strategy* s) {myStrategy = s;};
void doIt() {myStrategy->doIt();};
...
}
Now whoever determines the context of how one views the Employee
(manager vs. engineer vs. whatever) sets the pointer when that context
is determined. Any subsequent accesses to the actual behavior then Just
Works so long as that context prevails.
In you case it may be important that one keeps track of which particular
strategies are available for an individual Employee. That just requires
a second relationship:
R3
+-------------------------------+
| |
| applies to |
| 1 * role for 1 | *
[Employee] ------------------- [Strategy]
+ doIt() R1 + doIt()
A
| R2
+------------+--------...
| |
[Engineer] [Manager]
Now when the Strategy is selected for a particular context (R1) it can
be selected from the subset available to the Employee in hand (R3).
>
> Case 1: Just engineer/manager
> if (engineer)
> do engineer behavior
> else
> do manager behavior
Relevant subclasses of [Strategy] are: [Engineer], [Manager]
>
> Case 2: All 4
> if (engineer)
> if (hourly)
> do engineer/hourly behavior
> else
> do engineer/salary behavior
> else
> if (hourly)
> do manager/hourly behavior
> else
> do manager/salary behavior
Relevant subclasses are: [Engineer/Hourly], [Engineer/Salary],
[Manager/Hourly], [Manager/Salary]
>
> Case 3: 3 cases
> if (engineer)
> do engineer behavior that doesn't care about hourly/salary
> else
> if (hourly)
> do manager/hourly behavior
> else
> do manager/salary behavior
Relevant subclasses are: [Engineer], [Manager/Hourly], [Manager/Salary]
If all three cases are relevant, one needs subclasses for
[Engineer/Neutral] and [Manager/Neutral]. That is suspiciously
symmetric, so...
>
> So these are the contrived cases that actually map to my real world
> cases. There's not many, maybe only these three, but I'm wondering if
> I should do something clever with the Strategy design pattern. Only
> there are multiple type codes and the behavior sometimes depends on
> both. I'm not sure how to cleanly separate that. Does anybody have
> any suggestions? Leave as is? Only a Manager/Engineer Strategy since
> all the cases depend on that distinction? Two Strategy hierarchies?
There is nothing to prevent an additional layer of subclassing:
[Engineer]
A
|
+-----------+-----------+
| | |
[Neutral] [Hourly] [Salaried]
But this will tend to become combinatorial in the number of subclasses.
It will not be attractive if the algorithms are duplicated. In
addition, multi-level subclassing should be avoided where possible
because it tends to be difficult to maintain.
Or combining two Strategy patterns:
[Employee]
| *
|
| 1 * 1
[Role] ---------------------- [PayStrategy]
A A
| |
+------+-----+ +--------+-------+
| | | | |
[Engineer] [Manager] [Hourly] [Salary] [Neutral]
This might be useful if there is other processing besides a single
algorithm to apply. That is, one does something that depends only on
Engineer vs. Manager and something else that depends upon Hourly vs.
Salary or some combination. For example, the [Role] might actually be a
GoF State pattern where several behaviors and attributes are relevant
while [PayStrategy] is a single algorithm for computing pay.
Essentially this is just a delegation to simplify the combinatorial tree
produced with multiple levels of subclassing.
Bottom line: the choice from among these various solutions really rests
on what the particular responsibilities are and whether or how one can
abstract invariants. For example, can the pay computation be
generalized so that it is the same for all salaried Employees subject to
some parameterization that can be determined according to the Employee's
role? Parametric polymorphism can be useful in situations like Payroll
where there are a gazillion benefits calculations that are all very
similar. One may be able to abstract the calculation itself to reduce
the number of subclasses or even eliminate them altogether. [You might
find the category on invariants in my blog of interest for this.]
*************
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 (under constr): http://pathfinderpeople.blogs.com/hslahman
(888)-OOA-PATH
- Next message: Robert C. Martin: "Re: Is it better to pass objects or discrete properties to methods?"
- Previous message: H. S. Lahman: "Re: Is it better to pass objects or discrete properties to methods?"
- In reply to: Clint Shank: "Strategy Pattern for Multiple Type Codes"
- Next in thread: topmind: "Re: Strategy Pattern for Multiple Type Codes"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]