Refactoring method with two outputs - java

Background
I have encountered many methods in code I am tasked with refactoring that follow a general pattern as follows:
Take a composite input
Find children of composite according to some criterion
Remove children from composite, but keep track of them
Return the updated composite and append the removed children to an output parameter collection
In code it looks like this:
public Composite trim(Composite composite, List<Child> removed){
for (Child child : composite.getChildren()){
if (criterion(child)){
composite.remove(child);
removed.add(child);
}
}
}
There's a number of code smells here, but what I would like to address is how I can refactor this into immutable code that, accordingly, doesn't write to an output parameter.
Possible, but not-so-elegant solution
public Map<String, Object> trim(Composite composite){
final List<Child> removableChildren = filter(composite);
final Composite trimmed = copyCompositeWithoutChildren(composite, removableChildren);
return wrapInMap(removableChildren, trimmed);
}
Question
Are there more succinct ways of doing this using, for instance, functional programming approaches like collect or splice in groovy or Java 8 that might inform a better, albeit more verbose, approach in Java pre-8? Examples from either of the languages would be highly appreciated.
Edit:
2nd possible solution
Inspired by Steinar's answer: Use strategy pattern.
public interface FilterStrategy {
List<Child> filter(List<Child> children);
}
public interface RemovalResponseStrategy {
void respond(List<Child> removedChildren);
}
public class CompositeTrimmer {
private final FilterStrategy filterStrategy;
private final RemovalResponseStrategy removalResponseStrategy;
public Composite trim(final Composite composite){
final List<Child> removableChildren =
filterStrategy.filter(composite.getChildren());
final Composite trimmed =
copyCompositeWithoutChildren(composite, removableChildren);
removalResponseStrategy.respond(removableChildren);
return trimmed;
}
}

You didn't say what kind of freedom you have to change the Composite class or which methods are
already available. So I just made something up. It can probably be refitted to what you have available
quite easily.
A little bit more groovy solution would be something like this:
def trim(Composite composite, Closure removeCriterion) {
List<Child> removedChildren = composite.children.findAll(removeCriterion)
List<Child> remainingChildren = composite.children - removedChildren
Composite filteredComposite = new Composite(children: remainingChildren)
[filteredComposite, removedChildren]
}
You don't need to specify the criterion as a parameter, but I kind of like it. Makes the code more
flexible.
So basically, first find children to remove, then create a new composite with the remaining children.
Finally return a list of both outputs. Since groovy supports multiple assignments from a list, that's
an easy way to return several outputs.
The solution was tested with a dummy implementation of Composite and Child.
Usage and test:
def test() {
Composite composite = new Composite(children: [
new Child(name: 'Angus'),
new Child(name: 'Steinar'),
new Child(name: 'Erik'),
new Child(name: 'William'),
])
def (Composite filteredComposite, List<Child> removedChildren) =
trim(composite) {
Child child -> child.name > 'H'
}
assert filteredComposite.children == [new Child(name: 'Angus'), new Child(name: 'Erik')]
assert removedChildren == [new Child(name: 'Steinar'), new Child(name: 'William')]
}
#ToString(includePackage = false)
#EqualsAndHashCode
class Child {
String name
}
#ToString(includePackage = false)
#EqualsAndHashCode
class Composite {
List<Child> children
}

Related

ModelMapper: transferring attribute from root to each elements of a list

Model
We use org.modelmapper for our bean mapping. Here is the data model (using public for brevity):
class Root {
public String foo;
public List<Element> elements;
}
class Element {
public String foo; // <- this is the field that should be replicated from RootDTO
public String bar;
}
class RootDTO {
public String foo;
public List<ElementDTO> elements;
}
class ElementDTO {
public String bar; // notice how there is no `foo` attribute in the DTO
}
Expected mapping (example)
Input:
RootDTO
|-- foo = "foo"
|-- elements
|--ElementDTO(bar="bar")
|--ElementDTO(bar="something")
|--ElementDTO(bar="else")
Output:
Root
|-- foo = "foo"
|-- elements
|--Element(foo="foo", bar="bar")
|--Element(foo="foo", bar="something")
|--Element(foo="foo", bar="else")
Problem
As you can see, ModelMapper would handle this case very easily if it wasn't for the Element.foo field that should take its value from RootDTO.foo.
How do I configure ModelMapper to achieve my goal?
I'd want the solution to:
Not require developers to change the mapping's code if new matching attributes were to be introduced in the model.
To be done through a single method call to the ModelMapper.
Current configuration
myModelMapper.typeMap(ElementDTO.class, Element.class).implicitMappings();
myModelMapper.typeMap(RootDTO.class, Root.class).implicitMappings();
Results when using that configuration
Root mappedRoot = myModelMapper.map(rootDto, Root.class);
assertEquals(rootDto.foo, mappedRoot.foo);
assertEquals(rootDto.elements.size(), mappedRoot.elements.size());
// let's assume we have 1 Element
assertEquals(rootDto.elements.get(0).bar, mappedRoot.elements.get(0).bar);
assertEquals(rootDto.foo, mappedRoot.elements.get(0).foo); // FAILS! But this is what I want
// indeed, the value of `mappedRoot.elements.get(0).foo` is `null` because it was not mapped
Explored avenues
Idea 1
It seems to me like if I could set an order I could simply configure it this way:
Converter<RootDTO, Root> replicateFooValue = new Converter<>() {
#Override
public Root convert(MappingContext<RootDTO, Root> context) {
final String valueToReplicate = context.getSource().foo;
for (Element elem : context.getDestination().elements) {
elem.foo = valueToReplicate;
}
return context.getDestination();
}
};
myModelMapper.typeMap(ElementDTO.class, Element.class).implicitMappings();
myModelMapper.typeMap(RootDTO.class, Root.class).implicitMappings()
.thenUseConverter(replicateFooValue);
... but I do not think this is possible.
Idea 2
If I could use the ModelMapper's mapping functionality from within the Converter<> itself, then I could use the implicitMappings() before trying to simply set the value I want, but once again: I do not think that this is possible.
Idea 3
One way to be sure that it'd work would be:
Converter<RootDTO, Root> myCustomConverter = new Converter<>() {
#Override
public Root convert(MappingContext<RootDTO, Root> context) {
final Root mappedRoot = new Root();
// map each field individually, by hand
return mappedRoot;
}
};
myModelMapper.addConverter(myCustomConverter);
... but it requires maintenance if I was to add new fields in my data model.
Idea 4
Add a Converter<RootDTO, Element> that would only populate the foo value. That would result in the following usage:
Root mappedRoot = myModelMapper.map(rootDto, Root.class);
mappedRoot.elements.forEach(e -> myModelMapper.map(rootDto, e));
This is not ideal because then the entire expected behavior is not encapsulated in a single call: the developers have to know (and remember) to make that second call as well to achieve the desired mapping.
Idea 5
Make a utility class that encompasses the logic shown by Idea 4.
This is also a bad idea because it requires developers to know (and remember) about why they need to do that specific mapping this way instead of using the ModelMapper.
Turns out there is a way to introduce some ordering. Here is the solution:
Converter<RootDTO, Root> finishMapping = new Converter<>() {
#Override
public Root convert(MappingContext<RootDTO, Root> context) {
final String srcFoo = context.getSource();
var dest = context.getDestination();
dest.elements.stream().forEach(e -> e.foo = srcFoo);
return dest;
}
};
myModelMapper.typeMap(ElementDTO.class, Element.class).implicitMappings();
myModelMapper.typeMap(RootDTO.class, Root.class).implicitMappings()
.setPostConverter(finishMapping); // this is executed AFTER the implicit mapping

avoiding if conditions for similar type of checks

Is there anyway to avoid these if conditions? because there may be different type of objects coming in.
if ("OpenOrder".equals(order.getClass().getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
}
if ("ExecutionOrder".equals(order.getClass().getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
You can use a Router pattern to do this. Simple add the computations in a Map like this:
Map<String, Function> router = new HashMap<>();
router.put("OpenOrder", (value) -> OpenOrderBuilder.createOFSMessage((OpenOrder) value));
router.put("ExecutionOrder", (value) -> ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order));
And you can route the order using the String key. Here is a "OpenOrder" example:
String result = (String) router.get("OpenOrder").apply(order);
There are many ways to do it. Which one to choose, depends on your needs and in this case in particular on how many different types of objects you will have.
I suggest looking at concepts like interfaces and inheritance and on specific design patterns.
One approach I tend to like, although still not perfect, works as follows:
interface Order {
}
interface OrderBuilder<T> {
T forType();
Object createOFSMessage(Order order);
}
class OpenOrderBuilder<OpenOrder> implements OrderBuilder {
#Override
OpenOrder forType() {
return OpenOrder.class;
}
...
}
class ExecutionOrderBuilder<ExecutionOrder> implements OrderBuilder {
#Override
ExecutionOrder forType() {
return ExecutionOrder.class;
}
...
}
class MyProcessor {
Map<Class, OrderBuilder> obs;
public void initialize() {
List<OrderBuilder> builders = new ArrayList<>();
builders.add(new OpenOrderBuilder());
builders.add(new ExecutionOrderBuilder());
obs = new HashMap<Class, OrderBuilder>();
for(OrderBuilder b : builders) {
obs.put(b.forType(), b);
}
}
public Object createOFSMessage(Order order) {
return obs.get(order.getClass()).createOFSMessage(order);
}
}
In the above example, adding a new implementation would just consist of adding an entry to the builders collection. While in the example above it's done manually, normally this is done through Dependency Injection and frameworks like spring (in which case, the initialize method may turn into a constructor with builders as an #Autowired argument).
There are of course other ways, some more simple some more complicated. The best way really depends on what you have to do and one key rule: the less code you have the better.
First one should not forget the switch-on-string:
switch (order.getClass().getSimpleName()) {
case "OpenOrder":
return OpenOrderBuilder.createOFSMessage((OpenOrder) order); //Returns String
case "ExecutionOrder":
return ExecutionOrderBuilder.createOFSMessage((ExecutionOrder) order); //Returns String
}
The code however shows inheritance being used in combination with static child class factories. Evidently a createOFSMessage is not desired in the Order base class.
Then use a non-static "builder" - a factory. Follow the strategy pattern.
If you already know the type when calling the method, this code can help you :
private String CreateOFSMessage(Class<T> classOrder) {
if ("OpenOrder".equals(classOrder.getSimpleName())) {
return OpenOrderBuilder.createOFSMessage((classOrder) order);
}else if ("ExecutionOrder".equals(classOrder.getSimpleName())) {
return ExecutionOrderBuilder.createOFSMessage((classOrder) order);
}
}

How to name a composite class like this?

Our team are using Spring Boot 2 with sql2o as db library. In the paste in our services, for trivial methods, we simply call the repository and returns the model. For example, if I have a Supplier table, I had in the service
#Override
public List<Supplier> findAll() {
return supplierRepository.findAll();
}
Now, since in our controllers we need 99% in the cases other objects correlated to the model, I would create a composite class that holds the model and other models. For example:
#Override
public List<UnknownName> findAll() {
List<Supplier> suppliers = supplierRepository.findAll();
List<UnknownName> res = new ArrayList<>();
UnknownName unknownName;
LegalOffice legalOffice;
if (suppliers != null) {
for (Supplier supplier in suppliers) {
unknownName = new UnknownName();
unknownName.setSupplier(supplier);
legalOffice = legalOfficeService.findByIdlegaloffice(supplier.getLegalofficeid);
unknownName.setLegalOffice(legalOffice);
res.add(unknownName);
}
}
return res;
}
What should the name of class UnknownName?
PS: I simplified the code for better redability, but I use a generic enrich() function that I call for all the find methods, so I don't have to dupe the code.
I would recommend SupplierDto or SupplierLegalOfficeDto. DTO stands for Data Transfer Objects and it's commonly used for enriched models (more here).
Also you shouldn't check suppliers for null as repository always returns a non-null list.
In the end, I adopted the suffix Aggregator, following the Domain-driven design wording.

How Can I make This Algorithm Generic?

I am working on an object cache of CMS objects. I need to maintain a parent/child relationship between a Product and child objects (Options and Attributes). The parent (Product) is illustrated in the first code sample.
It is easy enough to do, but I am looking for a way to make the assignment of the child to the parent, as shown in the 2nd code block, generic.
Since all CMS objects extend CMSContent, I can use ProductID. However, is there a way to make the field (e.g. ProductAttribute) generic so that I can put the algorithm in a method and call the method with a parent and child object to make the attribute assignment?
I know that an ORM framework like Hibernate is appropriate here, but that won't fit since I have a fixed database structure.
public class Product extends CMSContent {
private List<ProductAttribute> productAttributes;
private List<ProductOptions> productOptions;
// getters,setters
}
Algorithm to match them up.
// attach Product Attributes to Product
for (Product p : listP) {
Map<String, Object> parameters = new HashMap<String, Object>();
for (ProductAttribute po : listPA) {
parameters.put("pid", p.getPid());
parameters.put("ref", po.getRid());
int i = jdbcTemplate.queryForInt(sqlAttr, parameters); // select count(*), 1 if matched.
if (i == 1) {
p.getProductAttributes().add(po); // generic field?
}
}
}
Wouldn't this two Methods in Product help
public void add(ProductAttribute item){
productAttributes.add(item);
}
public void add(ProductOption item){
productOption.add(item);
}
so you should be able to just add a ProductAttribute or a ProductOption

Can Hibernate automatically manage the order of child collections?

Given a Parent class which has many Children, can Hibernate automatically manage the order of said children? From reading documentation and searching, it seems that the #OrderColumn annotation may enable this, but I've not found any examples of how to do it. The closest thing I've found is JPA 2.0 #OrderColumn annotation in Hibernate 3.5, which looks a bit discouraging, given that it looks just like what I want to do.
Here is a rough sketch of what I'm trying to do, minus the annotations, since I'm not sure what they would be:
class Parent {
// I probably need some sort of #OrderColumn annotation here, right?
private List<Child> children = new ArrayList<Child>;
}
class Child {
private Parent parent;
private int order;
}
class SomeBusinessLogic {
public static void createFamily() {
Parent dad = new Parent("Tom");
List<Children> children = dad.getChildren();
children.add(new Child("Alice");
children.add(new Child("Bob");
children.add(new Child("Carl");
session.save(dad);
}
public static void addChild(Parent parent) { // assuming Tom is passed in
List<Children> children = parent.getChildren();
children.add(1, new Child("Zelda");
session.save(parent);
// now, the order should be: Alice, Zelda, Bob, Carl
}
}
If someone can give me enough details to make this toy example work (or tell me that it will not work), I would be most appreciative.
In order to store children in its order you should map it with #IndexedColumn annotation it will create indexed column on your child objects table.

Categories