Re: Business objects, subset of collection
- From: "H. S. Lahman" <hsl@xxxxxxxxxxxxxxxxx>
- Date: Tue, 15 Jan 2008 00:17:49 GMT
Responding to Jimbalo22...
InvoiceCollection->Invoice->PaymentCollection->PaymentI 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 manageI know, but I guess I was looking for a way to separate out all the
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.
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 theSince I am not familiar with AAL I do not quite follow this - is this
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.
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@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.
- Follow-Ups:
- Re: Business objects, subset of collection
- From: frebe
- Re: Business objects, subset of collection
- From: jimbalo22
- Re: Business objects, subset of collection
- References:
- Business objects, subset of collection
- From: jimbalo22
- Re: Business objects, subset of collection
- From: H. S. Lahman
- Re: Business objects, subset of collection
- From: jimbalo22
- Business objects, subset of collection
- Prev by Date: Re: Derived class calling overriden base class methods
- Next by Date: Re: Asserts for Embedded Systems
- Previous by thread: Re: Business objects, subset of collection
- Next by thread: Re: Business objects, subset of collection
- Index(es):
Relevant Pages
|
Loading