tags:

views:

204

answers:

7

When I wrap up some procedural code in a class (in my case c++, but that is probably not of interest here) I'm often confused about the best way to do it. With procedural code I mean something that you could easily put in an procedure and where you use the surrounding object mainly for clarity and ease of use (error handling, logging, transaction handling...).

For example, I want to write some code, that reads stuff from the database, does some calculations on it and makes some changes to the database. For being able to do this, it needs data from the caller.

How does this data get into the object the best way. Let's assume that it needs 7 Values and a list of integers.

My ideas are:

  • List of Parameters of the constructor
  • Set Functions
  • List of Parameters of the central function

Advantage of the first solution is that the caller has to deliver exactly what the class needs to do the job and ensures also that the data is available right after the class has been created. The object could then be stored somewhere and the central function could be triggered by the caller whenever he wants to without any further interaction with the object.

Its almost the same in the second example, but now the central function has to check if all necessary data has been delivered by the caller. And the question is if you have a single set function for every peace of data or if you have only one.

The Last solution has only the advantage, that the data has not to be stored before execution. But then it looks like a normal function call and the class approaches benefits disappear.

How do you do something like that? Are my considerations correct? I'm I missing some advantages/disadvantages?

This stuff is so simple but I couldn't find any resources on it.

Edit: I'm not talking about the database connection. I mean all the data need for the procedure to complete. For example all informations of a bookkeeping transaction.

Lets do a poll, what do you like more:

class WriteAdress {
  WriteAdress(string name, string street, string city);
  void Execute();
}

or

class WriteAdress {
  void Execute(string name, string street, string city);
}

or

class WriteAdress {
  void SetName(string Name);
  void SetStreet(string Street);
  void SetCity(string City);
  void Execute();
}

or

class WriteAdress {
  void SetData(string name, string street, string city);
  void Execute();
}
+3  A: 

Interface design is very important but in your case what you need is to learn that worst is better.

First choose the simplest solution you have, write it now. Then you'll see what are the flaws, so fix them. Repeat until it's not important to fix them.

The idea is that you'll have to get experience to understand how to get directly to the "best" or better said "less worst" solution of some type of problem (that's what we call "design pattern"). To get that experience you'll have to hit problems fast, solve them and try to deeply understand why something was wrong.

That's you'll have to do each time you try something "new". Errors are not a problem if you fix them and learn from them.

Klaim
@Klaim: +1 for your comment. However any guidelines on which option to gravitate towards at each stage? Given the design of Windows APIs, having long list of arguments do no seem to be a concern.
Chubsdad
@Klaim +1 yep nice comment. Until we face the problem, one may have difficulties seeing complexity and its possible simplifications.
Stephane Rolland
But I'm writing the class for direct use of other programmers.If I can't do it at least good with the first shoot, I will have a hard time telling them to change their code over an over again.And I don't want another layer of indirection between them and me. I consider this class that I'm talking about already to be a representation of such a layer. I could do everything I describe here with a simple function call. But after some code changes due to expansion of the requirements I'm perhaps stuck with a list of 34 parameters that nobody understands. I'm looking for a way to avoid that upfront.
I understand you concern but I'm not telling you to throw the first version in the public, just to start writing the first version, make it work, then see the problems, fix them (even by changing the design) and then repeat until it's "enough". Then you can think providing the code to your colleagues.
Klaim
In this case and considering that worst is better I would probably be fine just writing plain old function calls for my clients...
+4  A: 

Values should be data members if they need to be used by more than one member function. So a database handle is a prime example: you open the connection to the database and get the handle, then you pass it in to several functions to operate on the database, and finally close it. Depending on your circumstances you may open it directly in the constructor and close it in the destructor, or just accept it as a value in the constructor and store it for later use by the member functions.

On the other hand, values that are only used by one member function and may vary every call should remain function parameters rather than constructor parameters. If they are always the same for every invocation of the function then make them constructor parameters, or just initialize them in the constructor.

Do not do two-stage construction. Requiring that you call a bunch of setXYZ functions on a class after the constructor before you can call a member function is a bad plan. Either make the necessary values initialized in the constructor (whether directly, or from constructor parameters), or take them as function parameters. Whether or not you provide setters which can change the values after construction is a different decision, but an object should always be usable immediately after construction.

Anthony Williams
What if I need 20 different values for the execution? That is more than a single constructor can handle (parameter list gets to long).
The advice about avoiding two-stage construction is fundamental. Incomplete/incoherent objects are a complication it's best to avoid as much as possible.
David Pierre
If you need 20 values, you probably can group some of them together in intermediary objects. You create each of those completely in its constructor. Then create the final object from the intermediary objects.Often in those cases, you end up realizing that some methods belong naturally to the intermediary objects.
David Pierre
I see your point, but records in databases in my company have sometimes 100 fields and more. If the clients code wants me to change all of these fields, it would be very inefficient to have 5 objects that go to the database each to update their part of the record when it can be done in one trip.
Group the fields that go together into structures. Put a function on the database-handling class that takes the appropriate sets of structures. This may be a function that takes 5 structures with 20 fields.That said, in the first instance I would start with a mammoth parameter list. Once that works then you can extract the structures to group the parameters, and move functions onto those structures as appropriate.
Anthony Williams
A: 

You should use the constructor parameters for all values, which are necessary in any case (consider that many programming languages also support constructor overloading). This leads to the second: Setter should be used to introduce optional parameters, or to update values.

You can also join these methods: expect necessary parameters in the constructor and then call their setter-function. This way you have to do check validity checks only once (in the setters).

Central functions should use temporary parameters only (timestamps, ..)

Fabian Fritz
A: 

First off, it sounds like you are trying to do too much at once. Reading, calculating and updating are all separate operations, that themselves can probably split down further.

A technique I use when I'm thinking about the design of a method or class is to think: 'what do I want the highest-level method to ideally look like?' i.e. think about the separate components of the method and split them down. That's top-down design.

In your case, I envisaged this in my head (C#):

public static void Dostuff(...)
{
    Data d = ReadDatabase(...);
    d.DoCalculations(...);
    UpdateDatabase(d);
}

Then do the same thing for each of those methods.

When you come to passing in parameters to your method, you need to consider whether the data you're passing in is stored or not - i.e. if your class is static (it cannot be instantiated, and is instead just a collection of methods etc) or if you make objects of the class. In other words: each object of the class has a state.

If the parameters can indeed be considered to be attributes of the class, they define its state, and should be stored as private variables with getters and setters for each, where neccessary. If the class instead has no state, it should be static and the parameters passed directly to the method.

Either way, it is common, and not considered bad practice, to have both a constructor and a few get / set functions where neccessary. It is also common to have to check the state of the object at the beginning of a method, so I wouldnt worry about that.

As you can see, it largely depends on what else you are doing in this class.

rmx
A: 

The reason you can't find many resources on this is that the 'right' answer is hugely domain-specific; it depends heavily on the specific project. The best way to find out is usually by experiment.

(For example: You're right about the advantages of the first two methods. An obvious disadvantage is the use of memory to store the data the whole time the object exists. This disadvantage doesn't matter in the least if your project needs two of these data objects; it's potentially a huge problem if you need a very large number. If it's a big live dataset, you're probably better querying for data as you need it, as implied by your third solution... but not definitely, as there are times when it's better to cache the data.)

When in doubt, do a quick test implementation with a simplest-possible interface; just writing it will frequently make it clearer what the pros and cons are for your project.

Tynam
A: 

Specifically addressing your example it seems as though you are still thinking too procedurally.

You should make an object that initialises the connection to the database doing all relevant error checking. Then have a method on the object that writes the values in whatever convenient way you prefer. When the object is destroyed it should release the handle to the database. That would be the object oriented way to approach the problem.

radman
A: 

I assume the only responsibility of your WriteAddress class is to write an address to a database or an output stream. If so, then you should not worry about getters and setters for the address details; instead, define an interface AddressDataProvider that is to be implemented by all classes with which your WriteAddress class will collaborate.

One of the methods on that interface would be GetAddressParts(), which would return an array of strings as required by WriteAddress. Any class that implements that method will need to respect this array structure.

Then, in WriteAddress, define a setter SetDataProvider(AddressDataProvider). This method will be called by the code that instantiates your WriteAddress object(s).

Finally, in your Execute() method, obtain the data that are required by calling GetAddressParts() on the "data provider" that you set and write out your address.

Notice that this design shields WriteAddress from subsidiary activities that are not strictly part of its responsibilities. So, WriteAddress does not care how the address details are retrieved; it does not even care about knowing and holding the address details. It just knows from where to get them and how to write them out.

This is obvious even in the description of this design: only two names WriteAddress and AddressDataProvider come up; there is no mention of database or how to pass the address details. This is usually an indication of high cohesion and low coupling.

I hope this helps.

jeyoung
With subsidiary activities you mean the need to hold the values? I see your point but I don't think that it is worth the effort. My fellow co-workers do not really like stuff like this. Here is what they think, when I come up with your solution:Oh, look at this thing Mr. EverythingMustAtLeastBeTwiceAsComplecatedAsEverybodyElseInTheWorldWouldDoIt has come up with. I just want him to write a damn address! I want to give him the data and let him do it. What the hell is a AdressDataProvider? Can you see my Point?
Not to be rude, but I don't think you have any business even attempting OO if you hold that kind of hostility towards its principles and good practice.
jeyoung
I understand technically what you are suggesting and I also see the benefits (at least I hope) but it is so dramatically different from everything we are used to do where I work that I just can't believe that there is a place on this planet where you code it your way without the need to explain the way your stuff is going to be used at least 15 times per person that is supposed to be using it. And to be blamed when they do some really strange stuff with it and start shooting little holes in their own feet. Hope you see my point?
I trust this design because I see it work. In fact, the low coupling that is achieved saves from the pain of making a lot of changes to the implementation. The bottom line is, you should use a design with which you are comfortable (even if to make tons of changes later on) and for which you have developed a vision. To tell the truth, I have worked in places where the design seemed ridiculous yet made so much sense in its context and everyone was comfortable with it. I guess you have the same reaction because you have not yet grasped the design that I proposed.
jeyoung
I understand the advantages on an academic basis. WriteAdress is now independent of the caller but without the need to hold a copy of the data it uses. The source of the data can be anything that implements the interface. It could be an already existing object or an object specifically created for that purpose (even one that is itself responsible for the data like a transfer structure).The cost for your approach is that my co-workers just won't understand it when they have to maintain my code because I'm on holiday. This is just not the kind of c++ they are used to.
And you could do this all the time. Every function call can be translated accordingly by defining an provider interface and handing that to the function instead of the parameter list. That is, I guess, the part that I'm confused about...