Re: Factory or Inheritance for Initialisation?



Yesterday we did a lot of rework as a group ....

H. S. Lahman wrote:
Responding to Kognition...

I would like to restate the problems ignoring the original inherited
tree that I spoke in the first mail - it is confusing matters.

1. The system has 3 logical types of customers (billing, non-billing,
special).
2. A customer must be selected to be processed - the reasons why are
not relevant; a selection must be made accord to rules.
3. First, attempt n times to select a billing customer.
4. If after n times, no billing customer could be selected, select any
other type of customer.

FooBar is the object the client speaks to request SelectNextItem
(SelectNextCustomer). The client in this step does not care about the
rules about choosing one customer over another type of customer- all it
cares about is that a customer is returned.

Inside FooBar (call this TheCustomerSelector), these requirements need
to be implemented. It is useful that the rules about who is selected
first, and the number of retries/maxCount is not cemented. It is
feasible the business could say that non-billing customers get priority
with a number of retries, followed by billing customers with a number
of retries, followed by special, which are unlimited. This *must* be
considered for the design.

There are 2 parts to the selections:

1. Retries - in all cases retries are necessary, but under the billing
situation retries are limited and the other case is not. Retries are
necessary just because of the nature of the system. A valid customer
could be selected, but for other reasons he is not suitable, so another
customer needs to be selected. How many times this continues is a
feature of the system.

2. The algorithm used to select a customer

Different ways selections occur
When selecting an billing customer, the selection rules are
specifically directed to slant towards customers that are in a specific
state - customers with the Customer longer have a greater chance of
being selected, for example. This selection type goes into a strategy,
and deals with billing customers. If billing custom selection fails,
then we just need to select one of the remaining customer types -
theres no real discrimination, and the selection is almost random- this
goes in a second strategy object.

So the Client doesn't care what type of Customer is selected, right?


Right.

And whether a Customer is Billing, Non-Billing, or Special is an
intrinsic characteristic of the Customer status that somebody else
should be setting, right?


Its part of the customer values, loaded from DB.

And you need to prioritize by Customer type (e.g., selecting from among
Billing Customers first), right?


Right.

Finally, once the CustomerSelector finds a Customer the Client needs to
collaborate with the Customer directly, right? That is,
CustomerSelector is instantiating a relationship between Client and a
particular Customer.


It returns the customer - something omitted in post before because we
hadnt got to that ourselves, but the customer is to be returned.

I am still confused about what a Strategy actually does and how that
relates to retries. Your code example indicates that you would have to
have > 100 Strategies since retries are tested on invoking new
Strategies. Then UNLIMITED would just mean some arbitrary number of
strategies beyond 100.

That is suspicious to me because it screams for parametric polymorphism.
That is, the selection decision must be based on the values of some
set of Customer attributes. There are only two ways to get such a large
number of strategies in that situation. One is combinatorial in the
properties evaluated (Strategy A evaluates properties X and Y; Strategy
B evaluates properties X and Z; Strategy C evaluates properties Y and Z;
etc.). The other is that one has different thresholds for the property
values on each pass (e.g., one loosens the selection criteria on each
try). (Or some combination of the two.)

Heres what we have come up with so far yesterday. There are 4 objects
involved in this process. Company was Customer here.

1. The client - requests a Company
2. The CompanySelectionFacade - hides the selection process and has the
outer loop
3. The CompanySelector - the class that uses Composition/Delegation to
perform selection, and contains the retry loop
4. The SelectionStrategy

In the client code:
------------------

Company Company = GetSelectionFacade().Select();
// Do something with Company instance

In CompanySelectionFacade:
--------------------------

public Company Select() {
GetFirstSelector();

do {
Company Company = GetCurrentSelector().Select();
if(Company != null) return Company;
} while(GetNextSelector() != null);

throw new CompanyFacadeException("No Company could be selected.");
}

In the CompanySelector:
-----------------------

public Company Select() {

GetSelectionStrategy().Begin();

do {
Company Company = GetSelectionStrategy().GetNext();
if(Company != null) return Company;
} while(GetSelectionStrategy().CanRetry);

return null;
}

CompanySelector does some initialisation and has some state not shown,
but this is what actually happens above. The facade gets its first
CompanySelector and asks it to select a Company. CompanySelector deals
with the loop and state initialisation, and the actual selection
algorithm is the selection strategy. So far we have 3
SelectionStrategies, so they are not combinatorial. CompanySelector
and SelectionStrategy make up one object. Before CompanySelector had
the child-classes of BillingSelector and NonBillingSelector, but now
the inheritance has been swapped for composition. Its not required to
know what the SelectionStrategy does - all that is needed to know is
that it performs some algorithm to choose a Company. The facade is
loaded with its CompanySelectors using a factory, and the factory knows
to put the CompanySelector/SelectionStrategy objects relating to
Billing Companys in the first position in the array of CompanySelector
instances that are given to the facade:

CompanyFacadeFactory.Create() {
Array selectors = new Array[2];
selectors[0] = new CompanySelector(new BillingSelection(100));
selectors[1] = new CompanySelector(new
CurrentYearBusinesSelection(1000));
selectors[2] = new CompanySelector(new DefaultSelection()); // no
retry limit

return new CompanyFacade(selectors);
}

....

I dont want to ignore your post from before, but does this explain more
now how we have done this?

The reason I like the single, generic Strategy is that the
[SelectionSpec] objects can be defined in external configuration data,
along with the R3 relationship order. Since they are dumb data holders,
they can be initialized (with the R3 collection) at startup. That
allows you modify the selection criteria fairly arbitrarily without
touching the code itself; all you have to change is the configuration
data file.


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

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

.