I am in the process of making an application which is meant to be a personal pet project of mine, designed around comparing two vehicles against 1 another from a computer game I play
the vehicles have a bunch of stats such as speed, healthpoints, turret traversal etc. and I want to create a small app that will highlight the differences between the vehicles, however i have come to a stumbling block where the arguments taken for the constructors for each vehicles are huge and difficult to read
Here is an example of object creation with the given constructor:
HeavyTank T110E5 = new HeavyTank("T110E5", 2200,54.56d, 875, 37, 30, 254,76,38, 203,127,70,300, 202, 6,32, 400,745,10);
I am sure you would guess that this application I am making, is a tank comparer based off World of tanks, where I am hard coding the tank stats, but as you can see, the arguments taken are difficult to read, making it difficult to create new objects without getting confused. Each tank has different stats so this means I would have to hard code close to 100+ tanks individually. If someone here has a solution of reducing the mess, or recommendations I am willing to listen.
I would also like to reinstate a point I made up at the top, this application is not for commercial purposes and is purely just a pet personal project of mine.
I would suggest storing your tank data in a simple file format, for example a CSV file with one line per tank. Then your tank object can take something like an InputStream as a parameter and stream in the tank details from the file.
And if you use a CSV file you can just use a typical spreadsheet program to edit your tank details quite easily.
This sounds like a perfect use case for the builder pattern.
There are lots of good examples on how to implement this. Here's how Google uses it in the CacheBuilder class of Guava:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
This particular example even uses method chaining, but that's less important. The main feature is that newBuilder() sets some default parameters which you can then selectively adjust with one or more method calls. Finally, you call build() to actually create the instance (and calling the actual constructor with the long list of parameters).
JavaDoc + good IDE will help you figure out which arguments you need to provide.
Otherwise, you could wrap the arguments in a single object and pass that to the constructor. An overloaded constructor would work well, because you could unpack the arguments and pass it to the original.
If you know the arguments in advance, you would always put them in a Properties file and read them using java.util.Properties.
Use a fluent interface. Though generally used outside the ccnstructor, why not partly inside.
You might consider putting that information in a properties file that you could then easily edit. Your constructor would then take the name of the properties file and have the additional job of enforcing completeness and validation. While I still favor strongly-typed constructors, a property file approach might be easier to use in the long run.
If you have a mix of required and optional arguments (with sensible defaults), you can use the builder pattern. An example from Effective Java:
public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;
public static class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder(int s) {
this.sodium = s;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
Then you can do things like:
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build();
The first two arguments are required (240 and 8). The other attributes are optional and can be supplied through aptly-named methods.
You can still use the builder pattern if you have a large number of required parameters; you can check that they have been set in the build() method.
I recommend implementing a Standard Constructor without any Parameters. Setters and Getters can be generated via eclipse. This helps keeping a look at all and adding or removing members.
The next thing you should do is to write all your vehicle stuff in a XML file. The tags help you with the vehicle names, values etc. Also adding and removing vehicles will be easier.
You can access XML files via JDOM. Just read the vehicle tags and call the setter methods with the values from the XML file. This can be done with a loop.
In the end, the only thing you have to take care of is your vehicle XML file.
Feel free to ask if there are any questions.
You have three reasonable options that I see:
1) Use the standard contructor("string", value, "string", value,...) format.
This is likely more effort than you will want to put into this program.
2) Use a constructor() method and then setPropertyName(value) methods for each property.
This will allow you to easily add them, but you still need to do it by hand.
3) Read the data in from a file.
Setup the file using columns with names (to avoid just moving the "I forgot what value comes next" problem to a different place) This should be the best.
My suggestion would be to create an object to hold these types with a specific parameter list upon initialization. You can divide up that object based on what types go together logically. Then you'll only need two or three constructors with five parameters each. For example:
HeavyTank T110E5 = new HeavyTank("T110E5", 2200,54.56d, 875, 37, 30, 254,76,38, 203,127,70,300, 202, 6,32, 400,745,10);
would become:
StatObject1 stat1 = new StatObject1(2200, 54.56d);
StatObject2 stat2 = new StatObject2(875, 37, 30);
StatObject3 stat3 = new StatObject3(254, 76, 38);
...
HeavyTank T110E5 = new HeavyTank("T110E5", stat1, stat2, stat3, ...);
or however that works out logically for your system. The benefit of this is that you can apply this to any other tank you create, and can create modifiers in every StatObject class specific to that data.
Related
NOTE: This question is primarily theoretical; I've since given up on using this immediately, as everything I've thought up overcomplicates my code and smells a little like an antipattern. However, I find it interesting in theory and would love the community's help with it.
I'm refurbing some code written around the turn of the century for image manipulation; beginning by chopping apart a single class with a lot of redundancies into a number of BiFunction filters. They each accept a BufferedImage and an intensity (Double), and return a new BufferedImage. Of course, a lot of these image filters have additional concerns, like radius-of-effect or another intensity, and if I'm going to chain them efficiently, I would like to be able to set these as "uniforms" (or quasi-constants) before use.
My current method is simply to extend my ImageFilter class further, and set the uniforms as Bean-style properties. What I initially thought of doing was constraining them to a generic class that was held in an interface, and having something along the lines of:
public void addProperty(Uniform<T> property, T type)
in there, where T is specified entirely by the Uniform.
The original class kept all of these uniforms in a HashMap by String name, but that's the beginning of a lot of bad habits and I clean it up every time I see it. If a misspelling causes code to misfire, it's irresponsible code. I would prefer to just use a Singleton there, like a grown up.
So, in summary, is it possible in Java to bind a parameter type to the generic of another parameter? If so, how? If not, does anyone know of any plans to extend Java Generics in the future, in such a manner?
Thanks for any input!
Yes it's possible all we have to do is define the type parameter on the method itself. Check the example below.
public class Uniform {
}
public class ImageFilter {
//store in a list which can store any type of Uniform
List<Uniform<?>> uniformList = new ArrayList<Uniform<?>>();
//store in a map which can store any type of uniform and uses type as key
Map<Class<?>, Uniform<?>> uniformMap = new HashMap<>();
//Type parameter 'T' on the method itself
public <T> void addProperty(Uniform<T> property, T type) {
uniformList.add(property);
uniformMap.put(type.getClass(), property);
}
}
{
//sample usage
new ImageFilter().addProperty(new Uniform<>(), "test");
new ImageFilter().addProperty(new Uniform<>(), new Double(2.0));
}
Following is sample usage
new ImageFilter().addProperty(new Uniform<>(), "test");
//or
new ImageFilter().addProperty(new Uniform<String>(), "test");
//if for some reason JVM can't infer the type itself, it can be set manually
new ImageFilter().<String>addProperty(new Uniform<String>(), "test");
See this link for more information.
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.
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.
I've been developing a massive Role Playing Game. The problem is that I'm having trouble engineering how will I manage the Item and Inventory system. Currently I have something similar to this:
public abstract class Item has 5 Nested classes which all are abstract and static that represent the types of Items. Every Nested class has an unique use(), delete() (Which finalizes the class instance) and sell()(triggers delete) void. They also have optional getter and setter methods, like the setAll() method which fills all necesary fields.
Default: Has base price, tradeability boolean, String name, etc... Very flexible
Weapon: Else than the things that the Default type has, it has integers for stat bonus on being equipped(used in the equip() and unequip() voids). Interacts with public class Hero.
Equipment: Similar to Weapon, just that it has an Enum field called 'EquipSlot' that determines where it is equipped.
Consumable: Similar to default, just that has a consume() void that enables the player to apply certain effects to an Hero when using it. Consuming usually means triggering the delete() void.
Special: Usually quest related items where the 'Tradeable' boolean is static, final and always false.
Now, the way that I make customized items is this.
First, I make a new class (Not abstract)
Then, I make it extend Item.ItemType
Then, I make a constructor which has the setAll(info) void inside.
Then, I can use this class in other classes.
It all looks like this:
package com.ep1ccraft.Classes.Items.Defaults;
import com.ep1ccraft.apis.Item.*;
public class ItemExample extends Item.Default {
public ItemExample() { // Constructor
this.setAll(lots of arguments here);
}
}
then I can do:
ItemExample something = new ItemExample();
And I have a perfect ItemExample with all the properties that I want, So, I can make various instances of it, and use amazing methods like 'getName()' and that kind of stuff.
The problems come to Naming the instances, as I do not know how to make an automated form that will give the instance a Different name from the other instance so they don't collide. Also, I want to implement an inventory system that uses slots as containers and can keep stacks (Stackable items only), also the main feature of it is that you can drag and drop them into other slots (For example, to reorganize or to move to another inventory instance like a bank, or to place in an hero's weapon or equipment slots, if it is allowed) and that you can click on them to display a screen that shows the name, description and possible actions of the Item (Which trigger the previously mentioned delete() and use() voids).
Thank you for reading all that! I know that maybe I'm asking for too much, but I'll appreciate any answers anyway!
So basically, you're asking for a unique identifier for your object. There are probably countless approaches to this, but the three different approaches that immediately come to mind are:
1: A UUID; something like:
java.util.UUID.randomUUID()
Pros: A very, very simple solution...
Cons: It does generate a large amount of bytes (16 + the object itself), taking memory / disk storage, which might be an issue in a MMO
2: A global running number; something like:
class ID {
private static volatile long id = 0;
public synchronized long nextId() {
return id++;
}
}
Pros: Again, a simple solution...
Cons: Even though this is a very simple class, it does contain "volatile" and "synchronized", which might be an issue for an MMO, especially if it is used heavily. Also, What happens after X years of running time, if you run out of numbers. A 64 bit long does require quite a lot of named objects to be created, it may not be an issue after all... you'll have to do the math yourself.
3: Named global running numbers; something like:
class NamedID {
private static volatile Map<String, Long> idMap = new HashMap<String, Long>();
public synchronized long nextId(String name) {
Long id = idMap.get(name);
if (id == null) {
id = 0;
} else {
id++;
}
idMap.put(name, id);
return id;
}
}
Pros: You get id's "localized" to whatever name you're using for it.
Cons: A bit more complex solution, and worse than "2" in terms of speed, since the synchronzation lasts longer.
Note: I couldn't figure out how to make this last suggestion faster; i thought of using a ConcurrentHashMap, but that won't work since it works on a lower level; i.e. it will not guarantee that two thread does not interfere with each other between the idMap.get and the idMap.put statements.
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