I'm trying to get an understanding of object oriented style programming in Haskell, knowing that things are going to be a bit different due to lack of mutability. I've played around with type classes, but my understanding of them is limited to them as interfaces. So I've coded up a C++ example, which is the standard diamond with a pure base and virtual inheritance. Bat inherits Flying and Mammal, and both Flying and Mammal inherit Animal.
#include <iostream>
class Animal
{
public:
virtual std::string transport() const = 0;
virtual std::string type() const = 0;
std::string describe() const;
};
std::string Animal::describe() const
{ return "I am a " + this->transport() + " " + this->type(); }
class Flying : virtual public Animal
{
public:
virtual std::string transport() const;
};
std::string Flying::transport() const { return "Flying"; }
class Mammal : virtual public Animal
{
public:
virtual std::string type() const;
};
std::string Mammal::type() const { return "Mammal"; }
class Bat : public Flying, public Mammal {};
int main() {
Bat b;
std::cout << b.describe() << std::endl;
return 0;
}
Basically I'm interested in how to translate such a structure into Haskell, basically that would allow me to have a list of Animals, like I could have an array of (smart) pointers to Animals in C++.
You just don't want to do that, don't even start. OO sure has its merits, but “classic examples” like your C++ one are almost always contrived structures designed to hammer the paradigm into undergraduate students' brains so they won't start complaining about how stupid the languages are they're supposed to use†.
The idea seems basically modelling “real-world objects” by objects in your programming language. Which can be a good approach for actual programming problems, but it only makes sense if you can in fact draw an analogy between how you'd use the real-world object and how the OO objects are handled inside the program.
Which is just ridiculous for such animal examples. If anything, the methods would have to be stuff like “feed”, “milk”, “slaughter”... but “transport” is a misnomer, I'd take that to actually move the animal, which would rather be a method of the environment the animal lives in, and basically makes only sense as part of a visitor pattern.
describe, type and what you call transport are, on the other hand, much simpler. These are basically type-dependent constants or simple pure functions. Only OO paranoia‡ ratifies making them class methods.
Any thing along the lines of this animal stuff, where there's basically only data, becomes way simpler if you don't try do force it into something OO-like but just stay with (usefully typed) data in Haskell.
So as this example obviously doesn't bring us any further let's consider something where OOP does make sense. Widget toolkits come to the mind. Something like
class Widget;
class Container : public Widget {
std::vector<std::unique_ptr<Widget>> children;
public:
// getters ...
};
class Paned : public Container { public:
Rectangle childBoundaries(int) const;
};
class ReEquipable : public Container { public:
void pushNewChild(std::unique_ptr<Widget>&&);
void popChild(int);
};
class HJuxtaposition: public Paned, public ReEquipable { ... };
Why OO makes sense here? First, it readily allows us to store a heterogeneous collection of widgets. That's actually not easy to achieve in Haskell, but before trying it, you might ask yourself if you really need it. For certain containers, it's perhaps not so desirable to allow this, after all. In Haskell, parametric polymorphism is very nice to use. For any given type of widget, we observe the functionality of Container pretty much reduces to a simple list. So why not just use a list, wherever you require a Container?
Of course, in this example, you'll probably find you do need heterogeneous containers; the most direct way to obtain them is {-# LANGUAGE ExistentialQuantification #-}:
data GenericWidget = GenericWidget { forall w . Widget w => getGenericWidget :: w }
In this case Widget would be a type class (might be a rather literal translation of the abstract class Widget). In Haskell this is rather a last-resort thing to do, but might be right here.
Paned is more of an interface. We might use another type class here, basically transliterating the C++ one:
class Paned c where
childBoundaries :: c -> Int -> Maybe Rectangle
ReEquipable is more difficult, because its methods actually mutate the container. That is obviously problematic in Haskell. But again you might find it's not necessary: if you've substituted the Container class by plain lists, you might be able to do the updates as pure-functional updates.
Probably though, this would be too inefficient for the task at hand. Fully discussing ways to do mutable updates efficiently would be too much for the scope of this answer, but such ways exists, e.g. using lenses.
Summary
OO doesn't translate too well to Haskell. There isn't one simple generic isomorphism, only multiple approximations amongst which to choose requires experience. As often as possible, you should avoid approaching the problem from an OO angle alltogether and think about data, functions, monad layers instead. It turns out this gets you very far in Haskell. Only in a few applications, OO is so natural that it's worth pressing it into the language.
†Sorry, this subject always drives me into strong-opinion rant mode...
‡These paranoia are partly motivated by the troubles of mutability, which don't arise in Haskell.
In Haskell there isn't a good method for making "trees" of inheritance. Instead, we usually do something like
data Animal = Animal ...
data Mammal = Mammal Animal ...
data Bat = Bat Mammal ...
So we incapsulate common information. Which isn't that uncommon in OOP, "favor composition over inheritance". Next we create these interfaces, called type classes
class Named a where
name :: a -> String
Then we'd make Animal, Mammal, and Bat instances of Named however that made sense for each of them.
From then on, we'd just write functions to the appropriate combination of type classes, we don't really care that Bat has an Animal buried inside it with a name. We just say
prettyPrint :: Named a => a -> String
prettyPrint a = "I love " ++ name a ++ "!"
and let the underlying typeclasses worry about figuring out how to handle the specific data. This let's us write safer code in many ways, for example
foo :: Top -> Top
bar :: Topped a => a -> a
With foo, we have no idea what subtype of Top is being returned, we have to do ugly, runtime based casting to figure it out. With bar, we statically guarantee that we stick to our interface, but that the underlying implementation is consistent across the function. This makes it much easier to safely compose functions that work across different interfaces for the same type.
TLDR; In Haskell, we compose treat data more compositionally, then rely on constrained parametric polymorphism to ensure safe abstraction across concrete types without sacrificing type information.
There are many ways to implement this successfully in Haskell, but few that will "feel" much like Java. Here's one example: we'll model each type independently but provide "cast" operations which allow us to treat subtypes of Animal as an Animal
data Animal = Animal String String String
data Flying = Flying String String
data Mammal = Mammal String String
castMA :: Mammal -> Animal
castMA (Mammal transport description) = Animal transport "Mammal" description
castFA :: Flying -> Animal
castFA (Flying type description) = Animal "Flying" type description
You can then obviously make a list of Animals with no trouble. Sometimes people like to implement this via ExistentialTypes and typeclasses
class IsAnimal a where
transport :: a -> String
type :: a -> String
description :: a -> String
instance IsAnimal Animal where
transport (Animal tr _ _) = tr
type (Animal _ t _) = t
description (Animal _ _ d) = d
instance IsAnimal Flying where ...
instance IsAnimal Mammal where ...
data AnyAnimal = forall t. IsAnimal t => AnyAnimal t
which lets us inject Flying and Mammal directly into a list together
animals :: [AnyAnimal]
animals = [AnyAnimal flyingType, AnyAnimal mammalType]
but this is actually not much better than the original example since we've thrown away all information about each element in the list except that it has an IsAnimal instance, which, looking carefully, is completely equivalent to saying that it's just an Animal.
projectAnimal :: IsAnimal a => a -> Animal
projectAnimal a = Animal (transport a) (type a) (description a)
So we may as well have just gone with the first solution.
Many other answers already hint at how type classes may be interesting to you. However, I want to point out that in my experience, many times when you think that a typeclass is the solution to a problem, it's actually not. I believe this is especially true for people with an OOP background.
There's actually a very popular blog article on this, Haskell Antipattern: Existential Typeclass, you might enjoy it!
A simpler approach to your problem might be to model the interface as a plain algebraic data type, e.g.
data Animal = Animal {
animalTransport :: String,
animalType :: String
}
Such that your bat becomes a plain value:
flyingTransport :: String
flyingTransport = "Flying"
mammalType :: String
mammalType = "Mammal"
bat :: Animal
bat = Animal flyingTransport mammalType
With this at hand, you can define a program which describes any animal, much like your program does:
describe :: Animal -> String
describe a = "I am a " ++ animalTransport a ++ " " ++ animalType a
main :: IO ()
main = putStrLn (describe bat)
This makes it easy to have a list of Animal values and e.g. printing the description of each animal.
Related
I've got some class - lets call it MyMutableClass, which implements MutableInterface.
class MutableInterface {
public:
void setMyPreciousData(int value);
int getMyPreciousData() const;
.... //and so on
};
However there is a huge part of code, which should not change the state this class instance, but it need to have a read access.
How to do it in the most polite manner? Should I create an additional ImmutableInterfaces, with getters only and inherits it by MutableInterface? Then I can choose, which one will be passed to another parts of code.
Second option would be to create another class, which object would encapsulate the MutableInterface implementation and provide an access only to a subset of its methods. Is that better?
Is there some well-known patter, which I'm not aware of?
This won't be what you want to hear, but I think it's important to be said in this case.
Inheritance describes a 'is kind of' interface. The derived thing 'is a kind of' the base thing.
A const thing is not 'a kind of' mutable thing. It's an immutable thing.
A mutable thing is not 'a kind of' immutable thing. It's a thing which happens to be mutable.
Mutability is a property of the thing, not a specialisation.
Therefore, inheritance is the wrong model and this is why in c++, constness is a property, not an interface.
If you really must hide the fact that sometimes a thing is mutable (one wonders why), then as mentioned in the comments, you probably want some kind of proxy view class, such as:
// this is the actual thing
struct the_thing
{
void change_me();
int see_me() const;
};
// and this is the proxy
struct read_only_thing_view
{
int see_me() const { return _reference.see_me(); }
the_thing& _referent;
};
Lot of tutorials list abstraction as one of 4 basic principles in C++ (remaining 3 as encapsulation, inheritance and polymorphism). I tried to understand the concept of abstraction. Lot of online tutorials say that abstraction is a concept which hides the implementation details and provides only the interface. I didn't clearly understand this point. I didn't understand what we are hiding. Is this talking about hiding the internal structures that the function uses? if that is the case, even normal C function also will do this. When I talked with one of my colleague about this, he told abstract class is the best example of abstraction. But I didn't understand this also. Because when we have pure virtual function, we can't create an instance of the class and the pure virtual function mostly doesn't have definition. So there is no concept of hiding in this case. Can any one please explain abstraction in C++ with example?
You should distinguish between a language construct as abstract classes and a generic concept as abstraction.
Although abstract classes may be a useful tool in creating abstractions it's not a necessary tool, neither is using that tool a guarantee that you would get a (good) abstraction.
For example there are abstractions all over the place in the C++ standard so one should not require to come up with another example.
Take for example the STL. There are a number of containers of different kind, but for example there are sequences which all conform to a common set of functions defined on them, in addition there are guaranteed complexities for different operations depending on which one you select. The abstraction here is that these are sequential containers that you can use to store data in. Although they don't use virtual functions, the implementation varies from implementation to implementation (or at least could vary), but if you use it according to the specification the actual implementation would not matter to the programmer (and most often the programmer does not dig into the actual implementation).
Another abstraction in the specification is the language itself, the execution environment specified therein and the translation process. These parts are not specified in terms of how they are implemented, but according to the expected behavior. For example normally an implementation would implement local variables by putting them on the processor stack, but that is an implementation detail that the C++ specification leaves out. The specification puts up a number of assumptions about the behavior of the execution. And you construct your program using these assumptions instead of assuming that the implementation would need to be done in a specific concrete way.
Abstraction is something very natural in every day life, it is very common to talk about something without getting into many details of the thing. You can use your car without thinking/knowing about mechanics, fluid mechanics, chemistry, engineering, etc. Abstraction in computer engineering is exactly the same thing (in general).
Yes a simple function provides an abstraction. But functions are just small parts of a software, and they are sometimes built by factoring the code (a good idea but that do not always lead to a good abstraction). An abstraction should have a clear semantic meaning not tricky.
OOP is a paradigm in witch you can built new types and let you forget about the details of them. As in an course about algorithm where one can tell you how quicksort works but never speak about the real nature of the elements they are sorting (it is certainly not an interesting point in sorting). What is interesting about object (as with your car) is the way one can manipulate an object not how the behavior is realized. I want to turn to the left by rotating the steering to the left, I don't want to know that really happens behind the scene when I do this. When I leave my car to the repair man, I let him do anything he wants on my car provided that it works as usual (he can change anything he wants behind the scene). As a user I just want to focus on the manual not the internals. So you need to make a difference in between the interface of an ideal object (the manual) and the realization of a concrete object (the internals schemas). This is what every OOP language let you write (in different ways of course you have a variety of possibilities to realize all of this).
So you want to talk about points on the plane somewhere in your code? Let's talk about the manual (a short one for the sake on simplicity). A Point is an object from which you can get its cartesian coordinates or its polar ones, right? Then its abstract, whatever a Point is obtain/realized in the software you want to be able to do this with it. So it is an abstraction:
class Point {
public:
virtual double getX() = 0;
virtual double getY() = 0;
virtual double getAngle() = 0;
virtual double getLength() = 0;
}
This is a manual, with this you can use a point (provided you have one), then you can write a valid compilable code:
void f(Point *p) {
cout << p->getX() << "," << p->getY() << endl;
}
Here you need to be careful, either pass a pointer or a reference. You pass an object as an abstraction, then something should happen to retrieve the realization, in C++ this necessitate reference or pointer. Note that this function does not receive a Point (a Point is an abstraction something that doesn't exists), but can receive any kind of realization of a Point (this makes a big difference). Note: that this code is compilable and remains valid while you call it with a realization of the abstraction (this can be valid for a very very long time! Code reusability, you know?)
Ok now somewhere you can realize the abstraction:
class PolarPoint : public Point {
private:
double angle, length;
public:
PolarPoint(double a,double l) : angle(a), length(l) {}
virtual double getX() { return length*cos(angle); }
virtual double getY() { return length*sin(angle); }
virtual double getLength() { return length; }
virtual double getAngle() { return angle; }
}
Somewhere you instantiate it (create an object of this concrete model and then use it (then forget about all of its specificity) :
...
Point *p = new PolarPoint(3.14/4,10.0);
f( p );
....
Remind that f has been compiled even a long time ago, but works with this new realization now! An abstraction is a kind of contract.
You can also realize in another way:
class CartesianPoint : public Point {
private:
double x, y;
public:
CartesianPoint(double x,double y) : x(x), y(y) {}
virtual double getX() { return x; }
virtual double getY() { return y; }
virtual double getLength() { return /* the calculus from x/y*/; }
virtual double getAngle() { return /* the calculus from x/y */; }
}
...
Point *p2 = new CartesianPoint(3.14/6,20.56);
f( p );
...
In this example I also used information hiding, concept related to abstraction (at least useful with abstraction). private/public is related to information hiding, which lets you enforce the hiding, meaning that the user of a class can't access (at least too easily) the details, not only he is discouraged from look at them but he can't manipulate them. Again, with your car, it is not easy to change a piston, not only because it is an inner part of the engine but also because the constructor provide many ways to hide this from you : no instruction manual to do so, special tools difficult to obtain, etc. You may know that your car has a carburetor, but you may be unable to touch it.
Beware that abstraction does not mean hiding, but just let you forget about the details if you don't want to (and in general you don't want to). Abstraction is a good way to obtain low coupling of software components.
No, abstraction does not mean you must hide the internal structures.
CPP Primer Plus, page 507 give you an explain and also the example.
Life is full of complexities, and one way we cope with complexity is to frame simplifying
abstractions.You are a collection of more than an octillion atoms. Some students of the
mind would say that your mind is a collection of several semiautonomous agents. But it’s
much simpler to think of yourself as a single entity. In computing, abstraction is the crucial
step of representing information in terms of its interface with the user.That is, you
abstract the essential operational features of a problem and express a solution in those
terms. In the softball statistics example, the interface describes how the user initializes,
updates, and displays the data. From abstraction, it is a short step to the user-defined type,
which in C++ is a class design that implements the abstract interface.
Lot of tutorials list abstraction as one of 4 basic principles in C++ (remaining 3 as encapsulation, inheritance and polymorphism).
That list seems to describe Object Orientation, in any language. C++ has many "basic principles" depending on your perspective, and there's no agreed upon list.
I tried to understand the concept of abstraction. Lot of online tutorials say that abstraction is a concept which hides the implementation details and provides only the interface. I didn't clearly understand this point. I didn't understand what we are hiding. Is this talking about hiding the internal structures that the function uses? if that is the case, even normal C function also will do this.
Let's look at an example. Let's imagine a program handles a series of numeric inputs, and at a high - "abstract" - level, it wants to collect some statistics about those numbers. We might write:
#include <iostream>
template <typename Stats, typename T>
bool process_input(std::istream& in, Stats& stats)
{
T v;
while (in >> std::skipws && !in.eof() && in >> v)
stats(v);
return in; // true if no errors
}
In the above code, we "call" stats with each value v that we read from the input. But, we have no idea what stats does with the values: does it save them all, calculate min, max, a total, stdddev, the third percentile? Someone else can care because we've written our input logic above to abstract away those questions: the caller can provide a suitable stats object that does whatever's necessary (even nothing), as long as it's valid to "call" it with a value of type T using the stats(v) notation. Similarly, we didn't make a decision about what types of data the input would contain: T could be double, or std::string, or int or some yet-to-be-written class, and yet our algorithm would work for any of those because it abstracts the input logic.
Say we want a Stats object that can find the minimum and maximum of a set of values. In C++, I could write:
template <typename T>
class Stats
{
public:
Stats() : num_samples_(0) { }
void operator()(T t)
{
if (++num_samples_ == 1)
minimum_ = maximum_ = t;
else if (t < minimum_)
minimum_ = t;
else if (t > maximum_)
maximum_ = t;
}
T minimum() const { return minimum_; }
T maximum() const { return maximum_; }
size_t num_samples() const { return num_samples_; }
friend std::ostream& operator<<(std::ostream& os, const Stats& s)
{
os << "{ #" << s.num_samples_;
if (s.num_samples_)
os << ", min " << minimum_ << ", max " << maximum_;
return os << " }";
}
private:
size_t num_samples_;
T minimum_, maximum_;
};
This is just one possible implementation of an object that can be passed to process_input above. It is the void operator()(T t) function that satisfies the interface expectations of process_input. Any other function that handles a series of values could pass them to a Stat object, and they could even stream out the collected stats...
std::cout << stats << '\n';
...without ever understanding which statistics were calculated/collected. Again, that's abstraction: you can say what is to be done at a very high level, without knowing the lower-level details, let alone how it will be done.
When I talked with one of my colleague about this, he told abstract class is the best example of abstraction. But I didn't understand this also. Because when we have pure virtual function, we can't create an instance of the class and the pure virtual function mostly doesn't have definition. So there is no concept of hiding in this case. Can any one please explain abstraction in C++ with example?
What you're hiding with abstraction is how things get done - that's expressed in the definitions, so an abstract class does at least have that small amount of abstraction. Still, let's contrast the above example that had a reasonable level of abstraction from code that lacks abstraction, despite the use of an abstract class:
class Abstract_Stats
{
public:
virtual double get_minimum() const = 0;
virtual void set_minimum(double m) = 0;
virtual double get_maximum() const = 0;
virtual void set_maximum(double m) = 0;
private:
double minimum_, maximum_;
};
With such a stupid abstract class, our process_input function would need to be rewritten thus:
bool process_input(std::istream& in, Abstract_Stats& stats)
{
int v;
size_t n = 0;
while (in >> std::skipws && !in.eof() && in >> v)
if (++n == 1) { stats.set_minimum(v); stats.set_maximum(v); }
else if (v < stats.get_minimum()) stats.set_minimum(v);
else if (v > stats.get_maximum()) stats.set_maximum(v);
return in; // true if no errors
}
Suddenly, our Abstract_Stats class with it's less abstract interface has forced us to mix specifics of statistics gathering functionality into the input logic.
So, abstraction is less about whether a function is pure virtual, and more about the division of work to make things reusable in different combinations, with each being cleanly testable and understandable independently.
Abstraction and abstact classes are not the same.
Abstraction is simply creating a model of a concept or thing. However, abstraction in programming usually implies that the model is more simple than what you're abstracting. This goes for mostly all programming languages: most have constructs or ways to model what you want so that it somehow gives a benefit.
Abstracting a traffic flow simulation, for example, as a bunch of unrelated variables is messy. However, if you model each individual vehicle as an object, each object can handle its own internal state and it becomes simpler to deal with the idea of a "Vehicle" object than a bunch of variables that are not related to each other.
Abstract classes are more like Java's interfaces. They are meant to serve as a uniform programming "interface" within different internal parts of a program. By confining how objects can interact with other objects, you bring determinism to a program by confining how the program can program. It often leverages a langauge's type system to reduce the amount of unpredictable behavior or unwanted behavior that occurs within parts of a program by forcing it to conform to type constraints.
Some examples of abstraction: lambda calculus, objects, structs, constructors and destructors, polymorphism, etc.
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).
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.
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:
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)
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.
Go through a C++ tutorial for the basics, and read something like Stroustrup's "The C++ programming language" to learn how to use the language idiomatically.
Do not believe people telling you you'd have to learn OOP independent of the language. The dirty secret is that what each language understands as OOP is by no means even vaguely similar in some cases, so having a solid base in, e.g. Java, is not really a big help for C++; it goes so far that the language go just doesn't have classes at all. Besides, C++ is explicitly a multi-paradigm language, including procedural, object oriented, and generic programming in one package. You need to learn how to combine that effectively. It has been designed for maximal performance, which means some of the lower-bit stuff shows through, leaving many performance-related decisions in the hands of the programmer, where other languages just don't give options. C++ has a very extensive library of generic algorithms, learning to use those is required part of the curriculum.
Start small, so in a couple year's time you can chuckle fondly over the naïveté of your first attempts, instead of pulling your hair out.
Don't fret over "efficiency," use virtual member functions everywhere unless there is a compelling reason not to. Get a good grip on references and const. Getting an object design right is very hard, don't expect the first (or fifth) attempt to be the last.
First, a little background on OOP and how C++ and other languages like Java differ.
People tend to use object-oriented programming for several different purposes:
Generic programming: writing code that is generic; i.e. that works on any object or data that provides a specified interface, without needing to care about the implementation details.
Modularity and encapsulation: preventing different pieces of code from becoming too tightly coupled to each other (called "modularity"), by hiding irrelevant implementation details from its users.
It's another way to think about separation of concerns.
Static polymorphism: customizing a "default" implementation of some behavior for a specific class of objects while keeping the code modular, where the set of possible customizations is already known when you are writing your program.
(Note: if you didn't need to keep the code modular, then choosing behavior would be as simple as an if or switch, but then the original code would need to account for all of the possibilities.)
Dynamic polymorphism: like static polymorphism, except the set of possible customizations is not already known -- perhaps because you expect the user of the library to implement the particular behavior later, e.g. to make a plug-in for your program.
In Java, the same tools (inheritance and overriding) are used for solving basically all of these problems.
The upside is that there's only one way to solve all of the problems, so it's easier to learn.
The downside is a sometimes-but-not-always-negligible efficiency penalty: a solution that resolves concern #4 is more costly than one that only needs to resolve #3.
Now, enter C++.
C++ has different tools for dealing with all of these, and even when they use the same tool (such as inheritance) for the same problem, they are used in such different ways that they are effectively completely different solutions than the classic "inherit + override" you see in Java:
Generic programming: C++ templates are made for this. They're similar to Java's generics, but in fact Java's generics often require inheritance to be useful, whereas C++ templates have nothing to do with inheritance in general.
Modularity and encapsulation: C++ classes have public and private access modifiers, just like in Java. In this respect, the two languages are very similar.
Static polymorphism: Java has no way of solving this particular problem, and instead forces you to use a solution for #4, paying a penalty that you don't necessarily need to pay. C++, on the other hand, uses a combination of template classes and inheritance called CRTP to solve this problem. This type of inheritance is very different from the one for #4.
Dynamic polymorphism: C++ and Java both allow for inheritance and function overriding, and are similar in this respect.
Now, back to your question. How would I solve this problem?
It follows from the above discussion that inheritance isn't the single hammer meant for all nails.
Probably the best way (although perhaps the most complicated way) is to use #3 for this task.
If need be, you can implement #4 on top of it for the classes that need it, without affecting other classes.
You declare a class called Shape and define the base functionality:
class Graphics; // Assume already declared
template<class Derived = void>
class Shape; // Declare the shape class
template<>
class Shape<> // Specialize Shape<void> as base functionality
{
Color _color;
public:
// Data and functionality for all shapes goes here
// if it does NOT depend on the particular shape
Color color() const { return this->_color; }
void color(Color value) { this->_color = value; }
};
Then you define the generic functionality:
template<class Derived>
class Shape : public Shape<> // Inherit base functionality
{
public:
// You're not required to actually declare these,
// but do it for the sake of documentation.
// The subclasses are expected to define these.
size_t vertices() const;
Point vertex(size_t vertex_index) const;
void draw_center(Graphics &g) const { g.draw_pixel(shape.center()); }
void draw_outline()
{
Derived &me = static_cast<Derived &>(*this); // My subclass type
Point p1 = me.vertex(0);
for (size_t i = 1; i < me.vertices(); ++i)
{
Point p2 = me.vertex(1);
g.draw_line(p1, p2);
p1 = p2;
}
}
Point center() const // Uses the methods above from the subclass
{
Derived &me = static_cast<Derived &>(*this); // My subclass type
Point center = Point();
for (size_t i = 0; i < me.vertices(); ++i)
{ center += (center * i + me.vertex(i)) / (i + 1); }
return center;
}
};
Once you do that, you can define new shapes:
template<>
class Square : public Shape<Square>
{
Point _top_left, _bottom_right;
public:
size_t vertices() const { return 4; }
Point vertex(size_t vertex_index) const
{
switch (vertex_index)
{
case 0: return this->_top_left;
case 1: return Point(this->_bottom_right.x, this->_top_left.y);
case 2: return this->_bottom_right;
case 3: return Point(this->_top_left.x, this->_bottom_right.y);
default: throw std::out_of_range("invalid vertex");
}
}
// No need to define center() -- it is already available!
};
This is probably the best method since you most likely already know all possible shapes at compile-time (i.e. you don't expect the user will write a plug-in to define his own shape), and thus don't need any of the whole deal with virtual. Yet it keeps the code modular and separates the concerns of the different shapes, effectively giving you the same benefits as a dynamic-polymorphism approach.
(It is also the most efficient option at run-time, at the cost of being a bit more complicated at compile-time.)
Hope this helps.
I had a really long post on this and decided it can be summed up much shorter. Canonically speaking, is it better to include a data member inside of a class as opposed to inheriting it? I found I can implement identical functions either way, but don't really know what caveats I should be aware of.
Code example
#include "KClass.h"
class KPC : public KCharacter {
private:
KClass MyClass;
};
versus
class KClass : public KCharacter {
};
class KPC : public KClass {
};
In the first example, anytime I needed something from the KClass data, I could access it via MyClass->
In the second class, class KPC would just directly access them since it would inherit the data members.
For specifics to my problem I guess I should detail the class' function.
D&D format. Each character has a class which would determine: weapon/armor proficiencies, bonus defenses, special ability i.e. defender has mark.
So to me, it made sense to inherit it. However, is class a more specific PC or is PC a specific kind of class? There are so many PCs in a game that aren't a specific class, actually class should inherit PC on that concept sense it's more 'specialized' form of a PC. So would I want to structure it in a way of KClass : KPC ?
It seemed easier to implement a Has-A at first, but now I'm second guessing it. Hence the reason why I'm here, asking this question.
Generally speaking, composition is better than inheritance. But it depends on what exactly you want to do. For the most part think:
IS A -> inheritance
HAS A -> composition
Inherit when you want/need to extend a base class. If you just need to use another class, just have an instance of it with the other class.
Side note, composition and aggregation are basically the same thing. Conceptually slightly different, in code, the same thing.
It's a matter of design and what you are trying to model. Scott Meyers' Effective C++ will note that public inheritance (the second example) models 'is-a', whereas composition (the first example) models 'is-implemented-in-terms-of' or 'has-a'. So, for your example, you should decide what role KClass is playing and which of these philosophies makes more sense. Just looking at the names KCharacter, KClass, and KPC, it's hard for me to tell their purposes.
It really depends on what you are trying to do. Yes, both achieve mechanically similar things, but the rule is "is-a" or "has-a" for deciding which way to go.
If KPC really "is-a" form of KClass, then you should use inheritance. This means that you are looking to solve a polymorphic problem - you have several items that are similar:
class AeroPlaneBase
{
...
};
class JetPlane : public AeroPlaneBase
{
...
};
class PropellerPlane : public AeroPlaneBase
{
...
};
class GliderPlane : public AeroPlaneBase
{
};
All of these planes can do similar things - but they behave slightly differently, so they need a different class to describe their behaviour.
Now, each plane will have zero or more "engines", so the class may have a "has-a" relationship to a PlaneEngine class. The glider, which is an engineless plane doesn't have any engine, the JetPlane can have 8, perhaps...
Likewise, in a roleplaying game, a player "is-a" Character (which is also the baseclass for Monster and the different derived forms of that), say, but "has-a" relationship with the Weapon class. The Character isn't a type of Weapon, but it has a weapon.
Conceptual
The concept of classes and objects is usually used to model "real" things. But let's put the cart before the horse.
The transfer of the inheritance concept to the real world would be (like others said) an IS A-relation.
A TFT is a screen
A Fox is an Animal
...
The composition is, in contrast, usually considerd as HAS A-relation.
A PC has a CPU
A knife has a blade
...
So if you want to model the latter in object-oriented programming, make use of composition. In case of the former concept, use inheritance.
Examples
Composition > Inheritance
Examples always tend to come naturally to me. So I'll try to illustrate it a bit further. (No encapsulation here, sorry. ;))
Consider motorvehicles, respectively cars. The tend to have an engine, which has a specific sound.
struct Engine
{
void sound (void) const { std::cout << "BROOOM" << std::endl; }
void open_valve (void) { /* ... */ }
};
Engines also can perform certain engine-specific tasks.
Now we can have both specified options to include the engine into a car: inheritance or composition.
struct Car_A : public Engine { };
At the first moment, this seems appropriate. We don't need to reprovide sound() since a car (in the first approximation) just sounds like an engine.
Car_A a_car;
a_car.sound(); // mycar sounds like a car!
But the noise is not very realistic: No tread noise, no air draft. So we can just shadow the underlying method and define:
struct Car_A : public Engine
{
void sound (void) const
{
std::cout << "tread noise + air draft" << std::endl;
Engine::sound();
}
};
We still have a slight problem.
a_car.open_valve(); // ?
The concept of valves is part of the engine but not part of the car but we can use this method on the car.
The car has an engine but it isn't one.
We could switch to private inheritance now but the method would still be present, although not accessible.
Another (less conceptual) issue can be seen when using pointers of the types:
Engine * pointer_to_engine(new Car_A); // works
An engine that actually is a car? "(Suspected) Engines" exhibiting car behaviour and vice versa? Well that doesn't look like the way to do things here.
Let's look at composition instead:
struct Car_B
{
void sound (void) const
{
std::cout << "tread noise + air draft" << std::endl;
engine.sound();
}
void open_door (void) { /* ... */ }
Engine engine;
};
That's how things are supposed to be: A car that has a[n] (member) engine that sounds like an engine and contributes to the sound of the car and no methods are present in the car that are not part of the concept of a car.
Car_B b_car;
b_car.sound(); // still sounds like a car!
b_car.engine.open_valve(); // meaningful for an engine!
Here we have a case where composition is superior.
The "real" situation is modeled.
All concepts keep their validity. (No unintended behaviour.)
Inheritance > Composition
Now we add another Concept in our example: a vehicle.
struct Wheel {};
struct Motorvehicle
{
virtual void sound (void) const { engine.sound(); }
Engine engine;
std::vector<Wheel> wheels;
};
A motorvehicle is driven by an engine, so it knows to emmit engine sound.
However, the abstract vehicle has no clue of how many wheels its concrete objects will have have (motorcycle? car?) or how its shape is formed, so it can't tell anything about tread noise and air draft.
This time we look at composition first (miracle miracle...):
struct Car_C
{
void sound (void) const
{
std::cout << "tread noise + air draft" << std::endl;
vehicle.sound();
}
Motorvehicle vehicle;
};
Looks legit, doesn't it?
Car_C c_car;
c_car.sound(); // correct sound!
c_car.vehicle.sound(); // what the hell is "the vehicle of a car"?
c_car.wheels.... // error the car has no wheels?!
"Pretending" that wheels are part of the car will require us to add an additional function for our car. If we use inheritance instead, this coherency comes from scratch.
struct Car_D
: public Motorvehicle
{
void sound (void) const
{
std::cout << "tread noise + air draft" << std::endl;
Motorvehicle::sound();
}
};
The observable behaviour of Car_D is more like you would expect it to be.
Car_D d_car;
d_car.sound(); // correct sound!
d_car.wheels.[...] // valid, our car has wheels!
Conclusion
The consideration, whether to use inheritance or composition is not always as easy as in my examples but you should try to weight up and choose the concept that performas better in reflecting the desired behaviour.
If the designated base class describes an abstract generalization of the derived class, this is a good hint for inheritance.
I'm trying to learn C++, Thanks to this article I find many similarity between C++ and Python and Javascript: http://www.cse.msu.edu/~cse231/python2Cpp.html
But I can't understand C++ Classes at all, they looks like Javascript prototypes, but not that easy.
For example:
//CLxLogMessage defined in header
class myLOG: public CLxLogMessage{
public:
virtual const char * GetFormat (){
return "Wavefront Object";
}
void Error (const std::string &msg){
CLxLogMessage::Error (msg.c_str ());
}
void Info (const std::string &msg){
CLxLogMessage::Info (msg.c_str ());
}
private:
std::string authoringTool;
};
Question: What is this Public/Private stuff at all!?
Edit: To be honest, I more enjoy C++ than Python, because I can learn truth meaning of everything, not simple automated commands, for example I preferred to use "int X" rather than "X" alone.
Thanks
myLOG is the name of the class. It inherits (look it up2) from CLxLogMessage and has the functions GetFormat (which is virtual and can be overridden by subclasses and called through base class pointers, look it up2), Error, and Info. It has the data member authoringTool which is a string.
The public and private stuff is access specifiers. Something in the private section can only be used by the class's member functions, and stuff in the public section can be used by anybody. There is another type of section called protected which means that only a class and its subclasses can access it, but nobody else1.
If you start adding stuff to a class without setting an access level first, it defaults to private.
You can have as many public, private, and protected sections as you want, in any order.
You need these different protection levels because you don't want other people messing with your data when you don't know about it. For example, if you had a class representing fractions, you wouldn't want someone to change the denominator to a 0 right under your nose. They'd have to go through a setter function which would check that the new value was valid before setting the denominator to it. That's just a trivial example though. The fact that Python does not have these is a shortcoming in the language's design.
All your questions would be answered if you had read a C++ book. There is no easy way out with C++. If you try to take one, you'll end up being a horrible C++ programmer.
1 You can let somebody else access private and protected members by declaring them as friends (look it up2).
2 Sorry for saying "look it up" so much, but it's too much information for me to put here. You'll have to find a good resource for these kinds of things.
Even though there's no way to give a comprehensive answer or anything near that, maybe think about it like this: classes are types. Consider this:
int n;
Here "int" is the name of a type, and "x" is a variable of type "int". There are basic types in C++, like "int", "char", "double". Now we can also make new, compound types from old types:
struct Foo
{
int n;
char c;
double d;
};
This defines a new type called "Foo", and Foo x; makes a new variable of that type. Now we can add some magic to the type "Foo":
class Foo
{
int n;
double d;
public:
Foo() : n(20), d(0.5) { } // "constructor"
};
The keywords struct and class almost mean the same thing, so we still have a compound type that has two member variables, n and d. However, this type also has a member function, and this one gets called every time you create a new Foo object. So when you say, Foo x;, then this variable's member value x.n will be set to 20 and x.d will be set to 0.5.
So that's that in a nutshell: Classes are types with built-in magic. And you are the magician.
The private and public is to do with data encapsulation, it means you can change the implementation of the class without affecting how it is used. I suggest reading up on some of the theory of object orientation.