Design problem related to A-Bag-Of-Fruit is not the same as A-Bag-Of-Apple

T

T.A.

Class hierarchy below demonstrates my problem:

#include <vector>
#include <boost/smart_ptr.hpp>

class Fruit {
public:
virtual ~Fruit() = 0;
};

class Apple : public Fruit {
//...
};

class Banana : public Fruit {
//...
};

class BagOfFruit {
public:
virtual ~BagOfFruit() = 0;

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

class BagOfApple : public BagOfFruit {
//...
};

class BagOfBanana : public BagOfFruit {
//...
};

class Shop {
public:
virtual ~Shop() = 0 {}

void push_bag (const BagOfFruit&);

private:
typedef boost::shared_ptr<BagOfFruit> BagOfFruitPtr;
typedef std::vector<BagOfFruitPtr> BagsVector;
BagsVector itsContents;
};

class AppleShop : public Shop {
//...
};

class BananaShop : public Shop {
//...
};

class Market {
public:
void push_shop (const Shop&);

private:
typedef boost::shared_ptr<Shop> ShopPtr;
typedef std::vector<ShopPtr> ShopVector;
ShopVector itsContents;
};

The main thing here is Market class. It has to be able to deal with various
shops. Operating through base class Shop is clearest way to do it. Now,
shops deal with bags of fruit. Thus, I need common interface to various
bags of fruit (namely BagOfFruit class). And bags of fruit operate with
various Fruit subclasses.

Upper design has numerous problems. First of all, working with BagOfFruit
pointer allows Apple to be pushed into BagOfBanana which should not happen.
Same goes with Shop. BagOfApple could end up in BananaShop which should not
happen.

All above just illustrates my real problem. I have two requirements:
- common interface that allows me to manipulate with Fruit, BagOfFruit
and Shop
- restriction (already on base class level) that ie. Apple can't end up
in BagOfBanana etc.

I've had two ideas:
- Use of templates for Fruit, BagOfFruit and Shop. This solves problem
of restricting who goes where because template instantiation like
BagOfFruit<Banana> works only for Banana and so on... But this eliminates
the possibility for Market to store any kind of Shop because there is no
base Shop class, only Shop<T> template. And the main purpose of all this
was to enable Market to contain and handle any shop...

- Other idea was to use kind of RTTI in base classes. For example I
would add
enum FruitTypes {
APPLE,
BANANA
};

and then to all base classes:

virtual FruitTypes FruitType() const = 0;

With this, Fruit::eek:perator= (const Fruit& rv) (again, for demonstration
purpose let's assume that this operator is overloaded) would throw if
'this' and 'rv' don't return same FruitType(). This seems like behavior
that could confuse clients, and would also require same thing done for
other base classes, many checks in member methods and so on... Code getting
messy, and behavior confusing, this is never good. But I can't figure out
anything else to do... Any suggestions?

TIA
 
S

Salt_Peter

T.A. said:
Class hierarchy below demonstrates my problem:

#include <vector>
#include <boost/smart_ptr.hpp>

class Fruit {
public:
virtual ~Fruit() = 0;
};

class Apple : public Fruit {
//...
};

class Banana : public Fruit {
//...
};

class BagOfFruit {
public:
virtual ~BagOfFruit() = 0;

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

class BagOfApple : public BagOfFruit {
//...
};

class BagOfBanana : public BagOfFruit {
//...
};

class Shop {
public:
virtual ~Shop() = 0 {}

void push_bag (const BagOfFruit&);

private:
typedef boost::shared_ptr<BagOfFruit> BagOfFruitPtr;
typedef std::vector<BagOfFruitPtr> BagsVector;
BagsVector itsContents;
};

class AppleShop : public Shop {
//...
};

class BananaShop : public Shop {
//...
};

class Market {
public:
void push_shop (const Shop&);

private:
typedef boost::shared_ptr<Shop> ShopPtr;
typedef std::vector<ShopPtr> ShopVector;
ShopVector itsContents;
};

The main thing here is Market class. It has to be able to deal with various
shops. Operating through base class Shop is clearest way to do it. Now,
shops deal with bags of fruit. Thus, I need common interface to various
bags of fruit (namely BagOfFruit class). And bags of fruit operate with
various Fruit subclasses.

Shops deal with bags. A bag can be a bag of apples or a bag of bananas,
etc.
A Shop can sell any of those fruit bag derivatives.
So template the containers.

t#include <iostream>
#include <vector>

struct Fruit {
virtual ~Fruit() = 0;
};

Fruit::~Fruit() { }

struct Apple : public Fruit {
Apple() { std::cout << "Apple()\n"; }
Apple(const Apple& copy) { std::cout << "copy Apple\n"; }
~Apple() { std::cout << "~Apple()\n"; }
};

template < typename F >
class BagOfFruit {
std::vector< F > vf;
public:
BagOfFruit() : vf() { }
BagOfFruit(size_t sz) : vf(sz) { }
void push_back( const F& r_f)
{
vf.push_back(r_f);
}
};

int main() {
BagOfFruit< Apple > apples(10);
}

I'm sorry, i don't see any benefit of using a smart ptr when storing
fruit.
Upper design has numerous problems. First of all, working with BagOfFruit
pointer allows Apple to be pushed into BagOfBanana which should not happen.
Same goes with Shop. BagOfApple could end up in BananaShop which should not
happen.

All above just illustrates my real problem. I have two requirements:
- common interface that allows me to manipulate with Fruit, BagOfFruit
and Shop
- restriction (already on base class level) that ie. Apple can't end up
in BagOfBanana etc.

I've had two ideas:
- Use of templates for Fruit, BagOfFruit and Shop. This solves problem
of restricting who goes where because template instantiation like
BagOfFruit<Banana> works only for Banana and so on... But this eliminates
the possibility for Market to store any kind of Shop because there is no
base Shop class, only Shop<T> template. And the main purpose of all this
was to enable Market to contain and handle any shop...

Why, can't a Shop hold a vector of templated bags?
 
X

Xeranic

T.A. said:
Class hierarchy below demonstrates my problem:

#include <vector>
#include <boost/smart_ptr.hpp>

class Fruit {
public:
virtual ~Fruit() = 0;
};

class Apple : public Fruit {
//...
};

class Banana : public Fruit {
//...
};

class BagOfFruit {
public:
virtual ~BagOfFruit() = 0;

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

class BagOfApple : public BagOfFruit {
//...
};

class BagOfBanana : public BagOfFruit {
//...
};

class Shop {
public:
virtual ~Shop() = 0 {}

void push_bag (const BagOfFruit&);

private:
typedef boost::shared_ptr<BagOfFruit> BagOfFruitPtr;
typedef std::vector<BagOfFruitPtr> BagsVector;
BagsVector itsContents;
};

class AppleShop : public Shop {
//...
};

class BananaShop : public Shop {
//...
};

class Market {
public:
void push_shop (const Shop&);

private:
typedef boost::shared_ptr<Shop> ShopPtr;
typedef std::vector<ShopPtr> ShopVector;
ShopVector itsContents;
};

The main thing here is Market class. It has to be able to deal with various
shops. Operating through base class Shop is clearest way to do it. Now,
shops deal with bags of fruit. Thus, I need common interface to various
bags of fruit (namely BagOfFruit class). And bags of fruit operate with
various Fruit subclasses.

Upper design has numerous problems. First of all, working with BagOfFruit
pointer allows Apple to be pushed into BagOfBanana which should not happen.
Same goes with Shop. BagOfApple could end up in BananaShop which should not
happen.

All above just illustrates my real problem. I have two requirements:
- common interface that allows me to manipulate with Fruit, BagOfFruit
and Shop
- restriction (already on base class level) that ie. Apple can't end up
in BagOfBanana etc.

I've had two ideas:
- Use of templates for Fruit, BagOfFruit and Shop. This solves problem
of restricting who goes where because template instantiation like
BagOfFruit<Banana> works only for Banana and so on... But this eliminates
the possibility for Market to store any kind of Shop because there is no
base Shop class, only Shop<T> template. And the main purpose of all this
was to enable Market to contain and handle any shop...

- Other idea was to use kind of RTTI in base classes. For example I
would add
enum FruitTypes {
APPLE,
BANANA
};

and then to all base classes:

virtual FruitTypes FruitType() const = 0;

With this, Fruit::eek:perator= (const Fruit& rv) (again, for demonstration
purpose let's assume that this operator is overloaded) would throw if
'this' and 'rv' don't return same FruitType(). This seems like behavior
that could confuse clients, and would also require same thing done for
other base classes, many checks in member methods and so on... Code getting
messy, and behavior confusing, this is never good. But I can't figure out
anything else to do... Any suggestions?

TIA


Let's do this.

class AbstractBagOfFruit {
public:
virtual ~AbstractBagOfFruit() = 0;
virtual void push_fruit(const Fruit &);
}

template<class Fruit>
class BagOfFruit : public AbstractBagOfFruit {
public:
virtual ~BagOfFruit();

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

There are many types of bag when you *analyze*. And then you need to
implement the model you just get. Use a template is just a *implement*
tech. I don't think this is a same thing.

Good Luck.
 
A

Alf P. Steinbach

* T.A.:
Class hierarchy below demonstrates my problem:
[snip]

Too much for me to read, sorry.

But in general, note that a /constant/ BagOfApple is-a constant BagOfFruit.

There.
 
T

T.A.

Shops deal with bags. A bag can be a bag of apples or a bag of bananas,
etc.
A Shop can sell any of those fruit bag derivatives.
So template the containers.

t#include <iostream>
#include <vector>

struct Fruit {
virtual ~Fruit() = 0;
};

Fruit::~Fruit() { }

struct Apple : public Fruit {
Apple() { std::cout << "Apple()\n"; }
Apple(const Apple& copy) { std::cout << "copy Apple\n"; }
~Apple() { std::cout << "~Apple()\n"; }
};

template < typename F >
class BagOfFruit {
std::vector< F > vf;
public:
BagOfFruit() : vf() { }
BagOfFruit(size_t sz) : vf(sz) { }
void push_back( const F& r_f)
{
vf.push_back(r_f);
}
};

int main() {
BagOfFruit< Apple > apples(10);
}

I'm sorry, i don't see any benefit of using a smart ptr when storing
fruit.


Why, can't a Shop hold a vector of templated bags?

Because Market needs to operate shops through base class pointers. If Shop
holds templatized BagOfFruit then it also must be implemented as template:

template <class bag_of_fruit>
class Shop
{
public:
//...
private:
std::vector<bag_of_fruit>
};

This allows shop to be restricted to specific BagOfFruit but disallows
Market to hold any type of Shop because there is no base class Shop and
thus I can't store Shop pointers in Market. Storing pointers to Shop is
only way I know for Market to operate on Shop polymorphically.
 
T

T.A.

Let's do this.

class AbstractBagOfFruit {
public:
virtual ~AbstractBagOfFruit() = 0;
virtual void push_fruit(const Fruit &);
}

template<class Fruit>
class BagOfFruit : public AbstractBagOfFruit {
public:
virtual ~BagOfFruit();

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

On a first glance this looks like the thing I've needed, I'll try it...
Thanks.
There are many types of bag when you *analyze*. And then you need to
implement the model you just get. Use a template is just a *implement*
tech. I don't think this is a same thing.

Agreed...
 
T

T.A.

On a first glance this looks like the thing I've needed, I'll try it...
Thanks.
But on the second glance... It should be sth like this...

class AbstractBagOfFruit {
public:
virtual ~AbstractBagOfFruit() = 0;
virtual void push_fruit(const Fruit &) = 0;
};

template<class FruitType>
class BagOfFruit : public AbstractBagOfFruit {
public:
virtual ~BagOfFruit() {}

virtual void push_fruit (const FruitType& rv);

private:
typedef std::vector<FruitType> FruitVector;
FruitVector itsContents;
};

typedef BagOfFruit<Apple> BagOfApple;

int main ()
{
BagOfApple apple_bag;
}

But this doesn't compile because

void push_fruit(const Fruit &)

is not the same as

void push_fruit(const Apple &)

Pure virtual in AbstractBagOfFruit is not actually implemented in
template<class FruitType> class BagOfFruit...

Now, I could use RTTI in push_fruit implementation like this:

template<class FruitType>
void BagOfFruit::push_fruit(const Fruit & rv) {
if (typeid(rv) == typeid (FruitType)) {
// do push
} else {
// throw something
}
}

This not only that is not nice to client of BagOfFruit but also still
allows this to compile:

int main ()
{
BagOfApple apple_bag;

apple_bag.push_fruit (Banana());
}

So I'm stuck at the begining...
 
D

Daniel T.

T.A. said:
Class hierarchy below demonstrates my problem:

#include <vector>
#include <boost/smart_ptr.hpp>

class Fruit {
public:
virtual ~Fruit() = 0;
};

class Apple : public Fruit {
//...
};

class Banana : public Fruit {
//...
};

class BagOfFruit {
public:
virtual ~BagOfFruit() = 0;

void push_fruit (const Fruit&);

private:
typedef boost::shared_ptr<Fruit> FruitPtr;
typedef std::vector<FruitPtr> FruitVector;
FruitVector itsContents;
};

class BagOfApple : public BagOfFruit {
//...
};

class BagOfBanana : public BagOfFruit {
//...
};

class Shop {
public:
virtual ~Shop() = 0 {}

void push_bag (const BagOfFruit&);

private:
typedef boost::shared_ptr<BagOfFruit> BagOfFruitPtr;
typedef std::vector<BagOfFruitPtr> BagsVector;
BagsVector itsContents;
};

class AppleShop : public Shop {
//...
};

class BananaShop : public Shop {
//...
};

class Market {
public:
void push_shop (const Shop&);

private:
typedef boost::shared_ptr<Shop> ShopPtr;
typedef std::vector<ShopPtr> ShopVector;
ShopVector itsContents;
};

The main thing here is Market class. It has to be able to deal with various
shops. Operating through base class Shop is clearest way to do it. Now,
shops deal with bags of fruit. Thus, I need common interface to various
bags of fruit (namely BagOfFruit class). And bags of fruit operate with
various Fruit subclasses.

Let's look at a real world example. Let's say my name is "market" and
your name is "appleShop". You like it when people give you bags of
apples. Is it appropriate for me to deal with you without knowing that
fact? Of course not.

Market simply cannot pass "bags of fruit" to Shops without knowing (a)
what kind of fruit is in the bag and (b) what kind of fruit the shop
wants.

In other words, your whole design is messed up. "push_bag(BagOfFruit)"
is an inappropriate member-function for "Shop", remove it and Markets
can work with Shops with no problem. (Or as another poster said, allow
Markets to only work with 'const Shop' objects.
Upper design has numerous problems. First of all, working with BagOfFruit
pointer allows Apple to be pushed into BagOfBanana which should not happen.

Personally I don't see a problem with that. Although it may be true that
an AppleShop object can't have Banana objects in its particular bag of
fruit, that doesn't mean that you can't have a generic BagOfFruit class.
For example:

class AppleShop {
BagOfFruit bag;
public:
void push_apple( const Apple& a );
};

There is nothing wrong with the above, the AppleShop class is in charge
of making sure that only Apples are put in its Bag.
All above just illustrates my real problem. I have two requirements:
- common interface that allows me to manipulate with Fruit, BagOfFruit
and Shop
- restriction (already on base class level) that ie. Apple can't end up
in BagOfBanana etc.

You can have a common interface, but not the interface you have
currently. You should only have methods that actually *are* common
amongst the various sub-classes.
I've had two ideas:

I don't like either of your ideas. What you need to do is identify what
methods Market actually uses of Shop ("push_bag" is obviously not one of
them because Market doesn't have enough information to do that,) and
then make an abstract class containing only those methods.

Do the same for what methods Shop uses when dealing with BagOfFruit
objects.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,744
Latest member
CortneyMcK

Latest Threads

Top