Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Design problem related to A-Bag-Of-Fruit is not the same as A-Bag-Of-Apple

Reply
Thread Tools

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

 
 
T.A.
Guest
Posts: n/a
 
      12-02-2006
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: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
 
Reply With Quote
 
 
 
 
Salt_Peter
Guest
Posts: n/a
 
      12-03-2006

T.A. wrote:
> 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?

>
> - 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: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


 
Reply With Quote
 
 
 
 
Xeranic
Guest
Posts: n/a
 
      12-03-2006

T.A. wrote:
> 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: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.

 
Reply With Quote
 
Alf P. Steinbach
Guest
Posts: n/a
 
      12-03-2006
* 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.

--
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?
 
Reply With Quote
 
T.A.
Guest
Posts: n/a
 
      12-03-2006
On 2 Dec 2006 17:56:16 -0800, Salt_Peter wrote:

> T.A. wrote:
>> 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?
>


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.
 
Reply With Quote
 
T.A.
Guest
Posts: n/a
 
      12-03-2006
On 2 Dec 2006 19:35:43 -0800, Xeranic wrote:

> T.A. wrote:
>> Class hierarchy below demonstrates my problem:
>>
>> ...
>>
>> 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;
> };


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...
 
Reply With Quote
 
T.A.
Guest
Posts: n/a
 
      12-03-2006
On Sun, 3 Dec 2006 11:22:40 +0100, T.A. wrote:

> On 2 Dec 2006 19:35:43 -0800, Xeranic wrote:
>
>> T.A. wrote:
>>> Class hierarchy below demonstrates my problem:
>>>
>>> ...
>>>
>>> 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;
>> };

>
> 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:ush_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...
 
Reply With Quote
 
Daniel T.
Guest
Posts: n/a
 
      12-03-2006
"T.A." <(E-Mail Removed)> wrote:

> 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.

--
To send me email, put "sheltie" in the subject.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How should multiple (related) projects be arranged (structured) and configured so that they can share code, have a related package structure and enable proper unittesting, and ensuring no namespace collisions ToddLMorgan@gmail.com Python 14 04-21-2006 04:03 PM
Problem related to design Amit Mahajan C++ 1 03-14-2006 12:23 PM
Problem related to design Amit Mahajan C++ 0 03-13-2006 09:38 AM
How should threads be terminated? (related to 'Help with thread related tracebacks') Maxwell Hammer Python 7 06-18-2005 04:20 PM
Iterator-Related Java Design Problem Ken Java 4 06-21-2004 05:48 PM



Advertisments