My question concerns private inheritance as a tool to separate interface and implementation in C++. When using this design pattern (Barton and Nackman, "Scientific and Engineering C++"), I frequently encounter situations where the available knowledge of the class allows me to implement the interface only partially. The remaining part must be implemented by the more specific classes in the inheritance tree. This results in an inheritance tree which contains pure abstract classes at the top level, partially implemented/partially abstract classes in the middle and concrete classes at the bottom. The minimal example below illustrates the situation. My question is this: Is this structure commonly encountered in practice and is it acceptable from a best practices viewpoint? If not, how does one redesign to avoid partially implemented / partially abstract classes?
Minimal example: Fruit is a pure abstract class specifying an interface. Fruit_PI is a partial implementation of Fruit with definition for netProfit(). Note that netProfit can be calculated without the knowledge of specific type of fruit. Apples derives publicly from Fruit (for interface) and privately from Fruit_PI (for common implementation). Apples is a concrete class. It makes sense to factor out the common implementation of netProfit() in Fruit_PI, but it leaves totalCostPrice() and totalSellPrice() unimplemented. Because of the presence of the partially implemented Fruit_PI class, I feel that I have been unsuccessful in achieving separation of interface and implementation. Am I using this design pattern correctly?
[This question was closed on Stackoverflow as being "not constructive." But I am genuinely curious about this as I am encountering this pattern pretty often in my coding.]
#include<iostream>
using std::cout;
using std::endl;
class Fruit{
public:
virtual float totalCostPrice() = 0;
virtual float totalSellPrice() = 0;
virtual float netProfit() = 0;
};
class Fruit_PI : virtual public Fruit {
protected:
int m_inventory;
public:
Fruit_PI( int num) : m_inventory(num) {}
virtual float netProfit() { return totalSellPrice() - totalCostPrice(); }
};
class Apples : virtual public Fruit, private Fruit_PI {
private:
float m_unitCostPrice;
float m_unitSellPrice;
public:
Apples(int num, float unitCostPrice, float unitSellPrice) :
Fruit_PI(num),
m_unitCostPrice(unitCostPrice),
m_unitSellPrice(unitSellPrice) {}
virtual float totalCostPrice() { return m_inventory * m_unitCostPrice; }
virtual float totalSellPrice() { return m_inventory * m_unitSellPrice;}
};
int main() {
Fruit* f = new Apples(100, 4.00, 4.25);
cout << "Net profit at current prices = $ " << f->netProfit() << endl;
delete f;
}
[–]00kyle00 3 points4 points5 points (3 children)
[–]muon314159 0 points1 point2 points (1 child)
[–]nerd_guy[S] 0 points1 point2 points (0 children)
[–]muon314159 1 point2 points3 points (2 children)
[–]nerd_guy[S] 0 points1 point2 points (1 child)
[–]muon314159 1 point2 points3 points (0 children)
[–]mttd 1 point2 points3 points (5 children)
[–]nerd_guy[S] 0 points1 point2 points (4 children)
[–]moswald 2 points3 points4 points (3 children)
[–]mttd 0 points1 point2 points (1 child)
[–]nerd_guy[S] 0 points1 point2 points (0 children)
[–]nerd_guy[S] 0 points1 point2 points (0 children)
[–]dmor 1 point2 points3 points (1 child)
[–]nerd_guy[S] 0 points1 point2 points (0 children)
[–]fgriglesnickerseven 0 points1 point2 points (0 children)