Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object Orientation: How to Choose from a Number of Implementations

I am a decent procedural programmer, but I am a newbie to object orientation (I was trained as an engineer on good old Pascal and C). What I find particularly tricky is choosing one of a number of ways to achieve the same thing. This is especially true for C++, because its power allows you to do almost anything you like, even horrible things (I guess the power/responsibility adage is appropriate here).

I thought it might help me to run one particular case that I'm struggling with by the community, to get a feel for how people go about making these choices. What I'm looking for is both advice pertinent to my specific case, and also more general pointers (no pun intended). Here goes:

As an exercise, I am developing a simple simulator where a "geometric representation" can be of two types: a "circle", or a "polygon". Other parts of the simulator will then need to accept these representations, and potentially deal with them differently. I have come up with at least four different ways in which to do this. What are the merits/drawbacks/trade-offs of each?

A: Function Overloading

Declare Circle and Polygon as unrelated classes, and then overload each external method that requires a geometric representation.

B: Casting

Declare an enum GeometricRepresentationType {Circle, Polygon}. Declare an abstract GeometricRepresentation class and inherit Circle and Polygon from it. GeometricRepresentation has a virtual GetType() method that is implemented by Circle and Polygon. Methods then use GetType() and a switch statement to cast a GeometricRepresentation to the appropriate type.

C: Not Sure of an Appropriate Name

Declare an enum type and an abstract class as in B. In this class, also create functions Circle* ToCircle() {return NULL;} and Polygon* ToPolygon() {return NULL;}. Each derived class then overloads the respective function, returning this. Is this simply a re-invention of dynamic casting?

D: Bunch Them Together

Implement them as a single class having an enum member indicating which type the object is. The class has members that can store both representations. It is then up to external methods not to call silly functions (e.g. GetRadius() on a polygon or GetOrder() on a circle).

like image 847
MGA Avatar asked Mar 09 '14 16:03

MGA


People also ask

How is object oriented design implemented?

Object Oriented Design (OOD) serves as part of the object oriented programming (OOP) process of lifestyle. It is mainly the process of using an object methodology to design a computing system or application. This technique enables the implementation of a software based on the concepts of objects.

What is object-oriented implementation?

Object-Oriented Programming (OOP) is a programming paradigm based on objects as the central concept. In OOP, code is formatted based on functionality, enabling code maintenance, abstraction, reusability, efficiency, and numerous functionality on the object.


2 Answers

Here are a couple of design rules (of thumb) that I teach my OO students:

1) any time you would be tempted to create an enum to keep track of some mode in an object/class, you could (probably better) create a derived class for each enum value.

2) any time you write an if-statement about an object (or its current state/mode/whatever), you could (probably better) make a virtual function call to perform some (more abstract) operation, where the original then- or else-sub-statement is the body of the derived object's virtual function.

For example, instead of doing this:

if (obj->type() == CIRCLE) {
    // do something circle-ish
    double circum = M_PI * 2 * obj->getRadius();
    cout << circum;
}
else if (obj->type() == POLY) {
    // do something polygon-ish
    double perim = 0;
    for (int i=0; i<obj->segments(); i++)
        perm += obj->getSegLength(i);
    cout << perim;
}

Do this:

cout << obj->getPerimeter();

...

double Circle::getPerimeter() {
    return M_PI * 2 * obj->getRadius();
}

double Poly::getPerimeter() {
    double perim = 0;
    for (int i=0; i<segments(); i++)
        perm += getSegLength(i);
    return perim;
}

In the case above it is pretty obvious what the "more abstract" idea is, perimeter. This will not always be the case. Sometimes it won't even have a good name, which is one of the reasons it's hard to "see". But, you can convert any if-statement into a virtual function call where the "if" part is replaced by the virtual-ness of the function.

In your case I definitely agree with the answer from Avi, you need a base/interface class and derived subclasses for Circle and Polygon.

like image 145
Dwayne Towell Avatar answered Oct 11 '22 15:10

Dwayne Towell


Most probably you'll have common methods between the Polygon and Circle. I'd combine them both under an interface named Shape, for example(writing in java because it's fresher in my mind syntax-wise. But that's what I would use if I wrote c++ example. It's just been a while since I wrote c++):

public interface Shape {
   public double getArea();

   public double getCentroid();

   public double getPerimiter(); 
}

And have both Polygon and Circle implement this interface:

public class Circle implements Shape {
   // Implement the methods
}

public class Polygon implements Shape {
   // Implement the methods
}

What are you getting:

  1. You can always treat Shape as a generelized object with certain properties. You'll be able to add different Shape implementations in the future without changing the code that does something with Shape (unless you'll have something specific for a new Shape)

  2. If you have methods that are exactly the same, you can replace the interface with abstract class and implement those (in C++ interface is just an abstract class with nothing implemented)

Most importantly (I'm emphesizing bullet #1) - you'll enjoy the power of polymorphism. If you use enums to declare your types, you'll one day have to change a lot of places in the code if you want to add new shape. Whereas, you won't have to change nothing for a new class the implements shape.

like image 29
Avi Avatar answered Oct 11 '22 14:10

Avi