7

Let's say I have a class that just stores data:

ClassData
    Field1
    Field2
    Field3

Now let's say I have a function that uses Field1 and Field2:

public void DoStuff(string field1, string field2){
    if(field1 == "something"){ 
        //Do something
    }
    if(field2 == "somethingElse"){
        //Do something else
    }
}

vs

public void DoStuff(ClassData data){
    if(data.field1 == "something"){ 
        //Do something
    }
    if(data.field2 == "somethingElse"){
        //Do something else
    }
}

It seems like this is a type of "more art than science" type thing, but I have a few thoughts also. Passing specific fields shows you the required information right from the get-go, which can make it easier to read and understand the function. Sending just the data object does have advantages because it makes the code look cleaner. I wouldn't be surprised if I'm missing pros and cons for both and would be interested in hearing what you think. Thanks!

1
  • Hi :) Do you know if there are similar questions around here? Indeed, I was rather expecting opinions on when (not) to choose one pattern rather than the other, regardless this specific example. Thanks!
    – leaf
    Mar 5, 2014 at 8:51

5 Answers 5

6

I cannot say without more information.

As written above, the DoStuff method operates only on the fields of the ClassData object, therefore the DoStuff method should belong to the ClassData class directly.

5

The term "data object" has no meaning. It describes the structure of an object, not its purpose, the latter of which is the normal (and generally correct) way to classify an object in OOP.

There is a name for an object which encapsulates the arguments for a particular operation. It is called a command (or, less frequently, a command object).

Use it when you intend to implement the command pattern. Specifically, when you need to "set up" an operation and store it or pass it on to another object instead of executing it immediately.

If you are not implementing the command pattern, then a faux-"command" class which serves no purpose other than to hold arguments is a code smell. It indicates that your method has too many arguments, which further indicates that it has too many responsibilities, which goes against the guidance given by Single Responsibility Principle.

Objects are not meant to encapsulate data, they are meant to encapsulate behaviour. There are a few notable exceptions to the rule, such as Commands, Events, and DTOs, but outside those very specific cases, you should be suspicious of any class without behaviour. Only create one if you have a clear reason to do so. If you find yourself doing this a lot just to save keystrokes, you probably need to reevaluate your design.

0
1

Do field1 and field2 logically belong together, or represent something meaningful when taken as a set of data?

If so, perhaps DoStuff should be accepting some kind of object other than ClassData which perhaps ClassData can provide or implement as an interface.

For example, if field1 and field2 are actually card name and card number, and ClassData is an object representing an order, maybe DoStuff could accept a CreditCard. And maybe ClassData could implement a Card interface which provides access to those fields.

More here: http://martinfowler.com/bliki/RoleInterface.html

Alternatively, perhaps DoStuff should be a method on an object that contains field1 and field2, which is then a field in ClassData.

0

Are you likely to have received the data in fields or as an object?

If you only allow the single object to be passed in, be prepared to create a new instance if all you have are the fields. The same is true in the reverse. That is the judgement call you have to make.

2
  • The data is put directly into one of these objects from the UI, so it's natural home is in the data object. When you say: "Be prepared to create a new instance", what do you mean by that? I'm having a hard time understanding exactly what you mean, thanks.\
    – sooprise
    Jul 6, 2011 at 14:33
  • If you receive the data as just the fields, then in order to call you method you would have to create an object, set all the fields, and then pass in the object. This is opposed to just passing in the fields directly. What I am trying to say is that you should design the API for how you are going to use it. Don't create extra work for yourself.
    – Jeremy
    Jul 6, 2011 at 14:35
0

Steve McConnell answers this in his definite book Code Complete. The answer (I believe it's in chapter six) is that you should always pass in the necessary fields, and not the object that contains them.

His example is calculation of leave allowance for an employee. Assume that you can always calculate the leave allowance based on their job class, seniority, and years of service. You can either do:

calculateLeave(Employee e)

or

calculateLeave(JobClass j, Seniority s, int yearsOfService);

The first looks simpler. But what if you want to calculate the leave allowance of a theoretical employee? In the first case you would have to construct an Employee object for a non-existant person just in order to do the calculation. And you'd have no way of knowing exactly which fields the method was looking at.

3
  • I run into this a lot, looking at some opaque method and trying to figure out exactly which fields need to be populated - it's a common problem in persistence code and service-oriented code. It also illustrates why OOP tries to avoid data/behaviour separation; if the Employee class exposed this calculation as a method then you would be able to assume that it uses default values for anything you didn't initialize, and therefore not have to fuss about how to construct it. If you must isolate the behaviour, then the next best thing is to make it very clear precisely which data it depends on.
    – Aaronaught
    Jul 6, 2011 at 20:22
  • If calculateLeave is a method of Employee, you still have to create a 'fake' employee to run the method. Jul 6, 2011 at 21:49
  • Yes, but in that specific case, it's preferable as long as the class does not have sequential coupling. You would expect it to be OK to only initialize the properties you care about and then execute the method, as the class should understand its own defaults and invariants. Now maybe this is a very complicated object to construct, but in that case composition might be a better approach (e.g. give the JobClass a CalculateLeave(Seniority s, int yearsOfService) method, which puts the behavior where you would expect it to be).
    – Aaronaught
    Jul 6, 2011 at 22:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.