3

I'm creating a Java class that should encapsulate the six orbital elements of a celestial object, the six osculating elements of the same celestial object, the mass of the body and the name of the body. This means that my Java object must be created with no less than fourteen parameters, and I am now thinking about including another four constants of perturbation as parameters, which will bring that number up to eighteen.

This is how it looks with fourteen parameters:

new Planet("Mercury", 3.3022E23,0.387098, 0., 0.205637, 0.00002123, 7.00559, -0.00590158, 252.252, 149473., 77.4577, 0.1594, 48.3396, -0.122142)

I've looked around people say that a class that takes in more than ten parameters is probably poorly designed. They also say that a class should do one thing and one thing only. Well, I'm just doing one thing literally, the only thing the class does so far is calculating the position of the celestial object with those parameters as a function of time.

What is best practice for dealing with this situation?

C. E.
  • 10,297
  • 10
  • 53
  • 77
  • 1
    More classes for different types of fields and Builder instead of constructor. – tkroman Feb 23 '14 at 21:40
  • Can a group of these parameters be described as a single entity? This will allow you to create a class (or subclass) for them and pass them as a single argument. For instance `perturbation`. – tutuDajuju Feb 23 '14 at 21:41
  • If the values don't need to change then a constructor with methods to access only the values you will really need later on is what I do. – D-Klotz Feb 23 '14 at 21:41
  • OK, so far I have three suggestions: Builder, small classes that serve only to encapsulate data (basically arrays), and to keep using the constructor. No resolution because I don't know which one of you to trust. – C. E. Feb 23 '14 at 21:45
  • The answer really depends on what you want to achieve here. For example, do you want to make the code to instantiate a Planet shorter? Do you want to make the code more clear about what the numbers do? Do you want to introduce *compilation checks* for the parameters to the constructor (fourteen parameters: which one is which?)? – Radiodef Feb 23 '14 at 21:51
  • @Radiodef What I have works but what bothered me as I contemplated adding another four parameters was readability and maintainability. I had a feeling there could be a neater solution and that's why I wanted to ask how people do this generally. – C. E. Feb 23 '14 at 21:58

4 Answers4

3

I recommend the Bloch Builder, by Joshua Bloch (item 2) in Effective Java, 2nd edition:

http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2

It is a pattern designed specifically for classes with lots of fields, although it is intended for optional parameters, which is not your case. However, I still think this might be a good way for you to approach it. Such as

Planet p = new Planet.Builder("Mercury").gravity(3.3022E23).
   anotherAttribute(0.387098).avgTemp(0.).
   somethingElse(0.205637).andAnotherThing(0.00002123).
   ....
   build();

(change them to meaningful stuff...I have no idea what the numbers actually represent :)

I recommend against setters in the Planet object, in order to make the fields immutable ( https://www.google.com/search?q=fields+immutable+java+benefit).

I hope this helps.

aliteralmind
  • 19,847
  • 17
  • 77
  • 108
  • This is the right answer but your implementation is totally screwy. It should be new Planet.Builder().name("Mercury").gravity(....).build(); – Rob Feb 23 '14 at 21:57
  • That I'm putting the planet name in the constructor makes it "totally screwy"? I do understand about the underscore though...I happen to do something a bit different, and forgot to change it back.(http://programmers.stackexchange.com/questions/228939/how-to-improve-upon-blochs-builder-pattern-to-make-it-more-appropriate-for-use). Fixed. – aliteralmind Feb 23 '14 at 22:04
  • Like the setters also recommended here I don't think this is good OOP design. It allows the user of the class to create objects that will not work, because the method does not have access to all data that it needs. – C. E. Feb 23 '14 at 22:06
  • You're saying that Joshua Bloch's builder pattern is bad OOP design? You're going to have to elaborate a bit more on that one. It can absolutely be designed without sacrificing stability. You validate the data in the `build()` function, or the `Planet` constructor (which is what I do), or you can get really tricky and don't allow the build function to be called until each field is properly set (by having the self-returning functions return an alternate `NotYetReadyToBeBuilt` object...good luck with that one...). – aliteralmind Feb 23 '14 at 22:10
  • Personally, I've always found these builder pattern's to be a pain. IF a class doesn't need mandatory values before it becomes viable, then this approach is fine. But what I really don't like, is when people do this and forget some values. You end up with a non viable object and you might not know why. – D-Klotz Feb 23 '14 at 22:30
  • I just don't buy that at all. If it's inviable, then you're not validating the fields as necessary. If you validate *every* field in the `ToBeBuilt`'s constructor (for example), then you'll always have a stable object. For me, I choose to be notified this at runtime, but, as I stated in my previous comment, you could make it work at compile time if you really want to. – aliteralmind Feb 23 '14 at 22:36
  • It *is* more work, that's true, but as far as creating inviable objects, no. – aliteralmind Feb 23 '14 at 22:39
  • @aliteralmind no it's screwy because the whole pattern is a public static class and no public constructor, Dude. Maybe better read the section of Bloch's book again. Not much immutability if I can just call the constructor providing only the name and get an instance.. ?? – Rob Feb 23 '14 at 23:47
  • "Not much immutability if I can just call the constructor providing only the name and get an instance." Huh? You're worried someone might create a `Planet` with `new Planet.Builder().build()`? How would validation ***not*** catch this? Check the fields and crash! – aliteralmind Feb 24 '14 at 00:08
  • ***Don't finish construction*** unless all fields are valid. ***Throw an exception*** if some fields are missing or invalid. I'd like to see an example of how you propose to get around this. – aliteralmind Feb 24 '14 at 00:31
1

I would just have a bunch of setters. Maybe use name as constructor parameter. Just to make it clearer to read. Figuring out which of those 14+ parameters is which is just too difficult for the reader if you set them all in the constructor. Or use a builder as suggested by others.. Both are about the same for me.

kg_sYy
  • 1,127
  • 9
  • 26
  • Having setters is an anti-pattern. You can't reason behind the state of the object anymore since it's so easily mutable and could be modified by anything. – Sudip Bhandari Jun 28 '19 at 11:09
1

I would prefer combining the already mentioned solutions - as you write in your intro "I'm creating a Java class that should encapsulate the six orbital elements of a celestial object, the six osculating elements of the same element, the mass of the body and the name of the body.", it seems to me that you can group each six parameters into a new datastructure, so that you end up with four parameters for the Planet constructor (name, mass and the two parameter objects with six own values each) - next step I would ask myself if the six orbital and osculating elements somehow carry extra meaning or are merely a group of six (as in "arbitrary number") elements and can therefore be represented as a list.

Smutje
  • 17,733
  • 4
  • 24
  • 41
0

I would recommend you to use Builder Design Pattern. If you are using lombok annotation all the verbose code can be generated by using @Builder annotation.

@Builder
class Some{
private String a;
private String c;
private String d;
private String e;
}

You can generate object with the following semantics:

Some someObject = Some.builder()
                  .a("a")
                  .b("b)
                  .c("c")
                  .d("d")
                  .e("e)
                  .build();
Sudip Bhandari
  • 2,165
  • 1
  • 27
  • 26