473,387 Members | 1,641 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Is this class design correct? A better way?

nw
Hi,

I was wondering if someone would be able to give me some comments on
the following class structure, it feels to me as if there must be a
better way, but I'm unsure what it is, perhaps I should be using
multiple inheritance?

Basically I have a pure virtual class called Body, this contains a
number of integration algorithms which are applied to the Body.
Generally only one integration algorithm will be used with a
particular Body. The integration algorithm is called by the
integrate() method, which selects the integration algorithm depending
on how you have set the variable integration_type.

I'm not sure if it's relevant to this discussion but Body is the base
class from which two others are derived, these implement the
calculate_force() method, this method updates a variable used by the
integrate() method.

Compilable example code implementing this design follows. Apologies if
I've been overly verbose.

Any help greatly appreciated!

#include <iostream>

using namespace std;

class Body {

public:
double x, y, z;
double a;

static const int BODY_INTEGRATE_EULER=1;
static const int BODY_INTEGRATE_VERLET=2;
static const int BODY_INTEGRATE_LEAPFROG=3;

int integration_type;

virtual bool calculate_force(Body &b) = 0;

Body() {
integration_type=BODY_INTEGRATE_EULER;
}

bool integrate() {
if(integration_type == BODY_INTEGRATE_EULER) {
// .. do euler
cout << "Euler integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_VERLET) {
// .. do verlet
cout << "Verlet integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_LEAPFROG) {
// .. do leapfrog
cout << "Leapfrog integration" << endl;
return true;
}
}
};

class Planet : public Body {

virtual bool calculate_force(Body &b) {
// do force calculation for planet... updates a
}
};

class SimpleHarmonic : public Body {

virtual bool calculate_force(Body &b) {
// do force calculation for simple harmonic motion... updates a
}
};

int main(void) {
Planet p;
Planet p2;
SimpleHarmonic s;

p.integration_type = Body::BODY_INTEGRATE_LEAPFROG;
p2.integration_type = Body::BODY_INTEGRATE_VERLET;
s.integration_type = Body::BODY_INTEGRATE_VERLET;

cout << "planet,leapfrog: ";
p.integrate();

cout << "planet,verlet: ";
p2.integrate();

cout << "simpleharmonic,verlet: ";
s.integrate();

return 0;
}

Feb 16 '07 #1
4 1750
* nw:
>
I was wondering if someone would be able to give me some comments on
the following class structure, it feels to me as if there must be a
better way, but I'm unsure what it is, perhaps I should be using
multiple inheritance?
You should be using /inheritance/ and-or /templating/ instead of a type
switch.

For inheritance, you can represent the integration algorithm directly as
a derived class override of a Body virtual member function, or you can
represent it as an object passed to the Body constructor(s).
[snip]
Compilable example code implementing this design follows. Apologies if
I've been overly verbose.

Any help greatly appreciated!
OK...

#include <iostream>
Formally you also need <ostream>.

using namespace std;

class Body {

public:
double x, y, z;
double a;
Public member variables = ungood.

Name 'a' = ungood.

static const int BODY_INTEGRATE_EULER=1;
static const int BODY_INTEGRATE_VERLET=2;
static const int BODY_INTEGRATE_LEAPFROG=3;
All uppercase for non-macros = ungood.

int integration_type;
'int' to represent something with limited number of possible values =
ungood.

Representing type as value = in general, and in this case, ungood.

virtual bool calculate_force(Body &b) = 0;

Body() {
integration_type=BODY_INTEGRATE_EULER;
}
See the FAQ item titled 'Should my constructors use "initialization
lists" or "assignment"?', currently available at <url:
http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6>.

bool integrate() {
if(integration_type == BODY_INTEGRATE_EULER) {
// .. do euler
cout << "Euler integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_VERLET) {
// .. do verlet
cout << "Verlet integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_LEAPFROG) {
// .. do leapfrog
cout << "Leapfrog integration" << endl;
return true;
}
}
This type selection is what you need to avoid.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Feb 16 '07 #2

Hi, I hope this helps in some way. Note: I have no clue what your
problem domain is but I have some indication that the following
sample code will help you in some way. BTW, I did NOT try to compile
the code. I merely wrote it to give you some idea.

Good Luck!
PS. I like to use boost. If you don't, RcPtr can be replaced with
a raw pointer. (www.boost.org)
-------------------------------------------------------------------

#include <boost/shared_ptr.hpp>

class IntegratorImpl
{
public:
typedef boost::shared_ptr<IntegratorImplRcPtr;

public:
IntegratorImpl( double a ) : m_a( a ) {}

virtual bool integrate() = 0;

protected:
// I think that a should be here not in Body
// but I don't know what a is.
double m_a;
};

class EulerIntegrator : public IntegratorImpl
{
public:
EulerIntergrator( double a ) : IntegratorImpl( a ) {}

virtual bool integrate()
{
// do Euler integration
}
private:
// put Euler integration specific stuff here
};

// add Vertlet and LeapFrog integrators as separate classes like
// EulerIntegrator

class Integrator
{
public:
enum IntegrateType
{
kNone, kEuler, kVertlet, kLeapFrog
};
public:
Integrator();
~Integrator();

void selectIntegrator( Integrator type, double a )
{
switch( type )
{
case kEuler:
m_impl.reset( new EulerIntegrator( a ));
break;
case kVertlet:
m_impl.reset( new VertletIntegrator( a ));
// you get the idea
default:
// throw? your choice
}
}

bool integrate()
{
if( m_impl )
{
return false;
}
else
{
return m_impl->integrate();
}
}

private:
// just a raw pointer if you do not like boost
IntegratorImpl::RcPtr m_impl;
};

class Body
{
public:
Body();

bool integrate() { return m_integrator.integrate(); }

virtual void selectIntegrator( Integrator::IntegratorType type )
{
// assuming 0 is a good default for a here
m_integrator.selectIntegrator( type, 0 );
}

private:
Integrator m_integrator;
// rest of your data members here
};

class Planet : public Body
{
public:
Planet( const Body &body ) : m_body( body ) {}

double calcForce()
{
// use m_body to calculate a here
double a = 1.0;
return a;
}

virtual void selectIntegrator( Integrator::IntegratorType type )
{
m_integrator.selectIntegrator( type, calcForce() );
}

private:
const Body &m_body;
};

// something similar for SimpleHarmonic

int
main(void)
{
Planet p;
Planet p2;
SimpleHarmonic s;

p.selectIntegrator( Integrator::kLeapFrog );
p2.selectIntegrator( Integrator::kVertlet );
s.selectIntegrator( Integrator::kVertlet );

cout << "planet,leapfrog: ";
p.integrate();

cout << "planet,verlet: ";
p2.integrate();

cout << "simpleharmonic,verlet: ";
s.integrate();

return 0;
}
nw wrote:
Hi,

I was wondering if someone would be able to give me some comments on
the following class structure, it feels to me as if there must be a
better way, but I'm unsure what it is, perhaps I should be using
multiple inheritance?

Basically I have a pure virtual class called Body, this contains a
number of integration algorithms which are applied to the Body.
Generally only one integration algorithm will be used with a
particular Body. The integration algorithm is called by the
integrate() method, which selects the integration algorithm depending
on how you have set the variable integration_type.

I'm not sure if it's relevant to this discussion but Body is the base
class from which two others are derived, these implement the
calculate_force() method, this method updates a variable used by the
integrate() method.

Compilable example code implementing this design follows. Apologies if
I've been overly verbose.

Any help greatly appreciated!

#include <iostream>

using namespace std;

class Body {

public:
double x, y, z;
double a;

static const int BODY_INTEGRATE_EULER=1;
static const int BODY_INTEGRATE_VERLET=2;
static const int BODY_INTEGRATE_LEAPFROG=3;

int integration_type;

virtual bool calculate_force(Body &b) = 0;

Body() {
integration_type=BODY_INTEGRATE_EULER;
}

bool integrate() {
if(integration_type == BODY_INTEGRATE_EULER) {
// .. do euler
cout << "Euler integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_VERLET) {
// .. do verlet
cout << "Verlet integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_LEAPFROG) {
// .. do leapfrog
cout << "Leapfrog integration" << endl;
return true;
}
}
};

class Planet : public Body {

virtual bool calculate_force(Body &b) {
// do force calculation for planet... updates a
}
};

class SimpleHarmonic : public Body {

virtual bool calculate_force(Body &b) {
// do force calculation for simple harmonic motion... updates a
}
};

int main(void) {
Planet p;
Planet p2;
SimpleHarmonic s;

p.integration_type = Body::BODY_INTEGRATE_LEAPFROG;
p2.integration_type = Body::BODY_INTEGRATE_VERLET;
s.integration_type = Body::BODY_INTEGRATE_VERLET;

cout << "planet,leapfrog: ";
p.integrate();

cout << "planet,verlet: ";
p2.integrate();

cout << "simpleharmonic,verlet: ";
s.integrate();

return 0;
}
Feb 17 '07 #3
nw
On Feb 17, 12:17 am, Piyo <cybermax...@yahoo.comwrote:
Hi, I hope this helps in some way. Note: I have no clue what your
problem domain is but I have some indication that the following
sample code will help you in some way. BTW, I did NOT try to compile
the code. I merely wrote it to give you some idea.

Good Luck!
PS. I like to use boost. If you don't, RcPtr can be replaced with
a raw pointer. (www.boost.org)
-------------------------------------------------------------------

#include <boost/shared_ptr.hpp>

class IntegratorImpl
{
public:
typedef boost::shared_ptr<IntegratorImplRcPtr;

public:
IntegratorImpl( double a ) : m_a( a ) {}

virtual bool integrate() = 0;

protected:
// I think that a should be here not in Body
// but I don't know what a is.
double m_a;

};

class EulerIntegrator : public IntegratorImpl
{
public:
EulerIntergrator( double a ) : IntegratorImpl( a ) {}

virtual bool integrate()
{
// do Euler integration
}
private:
// put Euler integration specific stuff here

};

// add Vertlet and LeapFrog integrators as separate classes like
// EulerIntegrator

class Integrator
{
public:
enum IntegrateType
{
kNone, kEuler, kVertlet, kLeapFrog
};
public:
Integrator();
~Integrator();

void selectIntegrator( Integrator type, double a )
{
switch( type )
{
case kEuler:
m_impl.reset( new EulerIntegrator( a ));
break;
case kVertlet:
m_impl.reset( new VertletIntegrator( a ));
// you get the idea
default:
// throw? your choice
}
}

bool integrate()
{
if( m_impl )
{
return false;
}
else
{
return m_impl->integrate();
}
}

private:
// just a raw pointer if you do not like boost
IntegratorImpl::RcPtr m_impl;

};

class Body
{
public:
Body();

bool integrate() { return m_integrator.integrate(); }

virtual void selectIntegrator( Integrator::IntegratorType type )
{
// assuming 0 is a good default for a here
m_integrator.selectIntegrator( type, 0 );
}

private:
Integrator m_integrator;
// rest of your data members here

};

class Planet : public Body
{
public:
Planet( const Body &body ) : m_body( body ) {}

double calcForce()
{
// use m_body to calculate a here
double a = 1.0;
return a;
}

virtual void selectIntegrator( Integrator::IntegratorType type )
{
m_integrator.selectIntegrator( type, calcForce() );
}

private:
const Body &m_body;

};

// something similar for SimpleHarmonic

int
main(void)
{
Planet p;
Planet p2;
SimpleHarmonic s;

p.selectIntegrator( Integrator::kLeapFrog );
p2.selectIntegrator( Integrator::kVertlet );
s.selectIntegrator( Integrator::kVertlet );

cout << "planet,leapfrog: ";
p.integrate();

cout << "planet,verlet: ";
p2.integrate();

cout << "simpleharmonic,verlet: ";
s.integrate();

return 0;

}
nw wrote:
Hi,
I was wondering if someone would be able to give me some comments on
the following class structure, it feels to me as if there must be a
better way, but I'm unsure what it is, perhaps I should be using
multiple inheritance?
Basically I have a pure virtual class called Body, this contains a
number of integration algorithms which are applied to the Body.
Generally only one integration algorithm will be used with a
particular Body. The integration algorithm is called by the
integrate() method, which selects the integration algorithm depending
on how you have set the variable integration_type.
I'm not sure if it's relevant to this discussion but Body is the base
class from which two others are derived, these implement the
calculate_force() method, this method updates a variable used by the
integrate() method.
Compilable example code implementing this design follows. Apologies if
I've been overly verbose.
Any help greatly appreciated!
#include <iostream>
using namespace std;
class Body {
public:
double x, y, z;
double a;
static const int BODY_INTEGRATE_EULER=1;
static const int BODY_INTEGRATE_VERLET=2;
static const int BODY_INTEGRATE_LEAPFROG=3;
int integration_type;
virtual bool calculate_force(Body &b) = 0;
Body() {
integration_type=BODY_INTEGRATE_EULER;
}
bool integrate() {
if(integration_type == BODY_INTEGRATE_EULER) {
// .. do euler
cout << "Euler integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_VERLET) {
// .. do verlet
cout << "Verlet integration" << endl;
return true;
} else
if(integration_type == BODY_INTEGRATE_LEAPFROG) {
// .. do leapfrog
cout << "Leapfrog integration" << endl;
return true;
}
}
};
class Planet : public Body {
virtual bool calculate_force(Body &b) {
// do force calculation for planet... updates a
}
};
class SimpleHarmonic : public Body {
virtual bool calculate_force(Body &b) {
// do force calculation for simple harmonic motion... updates a
}
};
int main(void) {
Planet p;
Planet p2;
SimpleHarmonic s;
p.integration_type = Body::BODY_INTEGRATE_LEAPFROG;
p2.integration_type = Body::BODY_INTEGRATE_VERLET;
s.integration_type = Body::BODY_INTEGRATE_VERLET;
cout << "planet,leapfrog: ";
p.integrate();
cout << "planet,verlet: ";
p2.integrate();
cout << "simpleharmonic,verlet: ";
s.integrate();
return 0;
}
Thank you for both your and Alf P. Steinbach's suggestions.

In my example, a is acceleration. It is a quantity calculated from
other member variables of Body (not shown) and used by unshown members
of Body. I suppose I could pass the integrator object a reference to
Body through which it could access these values, but then I guess I
have lots of integrator objects which only contain a reference to
Body? I think this would basically mean changing the Integrators to
something like:

class EulerIntegrator : public IntegratorImpl
{
EulerIntegrator(Body &b) : IntegratorImpl(b) {}

bool integrate()
{
Vec velocity = m_body.get_velocity();
Vec acceleration = m_body.get_acceleration();
Ensemble &m_ensemble = m_body.get_ensemble();

velocity += acceleration * m_ensemble.get_dt();
position += velocity * m_ensemble.get_dt();
}
}

It seems from this I would have a lot of largely empty integrator
objects, one for each body. Is it possible to avoid this? Are there
other alternatives I should consider?

Thanks again for your help.

Feb 21 '07 #4
In article <11*********************@k78g2000cwa.googlegroups. com>,
ne*@soton.ac.uk says...

[ ... ]
In my example, a is acceleration. It is a quantity calculated from
other member variables of Body (not shown) and used by unshown members
of Body. I suppose I could pass the integrator object a reference to
Body through which it could access these values, but then I guess I
have lots of integrator objects which only contain a reference to
Body? I think this would basically mean changing the Integrators to
something like:

class EulerIntegrator : public IntegratorImpl
{
EulerIntegrator(Body &b) : IntegratorImpl(b) {}

bool integrate()
{
Vec velocity = m_body.get_velocity();
Vec acceleration = m_body.get_acceleration();
Ensemble &m_ensemble = m_body.get_ensemble();

velocity += acceleration * m_ensemble.get_dt();
position += velocity * m_ensemble.get_dt();
}
}
At least to me, the integrators look like good candidates to become
functors to be applied to Bodies:

struct Euler_Integrator {
void operator()(Body &b) {
double dt = b.get_ensemble().get_dt();
Vec velocity = b.get_velocity() +
b.get_acceleration() * dt();
TYPE position = velocity * dt();
// probably need to do something with
// velocity and/or position here...
}
};

// likewise for Verlet and Leapfrog integrators.

int main() {
Planet p;
Planet p2;

Euler_Integrator e;
Verlet_Integrator v;

e(p);
v(p2);

return 0;
}
It seems from this I would have a lot of largely empty integrator
objects, one for each body. Is it possible to avoid this? Are there
other alternatives I should consider?
I'm not sure what makes you think there would be largely empty
integrators for each body, so it's hard to guess how to avoid them. I
seem to have missed at least part of the thread, but I'm not entirely
clear on the significance of 'a' either -- and/or whether it's replaced
and/or affected by get_acceleration in the code above.

--
Later,
Jerry.

The universe is a figment of its own imagination.
Feb 24 '07 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

50
by: Dan Perl | last post by:
There is something with initializing mutable class attributes that I am struggling with. I'll use an example to explain: class Father: attr1=None # this is OK attr2= # this is wrong...
5
by: Martin Magnusson | last post by:
Hi! I have a class with a private member which is a pointer to an abstract class, which looks something like this: class Agent { public: void Step( Base* newB ); private:
13
by: Bryan Parkoff | last post by:
I have created three classes according to my own design. First class is called CMain. It is the Top Class. Second class and third class are called CMemory and CMPU. They are the sub-classes....
4
by: james | last post by:
I have a custom UserControl, which can have many sub class levels derived from it. I want to be able to discover all the components at Load time, but the only components I can see from the base...
17
by: RSH | last post by:
I am really trying to grasp the concept of OOP as it applies to C#. I am looking at trying to set up a simple Employee Class but I am having trouble conceptualizing what this class should look...
29
by: RageARC | last post by:
I have the following code: index.php: class main_class { public $database = new DAL; public $html = new HTML; }
20
by: tshad | last post by:
Using VS 2003, I am trying to take a class that I created to create new variable types to handle nulls and track changes to standard variable types. This is for use with database variables. This...
8
by: MMAS | last post by:
Hey everyone -- Curious about some strange behaviour I'm seeing that seems to be related to my lack of understanding on how generics work in C#. Here's some simplified code (sorry for strange...
1
by: Angus | last post by:
Hi all I have a design which models a telephone system. Roughly the design has these two items: device class which models an extension on the telephone system. The device constructor takes...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.