Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

This is a simple question but a design consideration that I often run across in my day to day development work. Lets say that you have a class that represents some kinds of collection.

Public Class ModifiedCustomerOrders
     Public Property Orders as List(Of ModifiedOrders)
End Class

Within this class you do all kinds of important work, such as combining many different information sources and, eventually, build the Modified Customer Orders.

Now, you have different processes that consume this class, each of which needs a slightly different slice of the ModifiedCustomerOrders items. To enable this, you want to add filtering functionality. How do you go about this? Do you:

  1. Add Filtering calls to the ModifiedCustomerOrders class so that you can say:

    MyOrdersClass.RemoveCanceledOrders()

  2. Create a Static / Shared "tooling" class that allows you to call:

    OrdersFilters.RemoveCanceledOrders(MyOrders)

  3. Create an extension method to accomplish the same feat as #2 but with less typing:

    MyOrders.RemoveCanceledOrders()

  4. Create a "Service" method that handles the getting of Orders as appropriate to the calling function, while using one of the previous approaches "under the hood".

    OrdersService.GetOrdersForProcessA()

  5. Others?

I tend to prefer the tooling / extension method approaches as they make testing a little bit simpler. Although I dependency inject all my sourcing data into the ModifiedCustomerOrders, having it as part of the class makes it a little bit more complicated to test. Typically, I choose to use extension methods where I am doing parameterless transformations / filters. As they get more complex, I will move it into a static class instead.

Thoughts on this approach? How would you approach it?

share|improve this question
2  
As a design question, I think this is fine to stay here instead of being migrated to Stack Overflow. –  Anna Lear Jul 7 '11 at 15:04
    
Extension methods are just syntactic sugar for static methods. I don't think there's any benefit from moving them into a separate static class. –  Anna Lear Jul 7 '11 at 15:08

2 Answers 2

I think the issue for me with this question is how you're encapsulating the data to begin with. That's where I would start to refactor to try and create a design that makes a bit more sense (and which then might make the answer to the solution more obvious, or eliminate the question outright).

Example: I would never have a class or collection called "ModifiedCustomerOrders". I would deal with things like Customer, Order, and ShoppingCart. I would make the assumption, from the beginning, that an Order can be in one of several "states" at any given time. This is the nature of data in any domain - it can change state. We don't need special collections or classes to represent those states, we can just ask the object itself.

An Order, for instance, can be in a state of "Pending", or "Complete", or "Cancelled", or "Shipped", etc. Do you want classes/collections for all of those different states? No, not really. What is preferable is to provide a way to ask an Order what state it is in. That's a business concern for an Order.

If an order is "Modified" - that again is a question you should simply be able to ask an Order.

if(Order.IsModified)...

So now we get to the main question: whose responsibility is it to RemoveCancelledOrders()?

This is where context matters. Why are we removing cancelled orders? And from what? Why do we have Order(s) (plural) to begin with? Why are we aggregating a bunch of Orders in potentially different "states" together into a collection? What is the business need for that? And if we're aggregating all of these Order(s) together, then what is holding them and what are we removing them from? A shopping cart? Is that how we're aggregating them? That doesn't make much sense, because a shopping cart should only be dealing with one order at a time.

I know this doesn't directly answer the question (which approach to choose for dealing with the code that actually does that), but this is an important aspect of designing a system. The proper solution depends on the context. Designing the system a specific way might eliminate the need to ask this question at all.

I know for me, when I am designing a domain, I prefer for root aggregates to manipulate the contents of their child collections. The job of adding or removing an OrderItem to or from an Order, for example, is really the job of the Order, so that business logic can run when that operation happens.

Removing a child object from a collection is something I would normally want a parent object to do. Or a UnitOfWork...

share|improve this answer
    
I think you have a good point w/ context and I appreciate the fact that it may be a deeper design flaw that is forcing me to this decision. On the other hand, I would stress that my example is just that. I was just trying to emphasize the situation rather than the actual context. I probably should have provided more of a real-world context to support the question. –  Nathan Jul 8 '11 at 12:56

I would let the user of that Data object do the work.

The data object should remain simple and generic enough to make the whole things more manageable.

If some class needs certain slice of the data object, it is the job of that class to customize and manipulate that data. If you put that responsibility in the data object class, you implicitly introduce a coupling in the direction of the data object to its user classes when you don't really need to. Doesn't seem like a good idea to me.

Having said that, there are cases when you need to provide customized operation on the data object class. These are the cases where you can put in extra tricks and optimization for that particular operation. Users of the data object won't have the chance to do this since they don't have access to internal structure of the data object.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.