0

I have a large, self-contained environmental model. In the model I have a large class called cell, that contains many (~100) vectors and doubles that all together dictate the current state of the cell across many attributes. These attributes are changed by different environmental and biological processes (other classes), which contain process specific functions/methods. I am tempted to make these all of these process classes (20+) friend classes of the cell class, so that I don't have to write nearly 200 get/set functions.

It would also allow my code to be more simplistic and easier to read:

cell->variableA = (cell->variableA * someInput)/(somOtherInput)

vs.

cell->setVariableA(cell->getVariableA * someInput)/(someOtherInput)

Are there solid reasons against doing this? Should I also just consider making the data members all public?

The model is a self contained application - I don't share the code with others to use/couple anything to.

traggatmot
  • 1,423
  • 5
  • 26
  • 51
  • 3
    If you have 20+ friends, how is that different from making the data members public? Otherwise, as a rule of thumb, when you have code like `cell->setVariableA(cell->getVariableA() ...` you haven't found the proper abstraction yet. You should aim at having something like `cell->do_something()` or `cell->connect_to(neighbor)`. – Bo Persson Sep 02 '15 at 22:43
  • 1
    What sort of invariants is your `cell` class maintaining as part of its definition of being a `cell`? If none, then just make it a struct and let your `process`es go to town. Are these processes independent of the concept of a `cell` or part of it? Do they belong as separate entities from `cell`? – John Sep 02 '15 at 22:47
  • @BoPersson, cell->do_something would imply that I move the methods that operate on a cell inside of the cell class? Am I interpreting that correctly? At that point the only thing outside of the class would be external forcings (inputs) to the functions that change the state variables. The class would grow significantly. – traggatmot Sep 02 '15 at 22:51
  • 1
    I don't know your use case, but changing the state of the cell ought to be managed by the cell itself, as a reaction to some input from the environment. If I think about a *real* cell, its "state" isn't determined by another cell doing `cell->setSodiumLevel(42)`, but by the other cell triggering some event. A program could simulate this by doing `cell->TriggerEventX()`. – Bo Persson Sep 02 '15 at 23:02

2 Answers2

0

I am not sure if your class concept is the way you should follow. You should make your data much more readable and compact. Anyway, to answer your question: Use private members and getter/setter functions when you want to change the implementation in the future. They also help you debugging with breakpoints and/or validating values. Prefer encapsulation and abstraction when you want to hide an implementation and combine functionality. You should definitely make your classes more abstract. E.g. my_vector.copyAbcDataFrom(other_vector) is much nicer than my_vector.member = other_vector.member.

If you don't care about these arguments you can use public members.

Btw, you could also use macros to auto-create getter/setter functions for your class.

#define MEMBER(datatype, name) \
    private: \
        datatype name; \
    public: \
        datatype get ## name() const { return name; }

class Foo
{
  MEMBER(int, abc)
  MEMBER(char, blub)
};

int main()
{
  Foo f;
  int val = f.getabc();
  return 0;
}
HelloWorld
  • 2,392
  • 3
  • 31
  • 68
  • How do you use those macros in C++? – traggatmot Sep 02 '15 at 22:48
  • I added an example for a getter function. Keep in mind that you should consider using references or pointers when dealing with bigger datastructures like vectors. Decide on your own. Keep in mind that these macros will make you loose the ability of debugging your code properly as breakpoints don't work in them. – HelloWorld Sep 02 '15 at 22:50
0

You are doing it wrong. The purpose of classes is to isolate logic so you can grok all of it. It's a step up from just having functions which is a step up from globals.

If you have 200 different parameters, you should not be sticking them in 200 different variables. Associative containers exist for a reason. Enums exist for a reason.

class Cell {
    public: 
        enum class ParameterType { Param1, Param2, };

        double getParameter(ParameterType type) { return variables.at(type); }
        void setParameter(ParameterType type, double value) { variables[type] = value; }

    private:
        std::map<ParameterType, double> variables;
};

and where are the 100s of get/set? Make a compact design, and things are readable.

The reason you do not want to make friends is for maintainability reasons. It is the same reasons why you don't use globals. If you change how things work internally, you don't want to break things outside your class.

Hint: If your class declaration is longer than a about a few pages, you need to split it up.

user3427419
  • 1,769
  • 11
  • 15
  • @negative voter: can you please give a comment what is wrong with this answer, or how it can be improved? For me the answer is pretty valid and a good hint to the OP how to restructure his class. Upped by me now. – HelloWorld Sep 02 '15 at 23:22