Re: Business objects, subset of collection
- From: "H. S. Lahman" <hsl@xxxxxxxxxxxxxxxxx>
- Date: Wed, 16 Jan 2008 19:04:10 GMT
Responding to Jimbalo22...
We have a disconnect. Your model is typical of how one uses CRUD/USERInteresting. If OO is the wrong tool for this job, it would explain
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.
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).
I don't know that it is the wrong tool. But it /sounds/ like it is from the problem description so far.
One way the difference is manifested is in separation of concerns forWhen you say "subsystem" do you mean layers? The app is 3-tiered.
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.
Yes. A layer is just a single subsystem encapsulating a single subject matter. The RAD IDEs use 1D layers because the problems they are solving are inherently less complex than general customer problems. However, once one is outside the realm of CRUD/USER the layers become more complicated so that they are no longer semantically homogeneous. So the 1D layer structure becomes a 2D subsystem structure. [The category on Application Partitioning in my blog provides more description.]
Your [InvoiceCollection] and [PaymentCollection] entities organize dataYes they do organize data for display, but also for operation - the
for the display and would map into instances of [Window] and [Control].
(In fact, they would both likely be GridControl instances.)
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.).
That's where the RAD and OOA/D paradigms diverge. Display is a different subject matter with it own unique paradigms (e.g., Window/Control for a GUI and Page/Section for a web browser). Those paradigms are abstracted quite differently than the business environment (e.g., objects like Invoice and Payment). That is why I suggested that they might be called something different, like GridControl.
More important, the functional semantics is different. In a UI subsystem the functionality is expressed in terms of displaying data and user choices (clicking the Save button). IOW, the semantics is about display rendering through an OS window manager or a web browser. There is no business problem semantics; that only appears in the form of text strings for things like labels and titles that the UI simply renders.
So the GridControl objects would have no functional responsibilities for business operations. Those responsibilities would all be in the Invoice and Payment objects in the subsystem that abstracts the business rules and policies. Conversely, the objects like Invoice and Payment would have no functional responsibilities for display. All they would share is data that is passed back and for between them and they each uniquely interpret that data in terms of their subject matter.
But mixing paradigms is usually a very bad idea from an OOA/DSorry, but I am not sure if I follow the last part: If
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.
[InvoiceCollection] contains [Invoices], how can it be unware of
[Invoice]?
[GridControl] (nee [InvoiceCollection]) doesn't contain invoices. It contains a bunch of data values that somebody outside the UI thinks should be displayed. It knows nothing about the semantics of those data values. All it (or someone in the UI) knows is how to map values from those external messages into its internal attributes and, possibly, how to talk to the window manger or browser to get them rendered.
[GirdControl] may have relationships to other objects, but they will be other display abstractions like Window. IOW, the relationships describe how the display elements play together, not how the business elements play together.
Obviously there must be some sort of semantic mapping between the UI and Business views since ultimately they are both processing the same data (or at least subsets of the same data). So there must be some scheme for identifying messages and the data in their data packets. But each subsystem can map that data into attributes of its own objects in a unique fashion so that the only thing the subsystems share is a view of the message data packets.
Using a pure message-based data transfer interface does wonders for decoupling the implementations of subsystems. That is why you InvoiceCollection in the UI doesn't have to know that Invoice even exists in the Business subsystem. The downside is that one needs to encode the data packet on the sender side using its mapping and decode it on the receiver side using the receiver's mapping.
That is very general and provides great decoupling, but is gets a bit silly if the mapping is always 1:1, as it invariably is in CRUD/USER processing. So the first thing the RAD infrastructure providers did was to simplify the encode/decode into things like MS' DAOs (i.e., copy This attribute to That attribute). From there is didn't take long to borrow composition from functional programming and use inheritance to provide different views of the same data across the layer boundaries. Now the boundary was barely visible. So it wasn't long before RAD developers eliminated the boundary by mixing display and business abstractions directly and making the display objects "smart" in a business sense. That leads to GridControl -> InvoiceCollection with business operations.
Also, would you mind elaborating on how you would separate the
collections from the elements they contain into different subsystems?
Hopefully the above discussion sheds some light on that.
Do you know if C# (VS 2005) lends it self well to this concept?
I am a translationist, so I quit writing 3GL code back in '87. B-) The last language I learned was C++ and I can't speak to C#.
I point all this out because it reflects a major disconnect betweenThat makes sense. I actually had a similar thought earlier: to
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.
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?
I am not sure exactly what you mean and I don't know very much about the application, so it is difficult to speculate. But I don't see any obvious problems and one might well make use of the Strategy pattern via something like:
* extracts from R1 1
[DataSet] ----------------------- [Filter]
A
|
+--------------+-------...
| |
[ForPaydates] [For...]
where [Filter] produces a DataView (which might be a message to the UI) for various contexts.
[Apropos of the discussion at the end of this message on performance, I could see an optimization where [DataSet] was actually [Payment]. That is, I could see a different family of filters attached to Invoice and Vendor. Each would extract information from the set in hand and would apply unique selection criteria. But that would not address the fundamental optimization problem because each flavor of [Filter] would have to deal with the same set ordering of [DataSet]. The only way to optimize extraction of subset members is to order the set from which they are extracted in some fashion.]
By the way, what is your RAD IDE of choice?
For the last 20 years before retiring I was an R-T/E guy so the only RAD problems we saw were support tools for the software process (e.g., defect tracking). For that mundane stuff I usually used Access. I haven't looked at the market so I wouldn't know what to use for something like a web-based POS where one has both networking and server issues. (There wasn't much around back in the '70s when I was doing IT, but I'm sure we've come a long way since.)
Assuming one needs a total paid by a vendor for a particular date acrossAFAIK, there is no way to use an argument with databinding. Example:
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.
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.
You're not wrong. This is just another manifestation of the divergence of our design views. The answer is related to the encode/decode I talked about above. I don't have a binding problem when I separate and encapsulate the UI. When the user selects a particular display, I see the UI sending the Business subsystem a message like "The user wants to display the Payment Window. Here's the value the user supplied: <value>".
The Business subsystem's interface (think: Facade pattern) would interpret the message ID to mean a certain pile of data needs to be collected and that the value supplied was a payment date. So the Business interface would re-dispatch a message to some object that knows how to collect that data from the Business objects. That object would be a business entity that corresponds to your [VenderCollection]. It just wouldn't be a display object; it would be some business space entity like [Product] that would have a bunch of vendors associated with it.
That object would have a 1:* relationship to [Vendor] in my model. The method invoked would proceed to collect all the relevant data by "walking" the relationships in my model with a set of nested iterations that access all the relevant Payment objects. Each Payment would be checked for the date and, if matched, its amount would be accumulated into a <temporary> total value.
Once the method collected all the required data, it would package it up in a message data packet to send back to the UI, "Here's the pile of data you requested." The UI interface, in turn, would decode that data packet and assign its values to GridControl and whatnot. Once the data packet was decoded, then the UI interface would invoke the appropriate method on some display object to get those values rendered in the display.
So the binding is entirely in the message identity mapping in the respective subsystem interfaces.
By "filter-specific responsiblities" I mean methods for retrieving andI know, but I guess I was looking for a way to separate out all theI am not sure where the filters come in. But I am probably looking at
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.
the solution quite differently.
working with a subset of the collections (like the SetFilter method
and the PaymentTotalForMonth).
OK, if I understand you correctly, this sounds like the DataSet/Filter application of the Strategy pattern I mentioned above.
One way to think of it is that there are three pieces of the problem.Yes, this is basically what I doing now. One problem I am running
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 ...
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.
Ah, performance raises its ugly head. It may well be that you need direct relationships or multiple relationships, each optimizing the search through the collections. Each relationship becomes a separate collection class with a unique sort.
[Company]
| 1
|
| R1
|
| supplied by
| *
[Vendor] -----------+
| 1 |
| |
| R2 |
| |
| pays |
| * |
[Invoice] | R4
| 1 | [equal, R2, R3]
| | <ordered>
| R3 |
| |
| paid by |
| * |
[Payment] -----------+
* extracts payment dates for
The R4 relationship collection is constrained to have the same elements as the set reached by navigating R2 -> R3. But it is sorted by payment date so that the Payments on the given date are grouped and can be found quickly. This is basically a trade-off between overhead for adding/removing Payments in multiple collections vs. efficient query access of Payment subsets.
There are a lot of variations on the theme and which one is best will depend on the specific requirements. However, dealing with performance is orthogonal to the functional requirements. One resolves the functional requirements first and then tinkers with variants of that solution to deal with the nonfunctional requirements.
--
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
- 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
- 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: How should a container class "know" its contained objects?
- Next by Date: Re: Business objects, subset of collection
- Previous by thread: Re: Business objects, subset of collection
- Next by thread: Re: Business objects, subset of collection
- Index(es):