From: jimbalo22 on
Hi,

I have a collection of business objects that the user works on via
data binding. The user needs to be able to work with a subset of the
collection and I am wondering what the best way to accomplish this is.

Example:

InvoiceCollection->Invoice->PaymentCollection->Payment

The user wants to only work with invoices or payments for a certain
date.

Two ways I can think of are 1) work with a subset, or 2) Work with the
full set in a filtered fashion. I am leaning toward 2), but it gets
quite messy with a bunch of filter related methods in the public
interfaces.

Any help on this would be appreciated.

Thanks,
Jim
From: H. S. Lahman on
Responding to Jimbalo22...

> Hi,
>
> I have a collection of business objects that the user works on via
> data binding. The user needs to be able to work with a subset of the
> collection and I am wondering what the best way to accomplish this is.
>
> Example:
>
> InvoiceCollection->Invoice->PaymentCollection->Payment

I assume this notation means something like:

[Customer]
| 1
|
| R1
|
| is billed with
| *
[Invoice]
+ date
| 1
|
| R2
|
| are paid by
| *
[Payments]
+ date

where InvoiceCollection and PaymnentCollection implement the R1 and R2
1:* relationships at OOP time.

>
> The user wants to only work with invoices or payments for a certain
> date.
>
> Two ways I can think of are 1) work with a subset, or 2) Work with the
> full set in a filtered fashion. I am leaning toward 2), but it gets
> quite messy with a bunch of filter related methods in the public
> interfaces.

The answer definitely depends -- on what problems are being solved and
at what level of abstraction.

The most general approach is (2) and most of the abstract action
languages (AALs) used in OOA/D will have a WHERE clause available for
any relationship navigation to filter the set of objects accessed. Thus
in an AAL method one might have something like

invoiceSet = this -> R1 WHERE (date=20080113)
FOREACH invoice IN invoiceSet
// process invoice objects

when the user wants invoices, or

paymentSet = this -> R1 -> R2 WHERE (date=20080113)
FOREACH payment IN paymentSet
// process payment objects

when the user wants payments. [Note that the WHERE clause filters the
date attribute in the target set. When a multistage path is navigated it
is assumed that the membership of the intermediate collections already
limits the set adequately. If not, one needs to do the second fragment
in two stages.]

This is fine so long as the filter is simple, such as a date. Each
collection is optimized for efficient access via a date. As you point
out, it gets somewhat messy if there are multiple filters (e.g., one
also may want payments where the amount was less than some percentage of
the outstanding balance). Then the collection class needs an interface
for each distinct selection criteria. Each accessor may need a
specialized implementation as well.

However, that is pretty much why collection classes exist; they manage
collections. More to the point, that is /all/ they do; they encapsulate
the collection management in one place. IOW, the collection class
isolates and encapsulates complexity of a highly focused nature. In
general that is a Good Thing. So there is nothing inherently wrong with
the second approach.

Note that in the AAL, one just needs to substitute "amount<100" in the
WHERE clause to deal with another selection criteria in the second
fragment above. The details of how one mucks about to extract that set
is left to the low level implementation so during OOA/D the application
developer doesn't need to think about it.

However, at OOP time one addresses other issues like performance and
maintainability. Suppose Invoices and Payments are very rarely added
compared to how often the various selection criteria are accessed. In
that case it may well be more efficient to have multiple 1:*
relationships in parallel, each with its own optimized collection
mechanics. Thus one has R2A optimized to access by <arbitrary> date and
R2B optimized to access by some amount criteria. For example, one
collection is sorted by date while the other is sorted by amount. The
overhead of adding the same entry to each collection is <hopefully>
small compared to accessing the same elements many times.

Maintainability may also drive the use of (1) simply because the
internal mechanisms for optimizing around several disparate criteria are
just making the collection object too complex to modify. This is a
variation on your concern about the interface being "messy". However,
multiple interface methods are usually not the core problem; the need to
break up the collection is usually driven by the complexity of the
implementation.

Bottom line: in OOA/D one wants to think generically about (2) without
worrying about the details and the OOA/D notations are designed around
that. But at OOP time one is addressing other issues and those may drive
using (1).


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

H. S. Lahman
hsl(a)pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info(a)pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
From: jimbalo22 on
Lahman,

Thanks for the reply.

> > Example:
>
> > InvoiceCollection->Invoice->PaymentCollection->Payment
>
> I assume this notation means something like:
>
> [Customer]
> | 1
> |
> | R1
> |
> | is billed with
> | *
> [Invoice]
> + date
> | 1
> |
> | R2
> |
> | are paid by
> | *
> [Payments]
> + date

Almost. The application is for scheduling of payments of invoices and
what I posted was a simplfied version of this. Here is a more
accurate diagram:
[VendorCollection]
| 1
|
|
| *
[Vendor]
| 1
|
|
| 1
[InvoiceCollection]
| 1
|
|
| *
[Invoice]
| 1
|
|
| 1
[PaymentCollection]
| 1
|
|
| *
[Payment]

> The answer definitely depends -- on what problems are being solved and
> at what level of abstraction.

Here is a brief description of the app. It is a WinForm (C#) app
with:
* a Vendor grid bound to [VendorCollection], showing each [Vendor]
with amount due, payment total, etc.
* an Invoice grid bound to [InvoiceCollection], showing each [Invoice]
for the selected vendor
* a Payment grid bound to [PaymentCollection], showing each [Payment]
scheduled for the selected invoice.

The user needs to be able to view and work with a variety of filtered
subsets of the vendors / invoices:
1) The Vendor grid shows daily totals, monthly totals and grand totals
for each vendor.
2) The user can select one or more vendors in the vendor grid and
perform operations on all payments assigned to the invoices for those
vendors for the day (for example: remove payments, change payment
date, add payments).
3) The user can select one or more vendors in the vendor grid and
perform operations on all payments assigned to the invoices for those
vendors for the month.
4) The user can select one or more vendors in the vendor grid and
perform operations on all payments assigned to the invoices for those
vendors (no time-limiter)
5) The user can select one or more invoices in the invoice grid and
perform operations on all payments assigned to those invoices.

I have a Setfilter(DateTime filterDate) methods defined in the
classes, so that when you call SetFilter on for example
[VendorCollection], it in turns call SetFilter on each [Vendor] and so
forth.

> The most general approach is (2) and most of the abstract action
> languages (AALs) used in OOA/D will have a WHERE clause available for
> any relationship navigation to filter the set of objects accessed. Thus
> in an AAL method one might have something like
>
> invoiceSet = this -> R1 WHERE (date=20080113)
> FOREACH invoice IN invoiceSet
> // process invoice objects
>
> when the user wants invoices, or
>
> paymentSet = this -> R1 -> R2 WHERE (date=20080113)
> FOREACH payment IN paymentSet
> // process payment objects

I am not familiar with Abstract Action Languages, but here is an
example of a method in [InvoiceCollection]:

public double PaymentTotalForDay
{
get
{
double amount = 0;
foreach (Vendor vendor in this)
amount += vendor.PaymentTotalForDay;

return amount;
}
}

This will return the total payment amount for the date specified in an
earlier call to Setfilter(filterDate). But since the VendorGrid also
needs to show PaymentTotalForMonth (regardless of day filtered on) and
PaymentTotal (no time-limiter), I wind up with two similar methods for
this. And then there are several other filter-specific methods, so it
does get a bit messy.


> when the user wants payments. [Note that the WHERE clause filters the
> date attribute in the target set. When a multistage path is navigated it
> is assumed that the membership of the intermediate collections already
> limits the set adequately. If not, one needs to do the second fragment
> in two stages.]
I belive I am doing something equivalent using the SetFilter method
above.


> However, that is pretty much why collection classes exist; they manage
> collections. More to the point, that is /all/ they do; they encapsulate
> the collection management in one place. IOW, the collection class
> isolates and encapsulates complexity of a highly focused nature. In
> general that is a Good Thing. So there is nothing inherently wrong with
> the second approach.
I know, but I guess I was looking for a way to separate out all the
filter-specific responsibilities in such a way as to simplify and make
the design cleaner, but it seems that doing that just adds more
complexity.

> Note that in the AAL, one just needs to substitute "amount<100" in the
> WHERE clause to deal with another selection criteria in the second
> fragment above. The details of how one mucks about to extract that set
> is left to the low level implementation so during OOA/D the application
> developer doesn't need to think about it.
Since I am not familiar with AAL I do not quite follow this - is this
something I could use in my C# app as described above?

> However, at OOP time one addresses other issues like performance and
> maintainability. Suppose Invoices and Payments are very rarely added
Actually, Invoices are not added / removed, just paid. Payments are
what changes (=are modified, added & removed).

Thanks,
Jim

Ps. It is probably not relevant, but in the app I am using Rockford
Lhotka's CSLA framework.
From: H. S. Lahman on
Responding to Jimbalo22...

>>> InvoiceCollection->Invoice->PaymentCollection->Payment
>> I assume this notation means something like:
>>
>> [Customer]
>> | 1
>> |
>> | R1
>> |
>> | is billed with
>> | *
>> [Invoice]
>> + date
>> | 1
>> |
>> | R2
>> |
>> | are paid by
>> | *
>> [Payments]
>> + date
>
> Almost. The application is for scheduling of payments of invoices and
> what I posted was a simplfied version of this. Here is a more
> accurate diagram:
> [VendorCollection]
> | 1
> |
> |
> | *
> [Vendor]
> | 1
> |
> |
> | 1
> [InvoiceCollection]
> | 1
> |
> |
> | *
> [Invoice]
> | 1
> |
> |
> | 1
> [PaymentCollection]
> | 1
> |
> |
> | *
> [Payment]
>
>> The answer definitely depends -- on what problems are being solved and
>> at what level of abstraction.
>
> Here is a brief description of the app. It is a WinForm (C#) app
> with:
> * a Vendor grid bound to [VendorCollection], showing each [Vendor]
> with amount due, payment total, etc.
> * an Invoice grid bound to [InvoiceCollection], showing each [Invoice]
> for the selected vendor
> * a Payment grid bound to [PaymentCollection], showing each [Payment]
> scheduled for the selected invoice.

We have a disconnect. Your model is typical of how one uses CRUD/USER
layered model infrastructures. That sort of processing is not a good
target for OO techniques. The RAD IDEs have already abstracted the
invariants of the processing in terms of Form/Query/Table and have
provided a lot of reusable infrastructure that one would have to develop
from scratch using OO techniques. In addition, such infrastructures are,
at best, object-based rather than object-oriented.

One way the difference is manifested is in separation of concerns for
different problem spaces. In an OOA/D context the model I provided would
be relevant in a subsystem that modeled the customer's business problem.
The rendering of the display would be handled in a different subsystem
that modeled the particular display paradigm. So for a GUI one might
have a model like:

[Display]
| 1
|
| R1
|
| contains
| *
[Window]
| 1
|
| R2
|
| Control
| *
[Control]

where [Control] is subclassed into things like [ListBox] and [Grid]. In
the GUI subsystem the only business semantics exists as labels and
titles for particular instances that are provided by the business
subsystem or, more commonly, through a configuration file.

Your [InvoiceCollection] and [PaymentCollection] entities organize data
for the display and would map into instances of [Window] and [Control].
(In fact, they would both likely be GridControl instances.) As it
happens, in CRUD/USER processing the use of the Form paradigm for I/O
conveniently allows display artifacts to map 1:1 to RDB tables and
Queries. That allows one to raise the level of abstraction and hide all
the Window/Control manipulations under the hood in the infrastructure.
So one doesn't need to think explicitly about windows and controls and
one can focus on manipulating Forms like InvoiceCollection and
PaymentCollection.

The developer still needs to provide a mapping, though, between those
Forms and RDB tables and queries. Typically that is done in the business
layer where one mixes classes like [Invoice] and [InvoiceCollection] and
uses relationships to enforce the inherent referential integrity rules.
That works fine for CRUD/USER processing because all the mapping is
essentially 1:1. No harm; no foul.

But mixing paradigms is usually a very bad idea from an OOA/D
perspective once one is outside the realm of CRUD/USER processing.
That's because the data structures needed to optimize the customer's
problem solution are usually not the same as those needed to optimize
data storage or display and the 1:1 mapping breaks down. So in OOA/D one
would be very careful to put [Invoice] and [Payment] in one subsystem
and [InvoiceCollection] and [PaymentCollection] in another subsystem.
That manages the mapping complexity by separating the concerns of
different problem spaces (display vs. business) so that one can focus on
one paradigm at a time. One would also encapsulate the subsystems
strongly so that objects in one subsystem would have no idea that the
objects in the other subsystem even exist. That, in turn, simplifies the
mapping problem to data identity so that each subsystem can map message
data packets into its own special view.

I point all this out because it reflects a major disconnect between
OOA/D and the RAD infrastructures. So far your application sounds like
classic CRUD/USER processing. In that case using the RAD paradigm and
infrastructures will probably save you a ton of keystrokes. But that
will necessarily result in different models than I would use as an OOA/D
guy when solving non-CRUD/USER problems. (When I need to solve a
CRUD/USER problem I forget about OO and use a RAD IDE.) Thus I can't
really express the points I was trying to make in terms of your model.

>> The most general approach is (2) and most of the abstract action
>> languages (AALs) used in OOA/D will have a WHERE clause available for
>> any relationship navigation to filter the set of objects accessed. Thus
>> in an AAL method one might have something like
>>
>> invoiceSet = this -> R1 WHERE (date=20080113)
>> FOREACH invoice IN invoiceSet
>> // process invoice objects
>>
>> when the user wants invoices, or
>>
>> paymentSet = this -> R1 -> R2 WHERE (date=20080113)
>> FOREACH payment IN paymentSet
>> // process payment objects
>
> I am not familiar with Abstract Action Languages, but here is an
> example of a method in [InvoiceCollection]:

The only point I was trying to make is that in OOA/D the AAL syntax
essentially restricts one to your type (2) approach because that is the
most general.

>
> public double PaymentTotalForDay
> {
> get
> {
> double amount = 0;
> foreach (Vendor vendor in this)
> amount += vendor.PaymentTotalForDay;
>
> return amount;
> }
> }
>
> This will return the total payment amount for the date specified in an
> earlier call to Setfilter(filterDate). But since the VendorGrid also
> needs to show PaymentTotalForMonth (regardless of day filtered on) and
> PaymentTotal (no time-limiter), I wind up with two similar methods for
> this. And then there are several other filter-specific methods, so it
> does get a bit messy.

I am confused. In your model each InvoiceCollection has only one Vendor
related to it. Where does the foreach collection come from?

Assuming one needs a total paid by a vendor for a particular date across
all vendor invoices, I don't see a need for InvoiceCollection,
PaymentCollection, and SetFilter. If we had
Vendor::getTotalPaymentsByDate(date), all that method needs to do is
"walk" the R1 and R2 relationships in my model. For each Payment
accessed one then just checks the date and, if a match, accumulates the
payment amount. If one needs a total for all vendors, we just need to
iterate over the vendors and sum the results of calling
Vendor::getTotalPaymentsByDate.

>> However, that is pretty much why collection classes exist; they manage
>> collections. More to the point, that is /all/ they do; they encapsulate
>> the collection management in one place. IOW, the collection class
>> isolates and encapsulates complexity of a highly focused nature. In
>> general that is a Good Thing. So there is nothing inherently wrong with
>> the second approach.
> I know, but I guess I was looking for a way to separate out all the
> filter-specific responsibilities in such a way as to simplify and make
> the design cleaner, but it seems that doing that just adds more
> complexity.

I am not sure where the filters come in. But I am probably looking at
the solution quite differently.

>
>> Note that in the AAL, one just needs to substitute "amount<100" in the
>> WHERE clause to deal with another selection criteria in the second
>> fragment above. The details of how one mucks about to extract that set
>> is left to the low level implementation so during OOA/D the application
>> developer doesn't need to think about it.
> Since I am not familiar with AAL I do not quite follow this - is this
> something I could use in my C# app as described above?

I think one way to express my point is that the filter is the WHERE
clause and one specifies the attribute(s) to be checked in it. Identify
a different attribute withing the same syntax and one has a different
filter. But, as I noted, that filter is applied to the set of target
objects (e.g., Payments) regardless of how long the relationship path is
to obtain them.

One way to think of it is that there are three pieces of the problem.
One is to get a set of objects that are of interest. This is just a
matter of relationship navigation, which is orthogonal to class
semantics. That is, wherever one needs to construct, say, a Payment
total, one needs to find a relationship path to the relevant Payment
objects and navigate it. That is just a matter of "walking" the
collection classes that implement the relationship path segments.

The second piece of the problem if to extract a subset of the navigable
objects based on problem constraints (if necessary). This is the
"filtering". One tests attributes of the collection resulting from
relationship navigation and prunes those that are not relevant.

The third piece is to actually do something with the objects, like
accumulating a total, once one has the relevant subset.

My point here is that the "filtering" is just part of the relationship
navigation. That is, the attribute testing is done as one accesses each
object. That requires no special objects. (If the testing is complex, it
may be delegated to another object, but it will always be invoked
synchronously by whoever is doing the relationship navigation.)


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

H. S. Lahman
hsl(a)pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info(a)pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
From: jimbalo22 on
Lahman,

First of all, thanks for taking the time to explain all of this.

> We have a disconnect. Your model is typical of how one uses CRUD/USER
> layered model infrastructures. That sort of processing is not a good
> target for OO techniques. The RAD IDEs have already abstracted the
> invariants of the processing in terms of Form/Query/Table and have
> provided a lot of reusable infrastructure that one would have to develop
> from scratch using OO techniques. In addition, such infrastructures are,
> at best, object-based rather than object-oriented.
Interesting. If OO is the wrong tool for this job, it would explain
why I am having a hard time coming up with a smooth design for this
(that and the fact that I still have much to learn in the OOA/D
arena).


> One way the difference is manifested is in separation of concerns for
> different problem spaces. In an OOA/D context the model I provided would
> be relevant in a subsystem that modeled the customer's business problem.
> The rendering of the display would be handled in a different subsystem
> that modeled the particular display paradigm. So for a GUI one might
> have a model like:
>
> [Display]
> | 1
> |
> | R1
> |
> | contains
> | *
> [Window]
> | 1
> |
> | R2
> |
> | Control
> | *
> [Control]
>
> where [Control] is subclassed into things like [ListBox] and [Grid]. In
> the GUI subsystem the only business semantics exists as labels and
> titles for particular instances that are provided by the business
> subsystem or, more commonly, through a configuration file.
When you say "subsystem" do you mean layers? The app is 3-tiered.

> Your [InvoiceCollection] and [PaymentCollection] entities organize data
> for the display and would map into instances of [Window] and [Control].
> (In fact, they would both likely be GridControl instances.)
Yes they do organize data for display, but also for operation - the
user can select one or many vendors or invoices or payments and
perform some action on these (like move payment dates or remove
payments, etc.).


> But mixing paradigms is usually a very bad idea from an OOA/D
> perspective once one is outside the realm of CRUD/USER processing.
> That's because the data structures needed to optimize the customer's
> problem solution are usually not the same as those needed to optimize
> data storage or display and the 1:1 mapping breaks down. So in OOA/D one
> would be very careful to put [Invoice] and [Payment] in one subsystem
> and [InvoiceCollection] and [PaymentCollection] in another subsystem.
> That manages the mapping complexity by separating the concerns of
> different problem spaces (display vs. business) so that one can focus on
> one paradigm at a time. One would also encapsulate the subsystems
> strongly so that objects in one subsystem would have no idea that the
> objects in the other subsystem even exist.
Sorry, but I am not sure if I follow the last part: If
[InvoiceCollection] contains [Invoices], how can it be unware of
[Invoice]?

Also, would you mind elaborating on how you would separate the
collections from the elements they contain into different subsystems?
Do you know if C# (VS 2005) lends it self well to this concept?


> I point all this out because it reflects a major disconnect between
> OOA/D and the RAD infrastructures. So far your application sounds like
> classic CRUD/USER processing. In that case using the RAD paradigm and
> infrastructures will probably save you a ton of keystrokes. But that
> will necessarily result in different models than I would use as an OOA/D
> guy when solving non-CRUD/USER problems. (When I need to solve a
> CRUD/USER problem I forget about OO and use a RAD IDE.) Thus I can't
> really express the points I was trying to make in terms of your model.
That makes sense. I actually had a similar thought earlier: to
simply use a Dataset with DataViews for filtering. I have not used a
DataSet in a long time, but I guess I would have to pass the dataset /
dataview into business layer methods for the execution of operations
on the data (such as the InvoiceCollection.ChangeAllPayDates(DateTime
newDate) method I currently have). What are your thoughts on this?

By the way, what is your RAD IDE of choice?

> > public double PaymentTotalForDay
> > {
> > get
> > {
> > double amount = 0;
> > foreach (Vendor vendor in this)
> > amount += vendor.PaymentTotalForDay;
>
> > return amount;
> > }
> > }
>
> > This will return the total payment amount for the date specified in an
> > earlier call to Setfilter(filterDate). But since the VendorGrid also
> > needs to show PaymentTotalForMonth (regardless of day filtered on) and
> > PaymentTotal (no time-limiter), I wind up with two similar methods for
> > this. And then there are several other filter-specific methods, so it
> > does get a bit messy.
>
> I am confused. In your model each InvoiceCollection has only one Vendor
> related to it. Where does the foreach collection come from?
The above snipped is from [VendorCollection] and it iterates though
each [Vendor].

> Assuming one needs a total paid by a vendor for a particular date across
> all vendor invoices, I don't see a need for InvoiceCollection,
> PaymentCollection, and SetFilter. If we had
> Vendor::getTotalPaymentsByDate(date), all that method needs to do is
> "walk" the R1 and R2 relationships in my model. For each Payment
> accessed one then just checks the date and, if a match, accumulates the
> payment amount. If one needs a total for all vendors, we just need to
> iterate over the vendors and sum the results of calling
> Vendor::getTotalPaymentsByDate.
AFAIK, there is no way to use an argument with databinding. Example:
one of the gridcolumns is bound to
VendorCollection.PaymentTotalForDay; I don't think you can bind it to
VendorCollection.getTotalPaymentsByDate(date). But I might be wrong -
if you (or anyone) knows how to do this, please let me know.


> > I know, but I guess I was looking for a way to separate out all the
> > filter-specific responsibilities in such a way as to simplify and make
> > the design cleaner, but it seems that doing that just adds more
> > complexity.
>
> I am not sure where the filters come in. But I am probably looking at
> the solution quite differently.
By "filter-specific responsiblities" I mean methods for retrieving and
working with a subset of the collections (like the SetFilter method
and the PaymentTotalForMonth).

> One way to think of it is that there are three pieces of the problem.
> One is to get a set of objects that are of interest. This is just a
> matter of relationship navigation, which is orthogonal to class
> semantics. That is, wherever one needs to construct, say, a Payment
> total, one needs to find a relationship path to the relevant Payment
> objects and navigate it. That is just a matter of "walking" the
> collection classes that implement the relationship path segments.
>
> The second piece of the problem if to extract a subset of the navigable
> objects based on problem constraints (if necessary). This is the
> "filtering". One tests attributes of the collection resulting from
> relationship navigation and prunes those that are not relevant.
>
> The third piece is to actually do something with the objects, like
> accumulating a total, once one has the relevant subset.
>
> My point here is that the "filtering" is just part of the relationship
> navigation. That is, the attribute testing is done as one accesses each
> object. That requires no special objects. (If the testing is complex, it
> may be delegated to another object, but it will always be invoked
> synchronously by whoever is doing the ...
Yes, this is basically what I doing now. One problem I am running
into is that the performance is not that great due to the many
traversals of the various collections. I will profile and see what
can be done (caching is tricky in this scenario). If I cannot resolve
the performance issue, I will probably look into using a DataSet &
DataView instead since they might be better tools for this job.

Thanks,
Jim