1

So OOP is about breaking down functionality, making each class responsible for one thing etc. But let's take the example where an object is using another object. First thing that comes to mind "composition, of course!".

I have 2 questions:

Question 1: Suppose you have a Shop that is loading products from a file using a ProductsLoader, which in term uses a ProductsFileReader. You design the API so that the code is simple and readable and specialized. Therefore, you are able to do this:

    std::shared_ptr<Shop> shop = std::shared_ptr<Shop> (new Shop());
    shop->LoadProducts();
    // other code

In LoadProducts you do this:

    m_productsLoader.LoadAllProducts();
    std::shared_ptr<Products> products = m_productsLoader.GetAllProducts();
    this->setProducts(products);

In LoadAllProducts() you do this:

    prodFileReader.OpenProductsFile();
    prodFileReader.LoadDescriptionsFromFile();
    prodFileReader.CloseProductsFile();

    // create products from descriptions
    std::shared_ptr<Descriptions> descriptions = prodFileReader.GetProductsDescriptions();
    this->createProductsFromDescriptions(descriptions);

And now here is the question: Do you keep ProductsLoader m_productsLoader as a Shop member? The same for ProductsFileReader: should it be a member of ProductsLoader? The alternative for this is creating these objects on the stack: when your functions are using one of them, you just declare, initialize etc them in the function and they get destructed when the function exits. Does this defy the purpose of OOP?

Question 2: Let's say you decided to keep them as members. How do you know wheter you should keep the whole object as a member, or you should keep only a pointer to it (and, of course, in the constructor, instantiate the member and assign the pointer). Whether you access the member with the dot opperator . or arrow ->, it will perform the same functionality.

2 Answers 2

0

I would usually keep the collaborator objects as pointer members in the object that will use them. I would also usually have them implemented using an abstract class and pass the pointers as parameters to Shop's constructor rather than having Shop building them itself. This decouples Shop from the detail of how products are loaded, and means I can change to a different source of Products without needing to modify Shop; this is the fundamental essence of the Open/Closed principle, which some people describe as the most important principle on object-oriented design.

1
  • This seems very legit, I was going to ask soon how to decouple them. However, I feel like I don't need to keep the ProductLoader in memory more than it's needed. I was thinking to add some methods like Init() and Destroy(), but this is not really part of the interface I want it to expose. Maybe create a factory method and have a scoped shared_ptr to manage it?
    – Hame
    Dec 29, 2014 at 2:58
1

The subject you're asking about is called modeling, and it's as much an art as a science. But the first step of OO modeling is to forget about the mechanics of the language and think about the properties of the things you're modeling.

A class named ProductsLoader is suspect to begin with. Names like that indicate that you're thinking procedurally instead of in an OO way. Instead, the Product class should have functions to produce instances and collections of itself from files or streams or whatever.

So back to your question, but substituting Product for ProductsLoader as the example. How to decide whether Product (or a collection of them) should be a member of Shop? Well, do shops have products in them? That's the kind of question that should guide your decisions in OO modeling, not how the particular language you're using allocates things.

3
  • I thought about the Product being able to load itself, but then I thought about the interface that I want to expose in Product, and I decided it should not know where it is coming from. My requirements are to load the products from a file. Shop should not know about this, it should load the products to be its contents. So I went for the ProductsLoader, which can be easily extended to get them from the network or other source.
    – Hame
    Dec 28, 2014 at 6:09
  • Let's agree to disagree on this for the moment. What about Question 2? How do you decide Object Member vs Pointer Member To Object?
    – Hame
    Dec 28, 2014 at 6:15
  • There are potentially two interfaces exposed in Product: functions that operate on an instance, and static functions that operate on the class. Functions to load instances would be static. As for question 2, there's a third option: make it a static member in Shop. From a modeling perspective, this is probably the next best thing to static functions in Product. Dec 28, 2014 at 6:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.