0

Hi all,

I’ve been told to convert a procedural C++ program into an object-oriented one, using the principals of polymorphism, encapsulation and inheritance.

The standard C++ program accepts data values and filter values from the user. It then multiplies these values together using a simple algorithm and prints the output to the screen. The standard program is shown below.

I have made a start by deciding to use a class for 'TheFilter' and a class for 'TheData' each with there own 'enterdata', 'displaydata()' and constructor member functions.

The problem I am having is with part of the criteria set, "main() should do no more than create the first object", I am having difficulty getting my head around this as all examples of OOP code I have ever seen generate objects within main().
The only current solution I could come up with to obey this is to create a 3rd class (FilteredData - see below) which would instantiate the other classes objects 'OriginalData', 'Filter' and 'FilteredData' upon being instantiated itself. However this is troubling me as would it not take away from the concept of encapsulation as it would result in having a class with members which are other classes objects?

If anyone has any suggestions at all into how this could be avoided and best obey the principles of encapsulation I would be very grateful!

I hate to admit defeat, but I’ve never programmed using an object-oriented approach and I’ve only been studying C++ for a couple of months. Please help!

Thanks in advance.
eelhonk

#include <iostream>
using namespace std;

class TheData 
{
public:
        TheData(double* = 0, unsigned long = 0, bool = false);      // constructor function
        ~TheData();

        void EnterData(TheData& OriginalData);              
        void DisplayData(TheData OriginalData, TheData FilteredData) const;

private:
        double* Values;                     
        unsigned long Length;                       
        bool Valid;

}

class TheFilter
{
public:
        TheFilter(double* = 0, unsigned long = 0, bool = false);    // constructor function
        ~TheFilter();

        void EnterData(TheData& OriginalData);              
        void DisplayData(TheData OriginalData, TheData FilteredData) const;
        int ApplyFilter();

private:
        double* Values;                     
        unsigned long Length;                       
        bool Valid;

}
class 
{
 public:
        FilteredData();    // constructor function that somehow instantiates an object for the filter and the data???
        void DisplayData();
 private:
        TheData data
        TheFilter filter
        double * filteredData
}

int main()
{   
    FilteredData Object1;
}
3
Contributors
7
Replies
45
Views
7 Months
Discussion Span
Last Post by JamesCherrill
1

This is difficult, if only because the problem isn't rich enough to require much in the way of OO design or implementation.
Anyway, here's one thought:
We start with picking Objects from the components in the problem defintion (DataSet, Filter), but you also need objects to represent the overall processes that the system performs. Often these Objects represent kinds of users (client requests cash from his account, bank teller validates request and issues cash - account is an obvious object, but also client and teller are candidate objects to manage the process). Other times you use an object that simply represents an instance of the process being performed (CashWithdrawal).
In this case you could have a class "Menu" whose responsibilities are to display itself, get the user's choice, create a Data object (this is delegated to the Data class), create a Filter object (delegated to the Filter class), filter the Data (delegated to the Filter object), and print the result (delegated to the Data object that is created by applying the Filter).
Your main just creates an instance of an Menu, and that does the rest.

Edited by JamesCherrill

1

would it not take away from the concept of encapsulation as it would result in having a class with members which are other classes objects?

Absolutely not. It's rare to find a class that does NOT have members that are objects of other classes (Car has Engine, Transmission. Employee has a Job. etc). What's important is that the containing class doesn't get involved with the internals of the members (and vice-versa). The containing class simply uses the member's public methods to ask them to perform their individual responsibilities (engine.increaseRevs(), transmission.shiftToGear(2) etc)

1

This is difficult, if only because the problem isn't rich enough to require much in the way of OO design or implementation.

Agree. This is a tough project to start your Object-Oriented career on. Note James' examples.

(Car has Engine, Transmission. Employee has a Job. etc)

This is easy to visualize. You need no Object-Oriented programming experience or even any programming experience at all to understand the relationship and figure out where things go and if things look wrong. "Data has a Filter" and "FilteredData has Data and a Filter" don't roll off the tongue nearly as nicely. If you are looking to learn OO, you might consider putting this assignment on the shelf and taking some tutorials that focus on "Car has an Engine" and "Employee has a Job" to get the hang of it, then come back.

One thing I'll throw out there though. Naming is important. Name something right and the Code-to-English and vice-versa flows nicely. Name it wrong and you'll get confused. I would change the class names to Filter, Data, and FilteredData. Get rid of the word "The" for the class name. "The" would be more in line with an object name, not a class name. Think about how you would speak in English: "I own a The Car" doesn't make sense. "I own a Car" makes sense. "I own a Car. I have named it 'The Car'" makes sense (sort of).

Car theCar; // build a new Car object by calling Car() constructor. Name this Car object "theCar".

Edited by AssertNull: spelling

1

I agree with assertnull re naming. In fact I'm obsessive about it. Not only does it manage the readability of the code, but good naming makes mistakes jump out at you.

I would change the class names to Filter, Data, and FilteredData

Names OK, but maybe filtered data is still just data, so maybe theRawData and theFilteredData should be instances of the same class Data ? In fact, looking at the limited info we have I wonder if Filter is-a-kind-of Data, and should therefore be a subclass???

Edited by JamesCherrill

1

Names OK, but maybe filtered data is still just data, so maybe theRawData and theFilteredData should be instances of the same class Data ? In fact, looking at the limited info we have I wonder if Filter is-a-kind-of Data, and should therefore be a subclass???

Fantastic example of why this shouldn't be the first Object-Oriented project in the learning curve. It's not obvious how this should all be organized as opposed to "Car has an Engine", plus subclasses are being considered.

0

AssertNull and James, thank you so much for the replies!

My current thoughts on this is that as stated by James :

theRawData and theFilteredData should be instances of the same class Data

I am now thinking of having only 2 objects, Filter and Data, as in the given procedural code the menu is named Filter Menu.
My thoughts on implementing this would be to instantiate one object Filter TheFilter in Main() and then within the constructor function of Filter call member function Filter Menu which proceeds to take user inputs and instantiate 2 objects InputData and OutputData from class Data.

As I said I am completely new to OOP, so my question would be whether you agree this method would be an effective use of encapsulation or whether FilterMenu is better off as it's own class? The criteria states other developers should also be able to use these classes, this sways me towards the use of the third class but I don't want to unnescerily complicate the program.

Also, what is best practice for instantiating objects outside of main()? I'm aware it's not good to use the constructor function of a different class. Is this good form:

#include <iostream>
using namespace std;

class NumberOne {

public:
    NumberOne(int = 1);

    int getVal() const
    {
        return x;
    }

private:
    int x;
};

class NumberTwo {

public:

    NumberTwo(int = 2);

    int getVal() const
    {
        return y;
    }

private:
    int y;

};

class Addition
{
public:
    Addition(int a, int b) : Object1(a), Object2(b) {
        output();
    }

    void output() 
    {
        cout << Object1.getVal() + Object2.getVal() << endl;
    }
private:
    NumberOne Object1;
    NumberTwo Object2;
};

int main()
{
    Addition Add(100, 200);

    return 0;
}

NumberOne::NumberOne(int input)
{
    x = input;
}

NumberTwo::NumberTwo(int input)
{
    y = input;
}

Thanks Again!
eelhonk

Edited by eel

1

When thinking about how many classes, consider cohesion and coupling (Google it).
The idea is that one class does one thing and contains just enough to do that thing, nothing more.
So a Filter holds filter data and knows how to filter a data object to create new data object.
A Menu knows how to interact with the user and call different functions depending in user input. You could have have multiple Menu classes - one for a command line, one for a GUI, one for a webserver, but they can all use the same Filter class. That's how you get re-use.

You may see that as more complicated, but what happens in reality is that you get multiple classes, each of which does just what it does and interacts only with the public members of any other classes. That makes it really easy to understand the overall division oof responsibility, and easy to understand the implementation of each class in isolation. It's all about architecture

As for instantiating outside main, I can't comment on good C++ practice, but in OO in genreal this is a non-issue. When the logic of your application requires you the create a new instance of something you call the appropriate constructor. To minimise coupling you create/use/destroy instances in the smallest scope/lifetime that makes sense. Doing it in main is about the longest possible lifetime, and potentially shares over a whole-program scope, so it's typically a bad idea.

Edited by JamesCherrill

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.