Builder pattern with error-checking: is it possible/advisable? - java

I am using the Builder pattern to make it easier to create objects. However, the standard builder pattern examples do not include error-checking, which are needed in my code. For example, the accessibility and demandMean arrays in the Simulator object should have the same length. A brief framework of the code is shown below:
public class Simulator {
double[] accessibility;
double[] demandMean;
// Constructor obmitted for brevity
public static class Builder {
private double[] _accessibility;
private double[] _demandMean;
public Builder accessibility(double[] accessibility) {
_accessibility = accessiblity.clone();
return this;
}
public Builder demandMean(double[] demandMean) {
_demandMean = demandMean.clone();
return this;
}
// build() method obmitted for brevity
}
}
As another example, in a promotion optimization problem, there are various promotional vehicles (e.g. flyers, displays) and promotion modes, which are a set of promotional vehicles (e.g. none, flyer only, display only, flyer and display). When I create the Problem, I have to define the set of vehicles available, and check that the promotion modes use a subset of these vehicles and not some other unavailable vehicles, as well as that the promotion modes are not identical (e.g. there aren't two promo modes that are both "flyer only"). A brief framework of the code is shown below:
public class Problem {
Set<Vehicle> vehicles;
Set<PromoMode> promoModes;
public static class Builder {
Set<Vehicle> _vehicles;
Set<PromoMode> _promoModes;
}
}
public class PromoMode {
Set<Vehicle> vehiclesUsed;
}
My questions are the following:
Is there a standard approach to address such a situation?
Should the error checking be done in the constructor or in the builder when the build() method is called?
Why is this the "right" approach?

When you need invariants to hold while creating an object then stop construction if any parameter violates the invariants. This is also a fail-fast approach.
The builder pattern helps creating an object when you have a large number of parameters.
That does not mean that you don't do error checking.
Just throw an appropriate RuntimeException as soon as a parameter violates the objects invariants

You should use the constructor, since that follows the Single Responsibility Principle better. It is not the responsibility of the Builder to check invariants. It's only real job is to collect the data needed to build the object.
Also, if you decide to change the class later to have public constructors, you don't have to move that code.
You definitely shouldn't check invariants in setter methods. This has several benefits:
* You only need to do checking ONCE
* In cases such as your code, you CAN'T check your invariants earlier, since you're adding your two arrays at different times. You don't know what order your users are going to add them, so you don't know which method should run the check.
Unless a setter in your builder does some intense calculations (which is rarely the case - generally, if there's some sort of calculation required, it should happen in the constructor anyway), it doesn't help very much to 'fail early' in, especially since fluent Builders like yours use only 1 line of code to build the object anyway, so any try block would surround that whole line either way.

The "right" approach really depends on the situation - if it is invalid to construct the arrays with different sizes, i'd say it's better to do the handling in the construction, the sooner an invalid state is caught the better.
Now, if you for instance can change the arrays and put in a different one - then it might be better to do it when calling them.

Related

How to compare Java function object to a specific method? [duplicate]

Say I have a List of object which were defined using lambda expressions (closures). Is there a way to inspect them so they can be compared?
The code I am most interested in is
List<Strategy> strategies = getStrategies();
Strategy a = (Strategy) this::a;
if (strategies.contains(a)) { // ...
The full code is
import java.util.Arrays;
import java.util.List;
public class ClosureEqualsMain {
interface Strategy {
void invoke(/*args*/);
default boolean equals(Object o) { // doesn't compile
return Closures.equals(this, o);
}
}
public void a() { }
public void b() { }
public void c() { }
public List<Strategy> getStrategies() {
return Arrays.asList(this::a, this::b, this::c);
}
private void testStrategies() {
List<Strategy> strategies = getStrategies();
System.out.println(strategies);
Strategy a = (Strategy) this::a;
// prints false
System.out.println("strategies.contains(this::a) is " + strategies.contains(a));
}
public static void main(String... ignored) {
new ClosureEqualsMain().testStrategies();
}
enum Closures {;
public static <Closure> boolean equals(Closure c1, Closure c2) {
// This doesn't compare the contents
// like others immutables e.g. String
return c1.equals(c2);
}
public static <Closure> int hashCode(Closure c) {
return // a hashCode which can detect duplicates for a Set<Strategy>
}
public static <Closure> String asString(Closure c) {
return // something better than Object.toString();
}
}
public String toString() {
return "my-ClosureEqualsMain";
}
}
It would appear the only solution is to define each lambda as a field and only use those fields. If you want to print out the method called, you are better off using Method. Is there a better way with lambda expressions?
Also, is it possible to print a lambda and get something human readable? If you print this::a instead of
ClosureEqualsMain$$Lambda$1/821270929#3f99bd52
get something like
ClosureEqualsMain.a()
or even use this.toString and the method.
my-ClosureEqualsMain.a();
This question could be interpreted relative to the specification or the implementation. Obviously, implementations could change, but you might be willing to rewrite your code when that happens, so I'll answer at both.
It also depends on what you want to do. Are you looking to optimize, or are you looking for ironclad guarantees that two instances are (or are not) the same function? (If the latter, you're going to find yourself at odds with computational physics, in that even problems as simple as asking whether two functions compute the same thing are undecidable.)
From a specification perspective, the language spec promises only that the result of evaluating (not invoking) a lambda expression is an instance of a class implementing the target functional interface. It makes no promises about the identity, or degree of aliasing, of the result. This is by design, to give implementations maximal flexibility to offer better performance (this is how lambdas can be faster than inner classes; we're not tied to the "must create unique instance" constraint that inner classes are.)
So basically, the spec doesn't give you much, except obviously that two lambdas that are reference-equal (==) are going to compute the same function.
From an implementation perspective, you can conclude a little more. There is (currently, may change) a 1:1 relationship between the synthetic classes that implement lambdas, and the capture sites in the program. So two separate bits of code that capture "x -> x + 1" may well be mapped to different classes. But if you evaluate the same lambda at the same capture site, and that lambda is non-capturing, you get the same instance, which can be compared with reference equality.
If your lambdas are serializable, they'll give up their state more easily, in exchange for sacrificing some performance and security (no free lunch.)
One area where it might be practical to tweak the definition of equality is with method references because this would enable them to be used as listeners and be properly unregistered. This is under consideration.
I think what you're trying to get to is: if two lambdas are converted to the same functional interface, are represented by the same behavior function, and have identical captured args, they're the same
Unfortunately, this is both hard to do (for non-serializable lambdas, you can't get at all the components of that) and not enough (because two separately compiled files could convert the same lambda to the same functional interface type, and you wouldn't be able to tell.)
The EG discussed whether to expose enough information to be able to make these judgments, as well as discussing whether lambdas should implement more selective equals/hashCode or more descriptive toString. The conclusion was that we were not willing to pay anything in performance cost to make this information available to the caller (bad tradeoff, punishing 99.99% of users for something that benefits .01%).
A definitive conclusion on toString was not reached but left open to be revisited in the future. However, there were some good arguments made on both sides on this issue; this is not a slam-dunk.
To compare labmdas I usually let the interface extend Serializable and then compare the serialized bytes. Not very nice but works for the most cases.
I don't see a possibility, to get those informations from the closure itself.
The closures doesn't provide state.
But you can use Java-Reflection, if you want to inspect and compare the methods.
Of course that is not a very beautiful solution, because of the performance and the exceptions, which are to catch. But this way you get those meta-informations.

Am I violating the "open/closed" principle?

Scenario: I stored some information (e.g. an array of doubles) in a class field (say field Measurements, array of integers in a class MeasureData). Now I would like to use this data to perform some calculations (e.g compute the arithmetic mean of the array, the maximum and the minimum). At the moment, I don't know if in the future I'll need to do any other operation on those data (e.g. maybe I will need to get the standard deviation, the sum or whatever). I'll have many objects of type MeasureData.
Solution: I could write a class Calculator, declare it final, use a private constructor and use several static methods to perform the calculations I need. This seems to make sense, since Calculator acts as an utility class, without any field, much like the standard Math class.
Problem: if, in a couple of months, I'll need to do any other calculation, I'll be needing to write another static method in Calculator. Does this mean to violate the open/closed principle (after all, I'm modifying the implementation of the class Calculator)?
The strict answer is yes; OCP states that a class is open for extension but closed for modification. You would be modifying Calculator, and, hence, violating OCP (as you've already concluded).
This leads to two points:
First, is violating OCP a big deal in this case? You're additively changing Calculator to add a new method to it. Calculator is a static helper class used to get meaningful data from your objects. Adding a new method, like calculating SD, is not going to affect any of the other operations within it. With a proper implementation, is there really a way that adding this method could compromise your project?
Second, if you feel like the OCP violation is not acceptable, then this is a textbook example of where Strategy Pattern can be utilized. Consider:
Measurements.Java
public class Measurements {
private int[] data;
public Measurements(int[] data) {
this.data = data;
}
public Number performCalculation(Calculation c) {
return c.performCalculation(data);
}
}
Calculation.java
public interface Calculation {
Number performCalculation(int[] data);
}
You can then make a calculation class for each different calculation you want to do on the data (eg: MeanCalculation, StdDevCalculation, etc.). If you want a new calculation (eg: MedianCalculation), you can make this without modifying any of the other code in this area (closed for modification, open for extension; OCP compliant). The end result looks like:
Measurements values = ...
Number mean = values.performCalculation(new MeanCalculation());
Number SD = values.performCalculation(new StdDevCalculation());
// etc.
I'm not saying this is the best approach (or best implementation of the approach even) for your specific case; you need to answer that for yourself. But I hope this answer provides a decent external perspective on the matter.

Class with many parameters, beyond the Builder pattern

Context
Suppose you have a component with a great many options to modify its behavior. Think a table of data with some sorting, filtering, paging, etc. The options could then be isFilterable, isSortable, defaultSortingKey, etc etc. Of course there will be a parameter object to encapsulate all of these, let's call it TableConfiguration. Of course we don't want to have a huge constructor, or a set of telescopic constructors, so we use a builder, TableConfigurationBuilder. The example usage could be:
TableConfiguration config = new TableConfigurationBuilder().sortable().filterable().build();
So far so good, a ton of SO questions deals with this already.
Moving forward
There is now a ton of Tables and each of them uses its own TableConfiguration. However, not all of the "configuration space" is used uniformly: let's say most of the tables is filterable, and most of those are paginated. Let's say, there are only 20 different combinations of configuration options that make sense and are actually used. In line with the DRY principle, these 20 combinations live in methods like these:
public TableConfiguration createFilterable() {
return new TableConfigurationBuilder().filterable().build();
}
public TableConfiguration createFilterableSortable() {
return new TableConfigurationBuilder().filterable().sortable().build();
}
Question
How to manage these 20 methods, so that developers adding new tables can easily find the configuration combination they need, or add a new one if it does not exist yet?
All of the above I use already, and it works reasonably well if I have an existing table to copy-paste ("it's exactly like Customers"). However, every time something out of the ordinary is required, it's hard to figure out:
Is there a method doing exactly what I want? (problem A)
If not, which one is the closest one to start from? (problem B)
I tried to give the methods some very descriptive names to express what configuration options are being built in inside, but it does not scale really well...
Edit
While thinking about the great answers below, one more thing occurred to me:
Bonus points for grouping tables with the same configuration in a type-safe way. In other words, while looking at a table, it should be possible to find all its "twins" by something like go to definition and find all references.
I think that if you are already using the builder pattern, then sticking to the builder pattern would be the best approach. There's no gaining in having methods or an enum to build the most frequently used TableConfiguration.
You have a valid point regarding DRY, though. Why setting the most common flags to almost every builder, in many different places?
So, you would be needing to encapsulate the setting of the most common flags (to not repeat yourself), while still allowing to set extra flags over this common base. Besides, you also need to support special cases. In your example, you mention that most tables are filterable and paginated.
So, while the builder pattern gives you flexibility, it makes you repeat the most common settings. Why not making specialized default builders that set the most common flags for you? These would still allow you to set extra flags. And for special cases, you could use the builder pattern the old-fashioned way.
Code for an abstract builder that defines all settings and builds the actual object could look something like this:
public abstract class AbstractTableConfigurationBuilder
<T extends AbstractTableConfigurationBuilder<T>> {
public T filterable() {
// set filterable flag
return (T) this;
}
public T paginated() {
// set paginated flag
return (T) this;
}
public T sortable() {
// set sortable flag
return (T) this;
}
public T withVeryStrangeSetting() {
// set very strange setting flag
return (T) this;
}
// TODO add all possible settings here
public TableConfiguration build() {
// build object with all settings and return it
}
}
And this would be the base builder, which does nothing:
public class BaseTableConfigurationBuilder
extends AbstractTableConfigurationBuilder<BaseTableConfigurationBuilder> {
}
Inclusion of a BaseTableConfigurationBuilder is meant to avoid using generics in the code that uses the builder.
Then, you could have specialized builders:
public class FilterableTableConfigurationBuilder
extends AbstractTableConfigurationBuilder<FilterableTableConfigurationBuilder> {
public FilterableTableConfigurationBuilder() {
super();
this.filterable();
}
}
public class FilterablePaginatedTableConfigurationBuilder
extends FilterableTableConfigurationBuilder {
public FilterablePaginatedTableConfigurationBuilder() {
super();
this.paginated();
}
}
public class SortablePaginatedTableConfigurationBuilder
extends AbstractTableConfigurationBuilder
<SortablePaginatedTableConfigurationBuilder> {
public SortablePaginatedTableConfigurationBuilder() {
super();
this.sortable().paginated();
}
}
The idea is that you have builders that set the most common combinations of flags. You could create a hierarchy or have no inheritance relation between them, your call.
Then, you could use your builders to create all combinations, without repeting yourself. For example, this would create a filterable and paginated table configuration:
TableConfiguration config =
new FilterablePaginatedTableConfigurationBuilder()
.build();
And if you want your TableConfiguration to be filterable, paginated and also sortable:
TableConfiguration config =
new FilterablePaginatedTableConfigurationBuilder()
.sortable()
.build();
And a special table configuration with a very strange setting that is also sortable:
TableConfiguration config =
new BaseTableConfigurationBuilder()
.withVeryStrangeSetting()
.sortable()
.build();
I would remove your convenience methods that call several methods of the builder. The whole point of a fluent builder like this is that you don't need to create 20 something methods for all acceptable combinations.
Is there a method doing exactly what I want? (problem A)
Yes, the method that does what you want is the new TableConfigurationBuilder(). Btw, I think it's cleaner to make the builder constructor package private and make it accessible via a static method in TableConfiguration, then you can simply call TableConfiguration.builder().
If not, which one is the closest one to start from? (problem B)
If you already have an instance of TableConfiguration or TableConfigurationBuilder it may be nice to pass it into the builder such that it becomes preconfigured based on the existing instance. This allows you to do something like:
TableConfiguration.builder(existingTableConfig).sortable(false).build()
If almost all configuration options are booleans, then you may OR them together:
public static int SORTABLE = 0x1;
public static int FILTERABLE = 0x2;
public static int PAGEABLE = 0x4;
public TableConfiguration createTable(int options, String sortingKey) {
TableConfigurationBuilder builder = new TableConfigurationBuilder();
if (options & SORTABLE != 0) {
builder.sortable();
}
if (options & FILTERABLE != 0) {
builder.filterable();
}
if (options & PAGEABLE != 0) {
builder.pageable();
}
if (sortingKey != null) {
builder.sortable();
builder.setSortingKey(sortingKey);
}
return builder.build();
}
Now table creation doesn't look so ugly:
TableConfiguration conf1 = createTable(SORTEABLE|FILTERABLE, "PhoneNumber");
How about having a configuration string? This way, you could encode the table settings in a succinct, yet still readable way.
As an example, that sets the table to be sortable and read-only:
defaultTable().set("sr");
In a way, these strings resemble the command-line interface.
This could be applicable to other scenarios that support the table re-use. Having a method that creates the Customers table, we can alter it in a consistent way:
customersTable().unset("asd").set("qwe");
Possibly, this DSL could be even improved by providing a delimiter character, that would separate the set and unset operations. The previous sample would then look as follows:
customersTable().alter("asd|qwe");
Furthermore, these configuration strings could be loaded from files, allowing the application to be configurable without recompilation.
As for helping a new developer, I can see the benefit in a nicely separated subproblem that can be easily documented.
What I would have done, if I didn't know SO
Assumption: There probably is way less than 2^(# of config flags) reasonable configurations for the table.
Figure out all the configuration combinations that are currently used.
Draw a chart or whatever, find clusters.
Find outliers and think very hard why they don't fit into those clusters: is that really a special case, or an omission, or just laziness (no one implemented full text search for this table yet)?
Pick the clusters, think hard about them, package them as methods with descriptive names and use them from now on.
This solves problem A: which one to use? Well, there is only a handful of options now. And problem B as well: if I want something special? No, you most probably don't.

How can implementation know if an input parameter is mutable?

For example, some method has the next implementation:
void setExcludedCategories(List<Long> excludedCategories) {
if (excludedCategories.contains(1L)) {
excludedCategories.remove(1L);
}
}
And it's called in the next way:
setExcludedCategories(Array.asList(1L, 2L, 3L));
Of course, it will lead ot an exception java.lang.UnsupportedOperationException when it will try to remove item.
The question: how can I modify this code to be sure that the input parameter excludedCategories supports remove?
UPD:
Thanks for answers. Let's summarize results:
Always create new ArrayList from the input list to be sure it's mutable - a lot of useless memory would be used -> NO.
Catch the UnsupportedOperationException.
Specify in the JavaDoc that a caller mustn't pass an immutable list - anybody read the JavaDoc? When something doesn't work only :)
Don't use Arrays.asList() in a caller's code - that's an option, if you an owner of this code, but anyway you should know if this concrete method allows immutable or not (see 3).
It seems the second variant is the only way to resolve this problem.
How can I modify this code to be sure that the input parameter excludedCategories supports remove?
In the general case, you can't. Given an arbitrary class that implements the List API, you cannot tell (statically or dynamically) if the optional methods are supported.
You can use instanceof tests to check if the class of the list is known to implement the method or to not implement it. For example ArrayList and LinkedList do, but Collections.UnmodifiableList does not. The problem is that your code could encounter list classes that your tests don't cover. (Especially if it is a library that is intended to be reusable in other peoples applications.)
You could also try to test the behavior of previously unknown classes; e.g. create a test instance, try a remove to see what happens, and record the behavior in a Map<Class, Boolean>. There are two problems with this:
You may not be able to (correctly) instantiate the list class to test it.
The behavior could depend on how you instantiate the class (e.g. constructor parameters) or even on the nature of the element you are trying to remove ... though the latter is pushing the boundary of plausibility.
In fact, the only completely reliable approach is to call the method and catch the exception (if it is thrown) each and every time.
In short, you can't know. If an object implements an interface (such as List) you can't know if it will actually do what is expected for all of the methods. For instance Collections.unmodifiableList() returns a List that throws UnsupportedOperationException. It can't be filtered out via the method signature if you want to be able to get other List implementations.
The best you can do is to throw IllegalArgumentException for known subtypes that don't support what you want. And catch UnsupportedOperationException for other types of cases. But really you should javadoc your method with what is required and that it throws IllegalArgumentException in other cases.
That depends somewhat on what you're trying to do. In your posted example for example you could just catch the UnsupportedOperationException and do something else instead.
This assumes that you can assume that non-mutable containers will throw that on every attempt to modify the container and will do so without side effects (that is they are indeed non-mutable).
In other cases where your code has other side effects than trying to modify the container you will have to make sure these doesn't happen before knowing that you can modify the container.
You can catch the exception in an utility class like in the example below (as others mentioned). Bad thing is you have to do insert/delete to test if there will be exception. You can not use instanceof since all Collections.Unmodifiablexxx classes have default access.
CollectionUtils:
import java.util.List;
public class CollectionUtils {
public <T> boolean isUnmodifiableList(List<T> listToCheck) {
T object = listToCheck.get(0);
try {
listToCheck.remove(object);
} catch (UnsupportedOperationException unsupportedOperationException) {
return true;
}
listToCheck.add(0, object);
return false;
}
}
Main:
import java.util.Arrays;
import java.util.List;
public class Main {
private static final CollectionUtils COLLECTION_UTILS = new CollectionUtils();
public static void main(String[] args) {
setExcludedCategories(Arrays.asList(1L, 2L, 3L));
}
private static void setExcludedCategories(List<Long> excludedCategories) {
if (excludedCategories.contains(1L)) {
if(!COLLECTION_UTILS.<Long>isUnmodifiableList(excludedCategories)){
excludedCategories.remove(1L);
}
}
}
}
Arrays.asList(T... a) returns the List<java.util.Arrays.ArrayList<E>> which is an immutable list. To get your code working just wrap the result with java.util.ArrayList<T> like shown below
setExcludedCategories(new ArrayList<Long>(Arrays.asList(1L, 2L, 3L)));
Always create new ArrayList from the input list to be sure it's mutable - a lot of useless memory would be used -> NO.
Thats actually the preferred way to do things. "A lot of useless memory" isn't a lot in most practical situations, certainly not in your cited exampled.
And ignoring that, its the only robust and inutitively understood idiom.
The only workable alternative would be to explicitly change the name of your method (thus communicating its behavior better), form the example you show, name it "removeExcludedCategories" if its meant to modify the argument list (but not an objects state).
Otherwise if it is meant as a bulk-setter, you're out of luck, there is no commonly recognized naming idiom that clearly communicates that the argument collection is directly incorporated into the state of an object (its dangerous also because the objects state can then be altered without the object knowing about it).
Also, only marginally related, I would design not an exclusion list, but an exclusion set. Sets are conceptually better suited (no duplicates) and there are set implementations that have far better runtime complexity for the most commonly asked question: contains().

Best practice for passing many arguments to method?

Occasionally , we have to write methods that receive many many arguments , for example :
public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}
When I encounter this kind of problem , I often encapsulate arguments into a map.
Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;
......
public void doSomething(Map<Object,Object> params)
{
// extracting params
Object objA = (Object)params.get("objA");
......
}
This is not a good practice , encapsulate params into a map is totally a waste of efficiency.
The good thing is , the clean signature , easy to add other params with fewest modification .
what's the best practice for this kind of problem ?
In Effective Java, Chapter 7 (Methods), Item 40 (Design method signatures carefully), Bloch writes:
There are three techniques for shortening overly long parameter lists:
break the method into multiple methods, each which require only a subset of the parameters
create helper classes to hold group of parameters (typically static member classes)
adapt the Builder pattern from object construction to method invocation.
For more details, I encourage you to buy the book, it's really worth it.
Using a map with magical String keys is a bad idea. You lose any compile time checking, and it's really unclear what the required parameters are. You'd need to write very complete documentation to make up for it. Will you remember in a few weeks what those Strings are without looking at the code? What if you made a typo? Use the wrong type? You won't find out until you run the code.
Instead use a model. Make a class which will be a container for all those parameters. That way you keep the type safety of Java. You can also pass that object around to other methods, put it in collections, etc.
Of course if the set of parameters isn't used elsewhere or passed around, a dedicated model may be overkill. There's a balance to be struck, so use common sense.
If you have many optional parameters you can create fluent API: replace single method with the chain of methods
exportWithParams().datesBetween(date1,date2)
.format("xml")
.columns("id","name","phone")
.table("angry_robots")
.invoke();
Using static import you can create inner fluent APIs:
... .datesBetween(from(date1).to(date2)) ...
It's called "Introduce Parameter Object". If you find yourself passing same parameter list on several places, just create a class which holds them all.
XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);
Even if you don't find yourself passing same parameter list so often, that easy refactoring will still improve your code readability, which is always good. If you look at your code 3 months later, it will be easier to comprehend when you need to fix a bug or add a feature.
It's a general philosophy of course, and since you haven't provided any details, I cannot give you more detailed advice either. :-)
First, I'd try to refactor the method. If it's using that many parameters it may be too long any way. Breaking it down would both improve the code and potentially reduce the number of parameters to each method. You might also be able to refactor the entire operation to its own class. Second, I'd look for other instances where I'm using the same (or superset) of the same parameter list. If you have multiple instances, then it likely signals that these properties belong together. In that case, create a class to hold the parameters and use it. Lastly, I'd evaluate whether the number of parameters makes it worth creating a map object to improve code readability. I think this is a personal call -- there is pain each way with this solution and where the trade-off point is may differ. For six parameters I probably wouldn't do it. For 10 I probably would (if none of the other methods worked first).
This is often a problem when constructing objects.
In that case use builder object pattern, it works well if you have big list of parameters and not always need all of them.
You can also adapt it to method invocation.
It also increases readability a lot.
public class BigObject
{
// public getters
// private setters
public static class Buider
{
private A f1;
private B f2;
private C f3;
private D f4;
private E f5;
public Buider setField1(A f1) { this.f1 = f1; return this; }
public Buider setField2(B f2) { this.f2 = f2; return this; }
public Buider setField3(C f3) { this.f3 = f3; return this; }
public Buider setField4(D f4) { this.f4 = f4; return this; }
public Buider setField5(E f5) { this.f5 = f5; return this; }
public BigObject build()
{
BigObject result = new BigObject();
result.setField1(f1);
result.setField2(f2);
result.setField3(f3);
result.setField4(f4);
result.setField5(f5);
return result;
}
}
}
// Usage:
BigObject boo = new BigObject.Builder()
.setField1(/* whatever */)
.setField2(/* whatever */)
.setField3(/* whatever */)
.setField4(/* whatever */)
.setField5(/* whatever */)
.build();
You can also put verification logic into Builder set..() and build() methods.
There is a pattern called as Parameter object.
Idea is to use one object in place of all the parameters. Now even if you need to add parameters later, you just need to add it to the object. The method interface remains same.
You could create a class to hold that data. Needs to be meaningful enough though, but much better than using a map (OMG).
Code Complete* suggests a couple of things:
"Limit the number of a routine's parameters to about seven. Seven is a magic number for people's comprehension" (p 108).
"Put parameters in input-modify-output order ... If several routines use similar parameters, put the similar parameters in a consistent order" (p 105).
Put status or error variables last.
As tvanfosson mentioned, pass only the parts of a structured variables ( objects) that the routine needs. That said, if you're using most of the structured variable in the function, then just pass the whole structure, but be aware that this promotes coupling to some degree.
* First Edition, I know I should update. Also, it's likely that some of this advice may have changed since the second edition was written when OOP was beginning to become more popular.
Using a Map is a simple way to clean the call signature but then you have another problem. You need to look inside the method's body to see what the method expects in that Map, what are the key names or what types the values have.
A cleaner way would be to group all parameters in an object bean but that still does not fix the problem entirely.
What you have here is a design issue. With more than 7 parameters to a method you will start to have problems remembering what they represent and what order they have. From here you will get lots of bugs just by calling the method in wrong parameter order.
You need a better design of the app not a best practice to send lots of parameters.
Good practice would be to refactor. What about these objects means that they should be passed in to this method? Should they be encapsulated into a single object?
Create a bean class, and set the all parameters (setter method) and pass this bean object to the method.
Look at your code, and see why all those parameters are passed in. Sometimes it is possible to refactor the method itself.
Using a map leaves your method vulnerable. What if somebody using your method misspells a parameter name, or posts a string where your method expects a UDT?
Define a Transfer Object . It'll provide you with type-checking at the very least; it may even be possible for you to perform some validation at the point of use instead of within your method.
I would say stick with the way you did it before.
The number of parameters in your example is not a lot, but the alternatives are much more horrible.
Map - There's the efficiency thing that you mentioned, but the bigger problem here are:
Callers don't know what to send you without referring to something
else... Do you have javadocs which states exactly what keys and
values are used? If you do (which is great), then having lots of parameters
isn't a problem either.
It becomes very difficult to accept different argument types. You
can either restrict input parameters to a single type, or use
Map<String, Object> and cast all the values. Both options are
horrible most of the time.
Wrapper objects - this just moves the problem since you need to fill the wrapper object in the first place - instead of directly to your method, it will be to the constructor of the parameter object.
To determine whether moving the problem is appropriate or not depends on the reuse of said object. For instance:
Would not use it: It would only be used once on the first call, so a lot of additional code to deal with 1 line...?
{
AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
SomeObject i = obj2.callAnotherMethod(a, b, c, h);
FinalResult j = obj3.callAFinalMethod(c, e, f, h, i);
}
May use it: Here, it can do a bit more. First, it can factor the parameters for 3 method calls. it can also perform 2 other lines in itself... so it becomes a state variable in a sense...
{
AnObject h = obj.callMyMethod(a, b, c, d, e, f, g);
e = h.resultOfSomeTransformation();
SomeObject i = obj2.callAnotherMethod(a, b, c, d, e, f, g);
f = i.somethingElse();
FinalResult j = obj3.callAFinalMethod(a, b, c, d, e, f, g, h, i);
}
Builder pattern - this is an anti-pattern in my view. The most desirable error handling mechanism is to detect earlier, not later; but with the builder pattern, calls with missing (programmer did not think to include it) mandatory parameters are moved from compile time to run time. Of course if the programmer intentionally put null or such in the slot, that'll be runtime, but still catching some errors earlier is a much bigger advantage to catering for programmers who refuse to look at the parameter names of the method they are calling.
I find it only appropriate when dealing with large number of optional parameters, and even then, the benefit is marginal at best. I am very much against the builder "pattern".
The other thing people forget to consider is the role of the IDE in all this.
When methods have parameters, IDEs generate most of the code for you, and you have the red lines reminding you what you need to supply/set. When using option 3... you lose this completely. It's now up to the programmer to get it right, and there's no cues during coding and compile time... the programmer must test it to find out.
Furthermore, options 2 and 3, if adopted wide spread unnecessarily, have long term negative implications in terms of maintenance due to the large amount of duplicate code it generates. The more code there is, the more there is to maintain, the more time and money is spent to maintain it.
This is often an indication that your class holds more than one responsibility (i.e., your class does TOO much).
See The Single Responsibility Principle
for further details.
If you are passing too many parameters then try to refactor the method. Maybe it is doing a lot of things that it is not suppose to do. If that is not the case then try substituting the parameters with a single class. This way you can encapsulate everything in a single class instance and pass the instance around and not the parameters.
... and Bob's your uncle: No-hassle fancy-pants APIs for object creation!
https://projectlombok.org/features/Builder

Categories

Resources