Architecture: Best practices for manipulating models without polluting the POJOs? And without repeating boiler-plate code everywhere [closed]
Asked Answered
A

5

11

This is a problem we encounter often. There must be some best practices to address this...

Simplified Question

Where's the best place to put common code that manipulates POJOs?

such that:

  • POJOs only have properties and getters/setters
  • the same model manipulation code isn't repeated "everywhere"
  • it's very clear which classes are responsible for manipulating the model

Background

We have a schema that defines our domain. From that, we generate a "pure" model that consists of simple objects (POJOs) that come from JAXB.

In working with this model, several developers on the team have created boiler-plate code to access and manipulate the model. It is "sprinkled" in many places. Some have created wrapper objects that subclass instances of the model and add functionality. Others have created external utility classes. I'm looking to unify this code so that it's no longer "sprinkled everywhere." Ideally, the logic can be contained in a certain class of objects who are clearly responsible for generic manipulations of the model.

Example

Let's use a grocery store as a generic example. Model objects consist of things like:
Products, Aisle, Shelf, Employee, WorkSchedule, Vendor

Common model manipulations consist of things like:
findManagerWorkingOnDay(day, schedule), findAisleForProduct(apples), countItemsOnShelf(topShelf), product.isModified(), removeProductFromVendor(apples, vendor)

We don't want to "pollute" our Vendor POJO with a function like removeProductFromVendor. Likewise, we don't necessarily want to extend every model object, just to add the isModified property just so our GUI can know to "enable/disable" the save button.

Or do we?

Summary

Once a model object is in memory, who should be responsible for manipulating it--for instance, to iterate over a list of "employees on duty today" and find one who is a "manager?"

In these cases, database calls are overkill because we already have everything we need in memory (for ex: we've already queried the DataStore and we need to work with the result objects throughout the application). Ideally, this code would be available to any object who had, for example, a list of employees.


In terms of best practice, where is the ideal place to put the static method:
public static Employee findManager(List<Employee> employeesOnDuty);

which would iterate over the list of employees (POJOs) and return the first one where employee.title.toLowerCase().contains("manager")

If a team were to work with this sample object model, several people would write functions like this. What are some of the best practices to capture this responsibility such that POJOs remain "pure" and the same boiler-plate code isn't "sprinkled everywhere."

Analog answered 23/1, 2012 at 15:59 Comment(2)
The best solution, IMO, is to "pollute" your objects so that they actually provide behavior, and encapsulation. That's the idea behind OOP.Teratoid
Objects can provide behavior, yes. However, in this and many other cases, the "behavior" provided by Pojos (or value objects) is to store values. That's it. Higher order objects can work with the value objects but value objects themselves should not contain business logic. Our schema (XSD) used to describe the object model does not and should not include complex logic.Analog
A
6

Given that your POJO objects are from a vendor, I understand your reluctance to add functionality to them, and you obviously don't want code randomly sprinkled around your project.

Sounds like a job for either a set of Decorator subclasses, or as Ken suggests, facades.

I like decorators here because you'd then just refer to your vendor pojo by the name of the decorator class, which would then have the added behavior (isModified, searchByName, etc), all in one place.

Acridine answered 23/1, 2012 at 16:53 Comment(4)
We have used the Decorator pattern in some places but what I'm finding is a lot of these classes contain excessive private or static methods, which is a clear indication that "there is another class in there struggling to get out" so this responsibility should belong to another class.Analog
It's true that this approach can be taken too far. Preventing that is a job for whoever in your team looks after concerns like that (you, it sounds like). But there's really no feasible way to add, (your example) isModified without adding some state somewhere, and it has to be instance-specific.Acridine
I've just touched on this issue, again, for the 5,000th time. In this case, I chose to go with the Decorator pattern, creating things like ProductDecorator to add operations/behavior to the underlying POJO. This time around, I'm not making any static methods (that was the part that bothered me before) because I'm planning to use these objects with Spring. I haven't fully fleshed out how that will look but, overall, decorating classes (or using facades) seems to be the way to go. I wish I could split the "right answer" award between you and Ken :) I'll just be sure to upvote him...Analog
@gmale thanks for the followup and acceptance. I also thought Ken's was a good answer, so I upvoted him myself.Acridine
P
5

To my understanding, it sounds like your POJO models are data only (or you want to keep them that way). Why not create facade objects, like Query, Count or other named functionality grouping, that hides all of the associated mechanics of your manipulation algorithms?

It wouldn't pollute the POJOs which is what it sounds like you want to avoid.

Prostatectomy answered 23/1, 2012 at 16:22 Comment(3)
that's an interesting idea, to create objects by functionality instead of the underlying objects they work with. That is easy to understand for "count" but less easy to apply for things like findManager(employeesOnDuty). In those cases, I guess we could create Query subclasses that interface with specific model objects.Analog
Ken, good approach, but a question: how do you implement product.isModified() like this?Acridine
No optimal...but you could just follow the pattern Modified.isModified(Product p) and have a series of calls. Then product.isModified() would become Modified.isModified(product). The reality is that the functions are just moving from the POJO model classes to the facade and it becomes a syntax thing. Although you lose the ability to use implementation details for modified status but the OP was using classes provided to him so that wasn't really an option.Prostatectomy
C
3

despite the question is at the moment 1 year old, hope it may appear helpful to some other people. So your willing to keep POJOs and behavour separately, so that these statements have place: a) POJOs only have properties and getters/setters, b) the same model manipulation code is reused, c) it's clear which classes are responsible for manipulating the model is widely acknowledged and is being achieved via splitting application to tiers, i.e.:

  1. your POJOs - is plain model, containing fields/properties and just getters/setters;
  2. business logic relating manipulations upon model is placed into Service layer - in your case everything concerning manipulations on e.g. Employee can be allocated in EmployeeService (and thus reused in numerous places);
  3. everything relating persistance goes to DAO layer, e.g. EmployeeDAO;
  4. for exchanging data between layers, e.g. the view and service - DTOs (data transfer objects) can be used.

In case if business logic is put into model beans, that's called domain-driven design - folks ask about it here and there (personally I'm not the admirer of domain driven design).

Hope, this helps.

Candlewood answered 3/5, 2013 at 8:15 Comment(0)
J
2

In a similar situation, we've decided to promote all "functions" (behaviours) as first class citizens of our object model.

So we now have distinct packages, one with POJO only, and one where we have Function objects (aka Processors) aplying functions on these objects.

For instance a Contract object as the POJO, and ContractBilling, ContractFinder, ContractCloser, etc. as processors. Most of them operate on a Contract or list of contracts.

We've found it very efficient and this has forced us to properly document everything in our system (as function have become objects). The architectural benefits of this approach have been awesome and, even though we have much more objects in this design, we have smaller classes much more manageable, understandable and easy to evolve.

Joell answered 7/2, 2013 at 12:37 Comment(1)
Thanks this was helpful although it's essentially the same thing as the Ken's suggestion, with (arguably) better naming of the facade objects, which also makes it the same as the accepted answer that suggests the decorator pattern. +1 because it was helpful to hear about how this is working in your team.Analog
L
0

I guess for operations such as find*, they belong to service classes; but for 'isModified', a service class won't help, it will have to be in the pojo itself unless of course the modification too happens through a service class. The corresponding service class can then maintain a state of such objects in a collection.

Lorimer answered 13/4, 2012 at 5:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.