LinkedList insert tied to inserted object - java

I have code that looks like this:
public class Polynomial {
List<Term> term = new LinkedList<Term>();
and it seems that whenever I do something like term.add(anotherTerm), with anotherTerm being... another Term object, it seems anotherTerm is referencing the same thing as what I've just inserted into term so that whenever I try to change anotherTerm, term.get(2) (let's say) get's changed too.
How can I prevent this from happening?
Since code was requested:
//since I was lazy and didn't want to go through the extra step of Polynomial.term.add
public void insert(Term inserting) {
term.add(inserting);
}
Code calling the insert method:
poly.insert(anotherTerm);
Code creating the anotherTerm Term:
Term anotherTerm = new Term(3, 7.6); //sets coefficient and power to 3 and 7.6
New code calling the insert method:
poly.insert((Term)anotherTerm.clone());
Which unfortunately still doesn't work due to clone() has protected access in java.lang.Object, even after doing public class Term implements Cloneable{

The solution is simple: make Term immutable.
Effective Java 2nd Edition, Item 15: Minimize mutability:
Immutable objects are simple.
Immutable objects can be shared freely.
Immutable objects make great building blocks for other objects.
Classes should be immutable unless there's a very good reason to make them mutable.
If a class cannot be made immutable, limit its mutability as much as possible.
Make every field final unless there is a compelling reason to make it non-final
Something as simple and small as Term really should be made immutable. It's a much better overall design, and you wouldn't have to worry about things like you were asking in your question.
See also
What is meant by immutable?
This advice becomes even more compelling since the other answers are suggesting that you use clone().
Effective Java 2nd Edition, Item 11: Override clone judiciously
Because of the many shortcomings, some expert programmers simply choose to never override the clone method and never invoke it except, perhaps, to copy arrays.
From an interview with author Josh Bloch:
If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken.
DO NOT make Term implements Cloneable. Make it immutable instead.
See also
How to properly override clone method?
Why people are so afraid of using clone() (on collection and JDK classes) ?

OK, replacing my old answer with this, now that I understand the question and behavior better.
You can do this if you like:
public void insertTerm(Term term) {
polynomial.insert(new Term(term));
}
and then create a new Term constructor like this:
public Term(Term term) {
this.coefficient = term.coefficient;
this.exponent = term.exponent;
}
That should work.

EDIT: Ok, I think I see what it is you're doing now. If you have this class:
public class Polynomial
{
List<Term> term = new LinkedList<Term>();
public void insert(Term inserting)
{
term.add(inserting);
}
}
And then you do this:
Polynomal poly = new Polynomal()
Term term = new Term();
poly.insert(term);
term.coefficient = 4;
...then the object term is the same object as poly.get(0). "term" and "poly.get(0)" are both references to the same object - changing one will change the other.

Question is no so clear, but i just try , when you are adding the objects , add anotherTerm.clone()

It sounds like you are not instantiating new Objects, just referencing the same one. You should instantiate a new Term, either with Term term = new Term(); or by cloning term.clone().
EDIT to be able to be cloned, Term need to implement the Cloneable interface. That means that you are responsible for how the new copy of a Term should be defined.
Hard to tell without seeing the code that calls the insert method, but sounds like that is the problem.

Related

Is it possible to make sure that two identical objects don't exist in JVM?

Consider this database model:
Book
isbn primary key
title
In a RDBMS, the database makes sure that two identical rows don't exist for the above model.
Similarly, in Java consider this object model:
Book
- isbn: int
- title: String
+ Book(isbn)
Let's say we are creating a Book object:
Book b = new Book(123456);
Later, in some other part of the code we are creating again an identical Book object:
Book c = new Book(123456);
Can Java make sure that no two objects exist in the JVM heap if they are identical? Just like a RDBMS does?
There's no built-in mechanism in Java that automatically does this for you. You could build something for this, but probably shouldn't. And if you do, then probably not in the way that you show in your question.
First: let's assume that these objects are immutable, so the problem is reduced to "let no two objects be constructed that have the same attributes". This is not a necessary restriction, but this way I can already demonstrate the issues with this approach.
The first issue is that it requires you to keep track of each Book instance in your program in a single central place. You can do that quite easily by having a collection that you fill when an object is constructed.
However, this basically builds a massive memory leak into your program because if nothing else hangs on to this Book object, that collection still will reference it, preventing it from being garbage collected.
You can work around that issue by using WeakReference object to hold on to your Book objects.
Next, if you want to avoid duplicates, you almost certainly want a way to fetch the "original" instance of a Book if you can't create a new one. You can't do that if you simply use the constructor, since the constructor can't "return another object", it will always create and return a new object.
So instead of new Book(12345) you want something like BookFactory.getOrCreateBook(12345). That factory can then either fetch the existing Book object with the given id or create a new one, as required.
One way to make the memory leak issue easier to handle (and also to potentially allow multiple parallel sessions each with their own set of unique Book objects) is to make the BookFactory be a BookSession: i.e. you instantiate one and it keeps tracks of its books. Now that BookSession is the "root" of all Books and if it no longer gets referenced it (and all the books it created) can potentially be garbage collected.
All of this doesn't even get into thread safety which is solvable reasonably easily for immutable objects but can get quite convoluted if you want to allow modifications while still maintaining uniqueness.
A simple BookSession could look a little like this (note that I use a record for book only for brevity of this sample code, this would leave the constructor visible. In "real" code I'd use an equivalent normal class where the constructor isn't accessible to others):
record Book(int isbn, String title) {}
class BookSession {
private final ConcurrentHashMap<Integer, Book> books = new ConcurrentHashMap<>();
public Optional<Book> get(int isbn) {
return Optional.ofNullable(books.get(isbn));
}
public Book getOrCreate(int isbn, String title) {
return books.computeIfAbsent(isbn, (i) -> new Book(i, title));
}
}
You can easily add other methods to the session (such as findByTitle or something like that).
And if you only ever want a single BookSession you could even have a public static final BookSession BOOKS somewhere, if you wanted (but at that point you have re-created the memory leak)
I do not know of a JVM internals specific way of doing this, but it is not that hard to achieve the basic goal. Joachim Sauer's answer goes into depth on why this might not be the greatest idea without some additional forethought :)
If you forego of thread safety, the code is basically just about creating a private constructor and use a factory method that keeps tab on created objects.
Pseudo Java follows
public class Book {
// potential memory leak, see Joachim Sauer's answer (WeakReference)
Map<Book> created = new Map<>();
// other internal fields follow
// can only be invoked from factory method
private Book(String isbn){ /* internals */ }
public Book get(String isbn){
if(created.has(isbn)) return created.get(isbn);
var b = new Book(isbn);
b.add(isbn, b);
return b;
}
}
Converting this to a thread safe implementation is just about adding some details * and is another question. Avoiding the potential memory leak means reading up on weak references.
i.e. locks (synchronized), mutexes, Concurrent*, Atomic*, etc
Neither of the other answers is technically correct.
They will often work, but in situations where multiple ClassLoaders are in play they will both fail.
Any object instance can ever only be unique within the context of a specific ClassLoader, thus 2 instances of the same Book can exist, even if you guard against multiples being created within a specific ClassLoader.
Usually this won't be a problem as many (especially simpler) programs will never have to deal with multiple ClassLoaders existing at the same time.
There is btw no real way to protect against this.

Side effects in Java methods

This might be a trivial question, but I need some clarification...
There is a book called Clean Code that says that our methods should be small, preferably up to 5-10 lines long. In order to achieve that we need to split our methods into smaller ones.
For instance, we may have someMethod() shown below. Let's say, modification of 'Example' takes 5 lines and I decide to move it into a separate method, modify 'Example' there and return it back to someMethod(). By doing this, someMethod() becomes smaller and easier to read. That's good, but there is a thing called "side effects" which says that we shouldn't pass an object to another method and modify it there. At least, I was told that it's a bad idea ) But I haven't seen anything prohibiting me from doing so in Clean Code.
public Example someMethod() {
// ... different lines here
Example example = new Example();
example = doSomethingHere(example, param1, param2, ...);
// ... different lines here
return example;
}
private Example doSomethingHere(Example example, 'some additional params here') {
// ... modify example's fields here ...
return example;
}
So, am I allowed to split the methods this way or such a side effect is prohibited and instead I should deal with a rather long-line method that definitely breaks Clean Code's rules talking about short methods?
UPDATED (more specific name for the sub-method)
public Example someMethod() {
// ... different lines here
Example example = new Example();
example = setExampleFields(example, param1, param2, ...);
// ... different lines here
return example;
}
private Example setExampleFields(Example example, 'some additional params here') {
// ... modify example's fields here ...
return example;
}
As JB Nizet commented, it's not actually a side effect if it's the only effect, so any blanket statement that "all side effects are bad" doesn't apply here.
Still, the main question stands: Is this (side) effect okay?
Talking about the principles first, side effects are, in general, dangerous for two reasons:
they make concurrency more difficult
they obscure/hide information
In your example, there is some information that is hidden. You could call this a potential side effect, and it can be exposed with a question: "Does this doSomethingHere method create a new object or modify the one I pass in?"
The answer is important, and even more so if it's a public method.
The answer should be trivial to find by reading the doSomethingHere method, especially if you're keeping your methods 'clean', but the information is nonetheless hidden/obscured.
In this specific case, I would make doSomethingHere return void. That way there's no potential for people to think that you've created a new object.
This is just a personal approach - I'm sure that plenty of developers say you should return the object you modify.
Alternatively, you can pick a 'good' method name. "modifyExampleInPlace" or "changeSomeFieldsInPlace" are pretty safe names for your specific example, imo.
we shouldn't pass an object to another method and modify it there.
Who says that? That is actually a good practice in order to split your function in a way that forms a "recipe" and have specific functions that know exactly how to populate your object properly.
What is not recommended (and probably the source where you got your recommendation misunderstood this rule) is defining a public API and modify the arguments. Users appreciate not having their arguments modified as it leads to less surprises. An example of that is passing arrays as arguments to methods.
When you define an object and pass it to an other method, method itself can modify the content of the object therein which may be unwanted in some cases. This is because you pass the reference(shallow copy) of the object to that method and method can modify that object.For example when you pass an Array, Arrays are objects, to a method, method can change the content of the Array which may not be what the caller method expects.
public static void main(String[] args){
int[] arr= {1,2,3,4};
y(arr);
//After the method arr is changed
}
public void y(int[] comingArray){
comingArray[0] = 10;
}
To make sure the values of Array cannot be changed, deep copy of the Array should be sent to method which is another story
However this is not the case when you use primite types(int, float etc.)
public static void main(String[] args){
int a= 1
y(a);
//After the method a is not changed
}
public void y(int comingInt){
comingInt = 5;
}
Due to the nature of the Objects, you should be carefulTo learn more about shallow copy and deep copy https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm

Preserving encapsulation of a generic in Java

Good evening.
I have a rather involved question. To practice Java, I've been re-implementing some of the data structures in the standard library. Stacks, LinkedLists, Trees, etc. I just established, through a very simple example, that the java.util.Stack class performs a deep copy when either the peek() or pop() methods are used. This is understandable, since the goal would be to protect the contents of the class from outside interference. So far, in my own implementation of the Stack (a naive implementation with a simple array, linked lists will come later), I have not cared for this at all:
public class ArrayStack<T> implements Stack<T> {
private T[] data; // Will expand the array when stack is full.
private int top; // serves as both top and count indicator.
...
...
#Override
public T pop() throws EmptyStackException {
if(top == -1)
throw new EmptyStackException("Stack is empty.");
return data[top--]; // Shallow copy, dangerous!
}
Unfortunately, since a generic cannot be instantiated, I cannot assume a copy constructor and do stuff like return new T(data[top--]); I've been looking around in S.O and I've found two relevant threads which attempt to solve the problem by using some variant of clone(). This thread suggests that the class's signature be extended to:
public class ArrayStack<T extends DeepCloneableClass> implements Stack<T>
...
where DeepCloneableClass is a class that implements an interface that allows for "deep cloning" (see the top response in that thread for the relevant details). The problem with this method, of course, is that I can't really expect from standard classes such as String or Integer to be extending that custom class of mine, and, of course, all my existing jUnit tests are now complaining at compile-time, since they depend on such Stacks of Integers and Strings. So I don't feel as if this solution is viable.
This thread suggests the use of a third-party library for cloning pretty much any object. While it appears that this library is still supported (latest bug fixes date from less than a month ago), I would rather not rely on third-party tools and use whatever Java can provide for me. The reason for this is that the source code for these ADTs might be someday shared with undergraduate college students, and I would rather not have them burdened with installing extra tools.
I am therefore looking for a simple and, if possible, efficient way to maintain a generic Java data structure's inner integrity while still allowing for a simple interface to methods such as pop(), peek(), popFront(), etc.
Thanks very much for any help!
Jason
Why do you need to clone the objects?
Your stack just has a collection of references. You probably don't need to clone them, just make a new array and put the appropriate references in it, then throw away the old array.
Integer, Strings, etc are all immutable, so their contents are safe by design.
As for custom objects, while experienced Java Programmers will certain have mixed feelings about it, implementing a custom interface is certainly one way to approach the problem.
Another one is to make <T extends Serializable> (which is implemented by Integer, String, etc) and "clone" through serialization.
But if you want to teach your students the "right way" I would definitively use a third party library... You can just create a lib folder in your project and configure you build tool / IDE to add the needed jars to the Classpath using relative paths, so your undergraduate students will not have to install or setup anything.
Just for reference, this question may be very useful.
I've been teaching Java introductory classes (as an IT Instructor / not as a college Professor) using this kind of approach, and it is way less painful than it sounds.
The comments helped me understand what I had wrong. I was using the following example to "prove" to myself and others that the Java Standard Library's Collections do a deep copy when providing references to objects in the Collection:
import java.util.Stack;
public class StackTestDeepCopy {
public static void main(String[] args){
Stack<String> st = new Stack<String>();
st.push("Jim");
st.push("Jill");
String top = st.peek();
top = "Jack";
System.out.println(st);
}
}
When printing st, I saw that objects had been unchanged, and concluded that a deep copy had taken place. Wrong! Strings are immutable, and therefore the statement top = "Jack" does not in any way modify the String (not that any Object would be "modified" by a statement like that, but I wasn't thinking straight), it just makes the reference point to a new place on the heap. A new example, involving an actually mutable class, made me understand the error in my ways.
Now that this problem has been solved, I'm quite baffled by the fact that the standard library allows for this. Why is it that accessing elements in the standard library is implemented as a shallow copy? Sounds very unsafe.
java.util.Stack doesn't do a deep copy:
import java.util.Stack;
public class Test {
String foo;
public static void main(String[] args) {
Test test = new Test();
test.foo = "bar";
Stack<Test> stack = new Stack<Test>();
stack.push(test);
Test otherTest = stack.pop();
otherTest.foo = "wibble";
System.out.println("Are the same object: "+(test.foo == otherTest.foo));
}
}
Results in:
Are the same object: true
If it did do a copy then test and otherTest would point to a different object. A typical stack implementation simply returns a reference to the same object that was added onto the stack, not a copy.
You probably also want to set the array item to null before returning, otherwise the array will still hold a reference to the object.

Method runs for all instances of a class

I ran across this problem, which has been driving me nuts. In a nutshell, I instantiate two objects of the same class. When I run a method in one object, the other object is affected too as if I called a method on a 2nd object explicitly. I was wondering if someone could please give me a hand on this.
Suppose, I have class Portfolio...
public class Portfolio implements Cloneable {
public ArrayList<Instrument> portfolio;
private String name;
private String description;
public Portfolio() {
portfolio = new ArrayList<Instrument>();
}
public Portfolio(Portfolio copyFrom) {
//attempt to copy the object by value
this.portfolio = (ArrayList<Instrument>) copyFrom.portfolio.clone();
this.name = copyFrom.name;
this.description = copyFrom.description;
}
public void applyStress(Stress stress) {
this.portfolio.set(0,this.portfolio.get(0)+1;
}
1st constructor is used to instantiate an object, etc. 2nd constructor is used to copy an object by value.
A method applyStress is used to run through sum calculations. in our case I simplified the method, so that it does nothing but adds +1 to whatever is in the object.
So I would instantiate an object as
Portfolio p = new Portfolio();
then I would assign to a portfolio field, some instruments;
p.portfolio = someInstrumentList;
then I would copy by value the portfolio p into pCopy:
Portfolio pCopy = new Portfolio(p);
So at this time I am have two objects that are the same. Further one is a copy-by-value object. Changing values of fields in pCopy does not affect same fields in p.
Now, when I run a method applyStress on p, then the values of the instrument list in pCopy will also change.
In other words, if p.portfolio.get(0) == 1, then after p.applyStress, I would expect to see that p.portfolio.get(0) is 2 and pCopy.portfolio.get(0) is 1
But what I see instead is p.portfolio.get(0) is 2 and pCopy.portfolio.get(0) is also 2
I do not understand why this happens. It is not the static modifier issue, as there is no static modifiers. Anyone has any ideas?
The clone method applied to you your ArrayList reference does a shallow copy, not a deep copy. This implies that whatever you had in the original collection is shared by the cloned one.
This means that you need to clone every instrument as well, or provide a copy constructor for every one of them.
this.portfolio = new ArrayList<Instrument>();
for(Instrument toBeCopiedInstrument : copyFrom.portfolio){
this.portfolio.add(new Instrument(toBeCopiedInstrument ));
}
By default .clone() does what is called a shallow copy, this means it just copies a reference to the objects that are held in the List that is being cloned, it doesn't actually copy the objects themselves to new instances.
What you need to do is implement a custom deep copy for the List and each of the items held in the list. But deep clone is a broken concept and implementation in Java.
A copy constructor isn't a really good pattern in Java either, because you end up copying references as well in most cases and every object you inject to the constructor has to follow the same copy constructor semantics all the way down the chain. Unlike C++, this is manual, tedious, unmaintainable and error prone process!
.clone() and implements Cloneable are some of the most complex to get correct concepts in Java. They are rarely needed in well designed applications. That is, if you are using .clone() you are probably doing it wrong. If making bit wise copies of your objects are part of your design for something other than storage, you might want to revisit your design.
Josh Bloch on Design
Object's clone method is very tricky. It's based on field copies, and
it's "extra-linguistic." It creates an object without calling a
constructor. There are no guarantees that it preserves the invariants
established by the constructors. There have been lots of bugs over the
years, both in and outside Sun, stemming from the fact that if you
just call super.clone repeatedly up the chain until you have cloned an
object, you have a shallow copy of the object. The clone generally
shares state with the object being cloned. If that state is mutable,
you don't have two independent objects. If you modify one, the other
changes as well. And all of a sudden, you get random behavior.
Immutable
A better pattern is to make everything immutable. That way you don't need separate instances, you can share instances until they need to change, then they change and you have a new instance with the new data, that can be shared without any side effects.

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