I always like simple and minimal designs that satisfy the "requirements". When design is reduced to minimum, the relationship between form and function becomes self-evident (or simply-evident). In object design, it is the usage or "perceived usage" that constitutes the requirements. As an example, suppose we have the following stocktest() routine:
void stocktest() {
1. stock s1("APPL", 125, 50), s2("FB", 80), s3; // 50 shares of APPL at 125 each, 0 share of FB at 80
2. s3 = s1;
3. cout << s1 << s2 << s3;
4. s1.setstock("GOOG", 553, 72);
5. stock s4(s1);
6. cout << s4;
}
The above routine essentially lays out the minimum requirements for stock class as:
constructor with 0, 2, or 3 parameters
assignment operator ( will default assignment operator work?)
operator << overloading
public function setstock
copy constructor ( again, will default work?)
Now, we can move on to design the stock.h file. Here is my take
class stock
{
friend ostream& operator<< (ostream&, const stock&);
friend istream& operator>> (istream&, stock&);
public:
stock(string sym="", int c=0, int s=0); // constructor with default
void setstock(string, int, int);
private:
string symbol;
int cost;
int shares;
};
In fact, the above design is not quite minimum, because I add the >> operator. Well, I like balance too :-)
One more thing: cost, shares are private... of course, they can be designed as public too. What are the reasons for choosing public or private? information hiding, yes, anything else?
The stock constructor above stock(string sym="", int c=0, int s=0); can handle stock declarations of: three parameters s1, two parameters s2 and no parameters s3. The capability is available because of "function overloading". In fact, the constructor can be written as:
stock(string sym, int c, int s);
stock(string sym, inc c);
stock();
Function overloading is analogic to operator overloading. Well, operator is a function.
operator<< (ostream& os, const stock& st);
The function << takes left hand side os, and right hand side st.
The implementation of stock.cpp is a straightforward exercise.
ostream& operator<< (ostream& os, const stock& sg)
{
os << sg.symbol << " " << sg.cost << " " << sg.shares << endl;
return os;
}
istream& operator>> (istream& is, stock& sg)
{
is >> sg.symbol >> sg.cost >> sg.shares;
return is;
}
stock::stock(string sym, int c, int sh) {
setstock(sym, c, sh);
}
void stock::setstock(string sym, int c, int sh) {
symbol=sym; cost=c; shares=sh;
}
--------------------------
Just another example, in Fall 2019 I gave the following object design question:
A course class has an id (number), location (string), units (number) as public members. A student class
has an id (number), name (string) as public members and a number of course objects organized as a
forward linked list .
The application main routine has the following instructions:
int main() {
1. course C1(126, "ENG337", 5), C2(180, "ENG325"); // 5 units in 126 course, 3 units in 180 course
2. std::cout << C1 << std::endl; // print out course information on one line
3.
4. student frank("frank", 111), patel("patel", 222), jose; // declare three students
5. frank.add(C1).add(C2); // frank add two course
6. patel.add(C2); // patel add one course
7. jose = patel; // copy patel to jose
8. patel.remove(C2); // delete C2 from patel
9. std::cout << frank << patel << jose << std::endl;
}
Instead of stock class, it is course class. The design is very similar. In fact, most of the real world objects can fit in the same design model.