I am trying to understand the is-a vs is-like-a relationship where I read somewhere that we must try to follow design such that we always have is-a relationship and not is-like-a. Consider the classic example of shape base class and derived triangle and circle classes. So circle is-a shape and so is triangle is-a shape. The function display area was defined in the base class. Now the below program runs fine.
#include "stdafx.h"
#include <cmath>
#include <iostream>
class shape
{
public:
virtual void displayArea()=0;
};
class circle :public shape
{
int radius;
public:
circle(int radius2) :radius(radius2){ }
void displayArea()
{
double area = 3.14*radius*radius;
std::cout << " \n Area circle" << area<<std::endl;
}
};
class triangle :public shape
{
double a,b,c;
public:
triangle(double a1, double b1, double c1): a(a1), b(b1),c(c1)
{
if (a + b > c && a + c > b && b + c > a)
std::cout << "The sides form a triangle" << std::endl;
else
std::cout << "The sides do not form a triangle. Correct me !" << std::endl;
}
void displayArea()
{
double s = (a + b + c) / 2;
double area = sqrt(s*(s - a)*(s - b)*(s - c));
std::cout << " \n Area triangle"<< area<<std::endl;
}
};
void main()
{
shape * p1[2];
p1[0]= new circle(20);
p1[1] = new triangle(5.6,8.1,10.3);
for (int i = 0; i < 2; ++i)
{
p1[i]->displayArea();
}
int y;
std::cin >> y;
}
Now if the requirement comes that one needs to implement modifyShape
function where each parameter of the shape is modified based on parameter of the user then how should I change my classes such that my is-a relationship is not altered. When I look at it, I feel I will have to define a single argument modifyShape
in circle and a 3-argument modifyShape
in triangle. But how should this function look like in the base class?
Option 1: I define both single argument and two argument modifyShape
function in shape but that would mean i would be having an extra 2 argument function in circle and an extra 1 argument function in triangle.
Option 2: I define a variable argument function modifyShape
in shape but somehow this is not looking cleaner to me.
There is a third option you can use, you can crate a new hierarchy of classes (or structs) that will represent the parameters of each shape. Then you can pass the pointer to the base class as an argument to the virtual function. For example:
struct ShapeParams
{
...
}
struct TriangleParams : public ShapeParams
{
double a;
double b;
double c:
}
class shape
{
public:
virtual void displayArea()=0;
modifyShape (ShapeParams*) = 0;
};
class triangle :public shape
{
public:
void modifyShape (ShapeParams*) = override;
private:
TriangleParams m_params;
}