Sign in to follow this  
twoaterisn

inheritance overkill?

Recommended Posts

Hello, I am trying to determine wether or not i should use inheritence. I am coding a molecule class (C++) which owns a few instances of an atom class. Atoms come in many types (114 to be precise: carbon, hydrogen, nitrogen, etc.) and each has its own properties (weight, valence,...) . They behave quite similar however and if i would derive each of them from a base atom class, i would (for the time being) need only one virtual method to check for errors in user input. So which is better:
class atom{
virtual bool checkcorrectness();
};

class carbon::public atom{
virtual bool checkcorrectness();
}

class nitrogen::public atom{
virtual bool checkcorrectness();
}

or

enum atomtype{carbon, nitrogen};

class atom{
atomtype itstype;
bool checkcorrectness(){if itstype=carbon "do this" else "do something else"};
};

? for now, the second one seems more convenient, but it could be that later on, more member functions will behave differently depending on the atom's type. 114 derived classes just seems to much to me...

Share this post


Link to post
Share on other sites
Quote:
Original post by twoaterisn

Atoms come in many types (114 to be precise: carbon, hydrogen, nitrogen, etc.) and each has its own properties (weight, valence,...).


This is a definite case for *not* using inheritance. An atom is an atom. Just because Hydrogen atom has different weight from carbon it doesn't make it something else.

I blame this on nonsensical OO tutorials, which use Pizza, PizzaWithCheese, PizzaWithHam, PizzaWith... examples to illustrate inheritance. A pizza is a pizza. There is only one. Just because it has different toppings it doesn't make it something else.

Quote:
for now, the second one seems more convenient, but it could be that later on, more member functions will behave differently depending on the atom's type. 114 derived classes just seems to much to me...


There's a problem with your abstraction. There is nothing in periodic table of elements that would require one element to be treated differently from another. All atoms are equivalent.

They have different composition, but they are still the same.

And if you use virtual function, there already is an enum. It's called vtable, which determines which function to call. Using enum manually in combination with switch or if statement does precisely and exactly what polymorphism does, and is how polymorphic functions are sometimes implemented in non-OO languages.

Quote:
i would (for the time being) need only one virtual method to check for errors in user input.


You only need one function total. What exactly is this method supposed to do?

Share this post


Link to post
Share on other sites
Isn't there a general way to describe the valid data input for all 114 types with a few parameters?
If they are really so similar, that is probably possible. Place these in a file or look up table and you can skip your whole inheritance/if statement overkill.

Dietger

Share this post


Link to post
Share on other sites
Hm, what exactly is different about the behavior of the different elements (not the data, like mass, but the behavior)? If there isn't a difference, subclassing probably isn't necessary at all. If there is, is it solely determined by the data? If so, why can't external objects just read the data and determine the proper course of action?

One example of this situation I've seen on these forums before is an RPG with character classes. Most people instantly code a base class from which they derive different game classes (bard, warrior, mage, etc.). However, this is often viewed as an abuse of inheritance, since the data (i.e., stats) may be different, but the behavior is essentially the same.

Also, I'm assuming that the data for each atom is static and does not change at runtime (this makes sense, unless you're planning on simulating electrons, nuclear reactions, ions and electricity, or some other phenomenon that can alter the state of the atom). In this case, you may want to look into template meta-programming. Using class/struct templates could speed up your code and allow you to do some neat tricks and optimizations. This would probably require a large enumeration though, and could easily lead to code bloat.

enum elements {
hydrogen,
helium
};
template<elements E>
struct atom { BOOST_STATIC_ASSERT(0 == sizeof(E)); };
template<>
struct atom<hydrogen> {
enum { some_int_data = 0 };
static const float some_float_data = 0.0f;
};
template<>
struct atom<helium> {
enum { some_int_data = 0 };
static const float some_float_data = 0.0f;
};
template<element E>
void foo(const atom<E>& bar) {
std::cout << bar.some_int_data << std::endl;
}



Just an idea. Anyway, I hope something here helps. :-) Good luck!

[Edited by - GenuineXP on January 16, 2009 10:42:09 AM]

Share this post


Link to post
Share on other sites
Just have an element class with name, symbol, protons, neutrons, electrons, etc.

Then if you want to get fancy make a set of enums for the common elements and a factory (or a simple constructed std::map) to give you the constructed element object for an enum.

One thing to remember/consider is that there's like 4 different isotopes of carbon, so 'just' carbon is perhaps inadequate. That's also a pretty good motivator to allow construction of elements outside of the factory/map.


Realistically, methods in element shouldn't change based on the 'type' of the atom. What methods would you have? I can't think of anything that couldn't be better done just with data on the type.

Share this post


Link to post
Share on other sites
By external objects reading the data, do you mean that i should move the checkcorrectness() outside the atom class? wouldn't this be C-style programming or is it just that i don't get what you're saying? :)

like this:

class atom{};
bool correct (atom* givenatom);

PS I'm a chemist by education so forgive my ignorance on C++ and my tendency to treat hydrogens differently from carbons, lol.

Share this post


Link to post
Share on other sites
Although not knowing the exact use case, I would throw in the following variant (please ignore any chemical nonsense, because school is out for some years now ;) ):

A class Element is responsible to describe the atoms of a specific element. The corresponding data are the number of electrons/protons, the standard number of neutrons and perhaps a range to describe known isotopes, the name (for sure), the abbreviated name, ... whatever is constant for all atoms of that element. Then for each element of interest an instance is instanciated using the correct data, of cause. The instances may be stored in maps using the name/abbreviated name or whatever as key, and/or in an array using the

Then a class Atom can be used to define the actual usage of an Element, e.g. as part of a Molecule instance. So Atom becomes a lightweight, because the majority of data is out-sourced. What remains is a pointer to the correct Element instance as well as any instance data, e.g. perhaps the actual amount of neutrons to better deal with isotopes.

Share this post


Link to post
Share on other sites
Also a molecule consists of several atoms so you should better let it store a vector of atoms.
Then you can also easily perform reactions by passing atoms from one molekul to another etc

Share this post


Link to post
Share on other sites
Quote:
Original post by twoaterisn
By external objects reading the data, do you mean that i should move the checkcorrectness() outside the atom class?


What does checkcorrectness() need to do? Can you show how you would implementat it for H, C and Si?

Share this post


Link to post
Share on other sites
Quote:
Original post by twoaterisn
By external objects reading the data, do you mean that i should move the checkcorrectness() outside the atom class? wouldn't this be C-style programming or is it just that i don't get what you're saying? :)

Possibly, yes, much like the function foo in my template example accesses the data of bar. In this way, atoms just contain data. Again, depending on exactly what you wish to model, that data could easily be static. If this is the case, it makes more sense for operations to be external (of atoms).

As mentioned above, using a table may be the simplest and best suited approach (forget my template ramblings). You could probably implement something like the following.

struct atom {
// Name, valance, mass, etc. Just data.
};
typedef std::vector<atom> atom_con_t;
typedef atom_con_t::iterator atom_iter_t;
typedef atom_con_t::const_iterator const_atom_iter_t;
class molecule {
public:
molecule(const atom_con_t& table); // References atoms in this table.
private:
typedef std::vector<const_atom_iter_t> atom_ref_con_t;
atom_ref_con_t atoms_;
};
void create_atomic_table(atomic_table_con_t& table, std::istream& in);
int main() {
atomic_table_con_t table;
std::ifstream fin("table.dat");
create_atomic_table(table, fin);
// Use the table.
}




Something like this may suffice; it really depends on what you're shooting for. In this example, atoms are just structs that contain atom data (such as valence and atomic mass). A table of atoms (elements) is just a vector of atoms, which could be read from some input stream (like a file). Once this table is created, it can be passed to molecules what store references into the table. I've used iterators here. The typedefs just help keep things clean and easier to remember.

What exactly do you plan to model? Knowing this would probably make it easier for others to help you. For example, if you need to model the actual bonds between atoms within a molecule, what I've done here may be too simplistic.

EDIT: You could also use a map in this approach, where the key is whatever you would most frequently use to identify a particular atom (probably name or valence).

[Edited by - GenuineXP on January 16, 2009 11:28:35 AM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this