开发者

A tricky OOP problem I never got my head around

开发者 https://www.devze.com 2023-03-06 20:57 出处:网络
Say I have two .cpp files: oranges.cpp and basket.cpp. They have the classes orange and basket, respective开发者_StackOverflow社区ly. My main program generates many baskets which in turn generate many

Say I have two .cpp files: oranges.cpp and basket.cpp. They have the classes orange and basket, respective开发者_StackOverflow社区ly. My main program generates many baskets which in turn generate many oranges. So basically, main will have many objects of Baskets; and baskets will have many objects of Oranges. If I have a function in orange that needs to know the color of my basket, how would I go about finding the color of the basket?

orangle.cpp

class oranges
{
    void whichColorBasket()
    {
        // get the color of the basket the orange is in...?
    }
}

basket.cpp

class basket
{
    int color;

    void basket()
    {
        for(int i = 0; i < 10; i++)              
            oranges *o = new oranges;
    }
}

I know my syntax may not be perfect but how would I access the datamember of basket from a function in orange (orange is an object created by basket).

Sending the color a parameter isn't an option as there are too many oranges and the color of the basket may change during runtime.

I read somewhere that static functions would do the trick, but they only work if they are in the same .cpp file.

So, what do I do?


Static functions are almost certainly not the answer here.

You would probably need to pass a reference to the "parent" Basket to the Oranges object, which it can then interrogate to find the colour.

For example:

class Basket
{
public:
    void Basket() {
        Oranges *o = new Oranges(*this);
    }

    color getColor() const { ... }
};


class Oranges
{
private:
    const Basket &myBasket;

public:
    Oranges(const Basket &b) : myBasket(b) {} // Store reference

    color getColor() const { return myBasket.getColor(); }  // Interrogate my basket

};

Whether you use a pointer or a reference will depend on whether the Oranges can ever move between Baskets.


you'll need a reference in orange pointing to the basket the orange is in

class oranges{
    basket* b;
    int whichColorBasket() {
        return b->color;
    }
}


class basket{
     int color;

     void basket(){
         for(int i=0;i<10;i++){         
              oranges *o=new oranges;
              o->b = &this;
         }
     } 
}

I'm ignoring memory leaks here


You need to somehow bind oranges to the basket they belong to - for example, by passing a pointer to the basket. How you do it will depend on whether oranges can change their binding to a backet during their lifetime and whether backet can be destroyed before all oranges in it are destroyed.

Assuming oranges never outlive the basket and never change the basket you do it this way:

class oranges{
public:
  oranges( backet* object ) { belongsTo = object; }
  void whichColorBasket() {
      here get belongsTo->color;
  }         
private:
  backet* belongsTo;
 };

 class basket{
     int color;
     void basket(){
         for(int i=0;i<10;i++)              
            oranges *o=new oranges( this ); //pass "current object"
     }
 };


Your oranges object should have an instance variable basket representing the basket it's in. Add a method putInBasket which takes a basket and sets the variable to the basket it's in. Then the orange looks at the variable in the whichColorBasket method. If basket is NULL it's not in a basket.

This isn't the best design, though, cause it provides a potential for inconsistency. A basket might think it has an orange, but the orange's basket pointer could point to a different basket. Should an orange really know the color of the basket it's in? What's the use case? If you're only handling baskets, and you have an orange, perhaps the basket should have a isOrangeHere method, which tells you whether a given orange is there. You call it on all the baskets, and then you take the color of the one that returns true.


The simple answer is that you don't. If you have to, there is a problem in your design somewhere: what kind of orange would know what basket it is in? And what if the orange isn't in a basket?

If you need to support this in some way, the "proper" solution would be something along the lines of the Observer pattern; your fruit would be an observer of its container; when you put the fruit into a container, the container would register with the fruit, and when you took the fruit out, the container would deregister. Clients could then ask the fruit for their current container, and ask it whatever they want.


Add a property to oranges that contains a reference to the parent basket. In the method for adding oranges to basket, also set the parent to the basket.

class oranges{
    basket* basket;

    void oranged(basket* bask)
    {
       basket = bask;
    }
    int whichColorBasket() {
       return basket->color;
    }
}

class basket{
     int color;

     void basket(){
         for(int i=0;i<10;i++)              
              oranges *o=new oranges(&this);
     }         
}

This syntax may be wrong but this is how its normally done.


Since you're using C++ use a pointer in your oranges. int *basket_color; then just assign it the address of the baskets color basket_color = &color


The way to avoid the coupling is to ask each basket if it contains a specific orange. If it does, check its color.

Why should the orange care about the basket's color? What about the apples in the other basket? Are they interested too?


Your oranges class could have a basket member variable that references the basket that contains it. Then, when an oranges object wants to know the colour of its containing basket it just calls gets the value of myBasket.color.

The oranges constructor would have to initialise this member variable, so it would need a basket parameter.


EDIT: Completely redid this solution as I had missed on a key constraint.

Another possible way would be to add a member variable to the orange class like so:

class oranges
{
    private:
    int m_nBasketID;

    public:
    oranges(int nID = 0)
    {
       m_nBasketID = nID;
    }

    void whichColorBasket() 
    {
        return gBasketList[m_nBasketID].color;
    }
}

And to set the value while creating the oranges in the basket class, like so:

class basket
{
   static unsigned int nBasketID;
 public:
 int color;
 void basket()
 {
     //First basket will have ID = 1 because
     //0 is reserved for unknown/uninitialized state.
     nBasketID++;

     for(int i=0;i<10;i++)              
          oranges *o=new oranges(nBasketID);

     gBasketList.push_back(*this);
 }

Additional modifications are as follows:

unsigned int basket::nBasketID = 0;
std::vector<basket> gBasketList;

I've made a couple of assumptions here:

  1. There is a known value (such as zero) that can be used to represent an unknown/uninitialized colour state. This value can be used a default value in the constructor of oranges.
  2. The oranges do not switch baskets after creation. If this use-case needs to be addressed then may you could delete the orange from the original basket and add a new one to the destination basekt or provide a member function to handle this case etc.
0

精彩评论

暂无评论...
验证码 换一张
取 消