OOPConcepts
OOPConcepts
Programming
Richard Berger
richard.berger@jku.at
Johannes Kepler University Linz
What this talk is about
• Introduction to Objects & Classes
• Building objects
• Composition
• Encapsulation
• Inheritance
• Polymorphism
• Best practices
• Recommendations
• Design Patterns
• Conclusion
• Pro & Cons of OOP
What is Object-Oriented Programming?
• OOP started in 1960s (Simula, SmallTalk)
• Using objects as basic unit of computation message Object
• Allocation
• Allocate enough memory to store object data/state Initialization
• Initialization
• Set an initial object state
• Usage Usage
• Interact with objects through methods
• Access and modify object data
• Cleanup Cleanup
• Make sure that everything is in order before deletion
• Deletion
• Memory is freed, object ceases to exist Deletion
Allocation
Allocation Initialization
+ Fast
+ Automatic cleanup
- Not persistent
- “Limited” storage
Allocation
Allocation Initialization
+ Persistent
+ “Infinite” storage
- Slow
- Manual cleanup
Allocation
Initialization Initialization
class Vector2D {
public: Usage
Vector2D(){
Constructors
x = 0.0;
• special methods which Cleanup
y = 0.0;
initialize an instance of a class
}
Vector2D(double x1, double y1){
• multiple variants with Deletion
x = x1;
different parameters possible
y = y1;
• initialize member variables
}
}; Vector2D v1();
Vector2D v2(10.0, 20.0);
Usage Initialization
Usage
Stack Objects Heap Objects
Vector2D v; Vector2D * pv = new Vector2D();
Cleanup
// access members // access members
v.x = 10.0; pv->x = 10.0;
Deletion
v.y = 20.0; pv->y = 20.0;
Cleanup Initialization
class MyVector {
Destructor Usage
double * data;
public:
• Cleanup before destruction
MyVector(int dim){ • Free any acquired resources
Cleanup
data = new double[dim]; (file handles, heap memory)
}
Deletion
~MyVector() {
delete [] data;
}
};
Allocation
Deletion Initialization
Deletion Initialization
delete v;
} objects on heap must be deleted explicitly
Static class members
Static Member Variable
• Only a single instance of this variables exists
class Point2D • Much like a global variable
{ • Can be accessed by all instances
static int numPoints = 0; • Access by class name using ::, not instance
public:
int identifier; // Definition in a single .cpp file
double x; int Point2D::numPoints = 0;
double y;
// access without having an instance
cout << Point2D::numPoints << endl;
Point2D(double x1, double y1) {
identifier = numPoints++;
x = x1; Static Method
y = y1; • A method which is not applied on a class instance, but on
} the class itself
• Much like a global function
static void reset_count() { • Can only access and modify static member variables
numPoints = 0;
} // call static method, no instance required
Point2D::reset_count();
};
II. Building objects
Composition, Encapsulation, Inheritance, Polymorphism
Low-Level Model of an Airplane
Abstractions Engine 1
Engine 3
Engine 4
Rudder
Longitude
Gear
Composition
• natural way of creating new
class Boeing747 objects is by building them out
{ of existing objects.
Engine engine1;
• Complex systems are composed
Engine engine2;
out of simpler sub-systems
Engine engine3;
Engine engine4;
Gear frontGear;
Gear backGear; Engine Engine Engine Engine Gear Gear …
…
};
Boing747
Encapsulation gearUp
Boeing747
speed
gearDown altitude
class Boeing747{
private:
hidden
Gear gear;
• View objects as black box
public:
void gearUp() { • Don’t operate directly on internal data
// update physics of an object
… • Implementation details are hidden behind
gear.up();
interface
} • make member variables private
visible
void gearDown() { • Use methods of the interface to perform
// update physics
certain actions
… • Some languages, e.g. C++ and Java, help
gear.down(); enforce this through specifiers: public,
} private, protected
};
Problems without Encapsulation
void Boeing747::gearDown() {
class Boeing747{ if(gear.isUp()) {
public: totalAirFriction += 20.0;
double totalAirFriction; gear.down();
Gear gear; }
void gearUp(); }
void gearDown(); void Boeing747::gearUp() {
double speed(); if(gear.isDown()) {
}; totalAirFriction -= 20.0;
gear.down();
Boeing747 * a = new Boing747(); }
}
Boeing747 Cessna206h
Type hierarchies and Inheritance
class Boeing747 { class Cessna206h {
float longitude, latitude; float longitude, latitude;
float heading, speed; float heading, speed;
float altitude; float altitude;
... ...
public: public:
void fullThrottle() { void fullThrottle() {
engine1.maxThrottle(); engine1.maxThrottle();
engine2.maxThrottle(); speed = …;
engine3.maxThrottle(); }
engine4.maxThrottle(); }
speed = …;
}
}
class Airplane {
Inheritance public:
float longitude, latitude;
float heading, speed; Common structure
float altitude;
};
Inheritance public:
float longitude, latitude;
float heading, speed;
float altitude;
virtual void fullThrottle(); Common interface
};
~SomeClass() {
delete dataSet; Won’t do anything if data set is never created
}
int getDataSet() {
if (!dataSet) {
dataSet = new LargeDataSet(); Create data set set on-demand
}
return dataSet;
}
}
Base class destructors should be virtual
• If you want to allow deletion of objects by using their base class,
make sure their destructor is virtual
class Base { class Base2 {
public: public:
~Base(); virtual ~Base2(){}
} }
Factory Methods
• Create objects without having to know specific language type
• Circumvent limitations of constructors
• No return result
only exceptions
• Constrained naming
e.g. can’t have two constructors with same parameter types
• Statically bound creation
there is no dynamic binding for constructors, you have to know which type you want
to instantiate
• No virtual constructors
• Factory methods can range from very simple implementations to complex
selection schemes
Creational Patterns
return nullptr;
}
// parse user input This factory
ShapeFactory factory;
string selectedShape = getUserInput(); implementation is hard
coded. But you can easily
// create object at runtime write an extensible
IShape * new_object = factory.createShape(selectedShape);
factory.
Structural Patterns
Adapter
• Used to make an object of one type compatible to another
• Typical use case:
• You defined your own types of objects with a certain interface
• You want to use an external library to manipulate your objects
• However the interface expected by library is different to the one you used
• Instead of rewriting you code, you can create an Adapter class, which
maps one interface to another.
Structural Patterns
Adapter – Example
class ForceComputation { class LegacyClass {
public: public:
virtual void compute_force(Vector3D & force); virtual void compute_force(double * force);
}; };
Strategy
• Used to keep parts of a larger implementations replacable
• You define a common interface to do a certain task
• Any class which implements that interface can be used in larger
implementation
• Allows you to exchange object of that interface during runtime
• Typical use case:
• Define a common interface to get data
• Interface can be implemented by classes which use files, databases, web
services, etc.
Behavioral Patterns
Strategy - Example
class IRandomNumberGenerator { class DiceRoll : public IRandomNumberGenerator {
public: public:
double getNextDouble() = 0; double getNextDouble() {
} // guaranteed to be random,
// determined with a fair dice roll
return 4;
class MyUncrackableEncryption { }
IRandomNumberGenerator * random; }
void setRandomNumberGenerator(IRandomNumberGenerator * r) {
random = r;
}