4

My question comes from this other question.

The first time I read the question, I understood (maybe worngly) that the OP was asking for a different way to initialize the object in such a way that it would allow him to extend and make easier the maintenance. Hence my answer.

However, some comments seem to agree with passing an array as a single argument would be even easier.

We usually don't turn a function with # named parameters into a function with a single array of # positions.

public Message(String id, String a, String b){...}

public Message(String[] arguments){...}
public Message(String... arguments){...}

Turning # named parameters (all the same type) into a array is something I have done back in my early days as coder. Back then, I realised how brittle they were. For example, these structures are very sensible to changes. The order of the elements within the array and the length of the array matters, because we map attributes, fields or variables to positions within the array.

The following question agree with me. The only apparent difference is that the former question, the function is a constructor and in the last one is a method.

So looking at both questions linked here, I see how on one side we are suggesting to use arrays the way I commented and on the other side, we are discouraging from doing the very same practice.

My question boils down to, from the OOP point of view, Are arrays (as data structures) suitable for constructors but unsuitable methods?

If yes. Why? Do we apply different "rules" to define constructor's and method's signatures? Ultimately, both are functions.

If not. When is appropriated to use one or another?

To me, both cases lacks on maintainability, readability and scalability.

Laiv
  • 14,283
  • 1
  • 31
  • 69
  • 3
    C# specifically supports arrays as first-class method parameter objects. It even provides a special `params` keyword so that a variable number of arguments can be specified. It's hard to imagine how they would incorporate that into the language if it weren't useful. You haven't really explained why you think such practices are brittle. – Robert Harvey Jul 21 '17 at 21:25
  • 1
    As to your answer on the previous question, I find it to be a perfect example of the kind of over-engineering that seems to be so much in vogue these days. – Robert Harvey Jul 21 '17 at 21:28
  • 1
    @Laiv Do you mean storing multiple, potentially heterogeneously typed, parameters into a single array, in place of a `Params` class? – Alexander Jul 21 '17 at 21:45
  • @Alexander not exactly. Let's say we model the class `Customer` and we initialize it with an array which holds the name, surname, a code and some more parameters. All of them strings – Laiv Jul 22 '17 at 07:44
  • @Laiv Why would you *want* to do that? Those pieces of data aren't related, why would you put them together in an array? – Alexander Jul 22 '17 at 07:49
  • That's my question. Take a look at the link I shared in my question. The OP has an homogenous number of attributes (strings). He would like to make the constructor simpler among other things. – Laiv Jul 22 '17 at 07:53
  • Looking for a proper title to my question. I think I failed in expressing my doubts. – Laiv Jul 22 '17 at 09:50
  • It is impossible to say anything meaningful about parameters named `a`, `b`, `c` or `arguments`. The same problem is in the question you refer to, which is the cause of the confusion in the first place. Maybe they are appropriate as individual values, maybe an array is appropriate, maybe an object or multiple objects. **Without meaningful names this is all guesswork**. You will not get any useful answer to the question as posed. – JacquesB Jul 22 '17 at 10:55
  • @JacquesB you are already answering my question. If meaningful names is what really matters in order to choose the strategy, then this is a useful answer. If you feel like elaborating the answer, I would apreciate It. I'm not looking for a black or white answer. Just a reasoning. – Laiv Jul 22 '17 at 11:06
  • 5
    @Laiv: In short, an array is appropriate if all items are treated uniformly. If the method treat items differently based on the index, then an array is not appropriate. An array with givenName, surName, age etc as fields is probably wrong, since code would treat givenName differently than age, but having an array with addresses might be appropriate if all addresses are processed the same way. Without meaningful names in the example it is impossible to way whether this is the case or not. – JacquesB Jul 22 '17 at 12:04
  • 1
    You all are right. I have understated the relevance of the lack on meaningful param names in the former question. In consequence I rushed into a solution that in many senses is overengineering. – Laiv Jul 22 '17 at 22:22

4 Answers4

9

You can absolutely use arrays as data structures, and pass them to constructors and methods. Let me propose few examples:

  • A curve fitting application. The complex model is broken into array of double-precision floating point numbers that are the fitting parameters. The function that calculates the fitting error will take in this array of double-precision floating point numbers in addition to a reference to the complex model. The benefit is that the fitting code itself doesn't need to know about the complex model, it only needs to know about N fitting parameters. So, you can develop the fitting code independently of your simulation code.
  • Drawing line charts. You will give the line chart library two arrays of double-precision floating point numbers: one that contains the X values and other that contains the Y values. If you use Java, the benefit is that array is much faster and needs less garbage-collection than some abstract List of Pair<Double, Double> objects.
  • Command line arguments. The function main takes an array of String objects.
  • Simulating one-dimensional curves for fitting. The function takes a data point of X coordinates along with a complex model, and calculates the quantity of interest for each data point.

I'm sure there are many other examples. I just picked the examples that are relevant to the application I'm currently developing.

juhist
  • 2,579
  • 10
  • 14
  • Sorry, I should have been more precise. I know that for math algorithm, graphic design, cartography, etc... arrays are common. But if we are modeling the class `Message`, I don't see how expressing its strucutre as array make the whole thing simpler and maintenable. – Laiv Jul 22 '17 at 08:07
  • I don't see how this changes a thing. If the message consists of number of fields of the same type without special names, sure, you could use an array there. – juhist Jul 22 '17 at 09:13
  • So why don't we just work with arrays instead of named parameters? – Laiv Jul 22 '17 at 09:15
  • Because sometimes, parameters do have meaningful names. Then you use either parameters or a Map or similar. – juhist Jul 22 '17 at 09:15
  • Do you usually code this way? Using Maps or named parameters indistinctly? – Laiv Jul 22 '17 at 09:17
  • @Laiv The benefits of named parameters include optional parameters can be defaulted individually and I don't have to memorize the positional meanings of a 6 argument constructor. When these are not issues, arrays of arguments are perfectly fine. When they are issues, and the language wont support named parameters [you can take matters into your own hands](http://www.informit.com/articles/article.aspx?p=1216151&seqNum=2) – candied_orange Jul 22 '17 at 17:31
  • Ok, I didn't think in languages that might not support named parameters. Thank you for the reference! – Laiv Jul 22 '17 at 17:34
  • @juhist I have decided to check besc's answer for the reasons I have commented on his answer. However, your answer is also right (I can't check both). At least, the score compensates my decision. Hope you don't mind. – Laiv Jul 23 '17 at 19:33
9

There is no difference in the suitability of arrays [1] as function or constructor arguments. The real question is if an array is a correct model for the kind of data you have. In your example where you have a 2-part message with an ID, transforming (pseudo code)

Message(String id, String a, String b)

to

Message(String[3] message)

is wrong because an array is the wrong data structure for that kind of data. You do not have a list of three texts. What you have is three pieces of data that comprise a single structured entity. So even the three separate arguments are highly questionable because they fail to express that they all belong together. At least use something like this:

struct MessageData {
    String id;
    String a;
    String b;
}

Message(MessageData message)

However, that’s not the ideal data structure, either. id should really be of its own distinct type that maybe holds a string inside. Why?

  • It is an identifier, not a text. So it shouldn’t be a string.
  • We have textual data that represents two different concepts: “identifier” and “payload text”. (At least that’s how I chose to interpret a and b for lack of more specific info about what kind of message this is.) Different concepts should be modelled as different types.

What this all boils down to is: Use the type system to its full extent to make your data structures as expressive as possible. After all, that’s why we have type systems in the first place.

[1]: I’m using “array” in a broad sense here – as in a container for a sequence of values

Laiv
  • 14,283
  • 1
  • 31
  • 69
besc
  • 1,133
  • 6
  • 6
  • This is the sort of answer I was expecting. I also suggested in my [answer](https://softwareengineering.stackexchange.com/a/353114/222996) encapsulation, which indeed, contribute to type our model far beyone from the primitive types. Despite my examples don't reflect this point. However, your point around typing the concept ID has make me realise that I often fail in modeling this elements. I like your question and hence the Up vote. If you don't mind I would like see If I'm missing something else. – Laiv Jul 22 '17 at 09:44
  • Reading this answer, I come to realise that maybe the title of my question doesn't reflect my concern. Would you mind to suggest a better way to do It? – Laiv Jul 22 '17 at 09:49
  • Maybe what you’re getting at is the question of “How to decide when to use general built-in types and when to create custom types?” Though, imo, that warrants its own question – and a bunch of article-long answers. :) – besc Jul 22 '17 at 22:01
  • True. I come to realise that I understated the importance of the lack on meaningful param names in the former question. I would like to check your question, but looks like the likely probable answer is that "It depends" and "I rushed into the solution without enough and meaningful info". Your answer, alongside with Robert and JaquesB comments, Ewan and juhist are partally right. The answer could be perfectly a sum of all them. I will think if I summarize It into a own answer or just check yours. Hope you don't mind. – Laiv Jul 22 '17 at 22:17
  • A few years ago, I read a book about object modeling, I think the title was "Head First: Object Modeling." The book was well written and worked through several design problems by creating data structures and then refining them as more requirements were added. The author(s) solved every data design problem with a dictionary instead of a strongly typed data structure. They reasoned that a strongly typed data structure is less flexible and prone to type conversion errors. I'm glad that I am not the only one who found the book's design strategies ill-advised. – user148298 Jul 22 '17 at 22:45
  • 2
    *They reasoned that a strongly typed data structure is less flexible and prone to type conversion errors.* Their reasoning is correct, but those properties are usually exactly what you want. It took me a while to realize but now I see the type system as a safety net. I *want* it to complain loudly and immediately if I do something weird with types. Things like in Javascript `1 == "1"` returning `true` instead of complaining about a type mismatch, scare me. It’s mindbogglingly error prone. If you can circumvent the type system in such a way, what’s the point in having one at all? – besc Jul 23 '17 at 08:54
  • I decided to check this answer because I think It express better my fail at stating my question and at the same time provide me with useful and meaningful insights. Thank you besc. – Laiv Jul 23 '17 at 19:31
1

The linked question is looking for a way not to explicitly pass in all the properties of an object which also doesn't use setters.

The normal way of having some other settings class or struct with the named values and passing that in doesn't help them because that class would essentially have the same problem.

So someone suggests passing in an array. But not as an optimal way of constructing, as you obviously lose the names of the parameters. Just as a practical solution to thier unsolvable dilemma.

Ewan
  • 70,664
  • 5
  • 76
  • 161
  • Don't you think that encapsulating different concepts may do the thing easier? Even if it lead you to a hierarchy of classes? Otherwise, I don't see the point in OOP. – Laiv Jul 22 '17 at 09:55
  • well at some point you have to map values to properties. I think the referred to question is basically 'how do i do this without doing that?' well, you can, but 'that' is the normal way of doing 'this' – Ewan Jul 22 '17 at 10:02
  • Ok. I buy you that :-). But how working with arrays solve the question around maintainability and scalability (OP's state that there're different messages so probably different "arrays"). – Laiv Jul 22 '17 at 10:13
  • it doesn't. its "Just as a practical solution to thier unsolvable dilemma." – Ewan Jul 22 '17 at 10:53
  • Somehow your answer alongside with JacquesB comments made me realise that I stated both my question here and my answer (there) under wrong assumptions. Sadly I can't check all the answers despite all of them are right. Thank you for the insights. – Laiv Jul 23 '17 at 19:37
1

Arrays are used to hold multiple interchangeable entities. They don’t need any ordering, but usually can be sorted.

You are storing three parameters. They have the same type, but that is purely coincidence. (Maybe not for an and b). You shouldn’t put them all into one array.

gnasher729
  • 42,090
  • 4
  • 59
  • 119