#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct IObject {
virtual ~IObject() = default;
/// Returns the name of the object.
virtual std::string getName() const = 0;
/// Prints a textual description of the object.
virtual void describe() const = 0;
/// Eats another object.
virtual bool eat(IObject* other) = 0;
};
/// `bark()` has volume in decibels.
/// `walk()` now takes coordinates (x, y).
struct IDog : IObject {
/// Bark with given loudness (in dB).
virtual void bark(double volumeDb) const = 0;
/// Walk to a given position (x, y) in meters.
virtual void walk(double x, double y) const = 0;
};
/// `swim()` receives a depth parameter (in meters).
struct IFish : IObject {
/// Swim at a given depth (in meters).
virtual void swim(double depthMeters) const = 0;
};
struct Bulldog : IDog {
std::string getName() const override { return "Bulldog"; }
void describe() const override {
std::cout << getName() << " — a strong and loyal dog.\n";
}
void bark(double volumeDb) const override {
if (volumeDb < 30.0)
std::cout << "Bulldog barks softly (" << volumeDb << " dB).\n";
else if (volumeDb < 70.0)
std::cout << "Bulldog barks normally (" << volumeDb << " dB).\n";
else
std::cout << "Bulldog barks very loudly (" << volumeDb << " dB)!\n";
}
void walk(double x, double y) const override {
std::cout << "Bulldog walks to position (" << x << ", " << y << ").\n";
}
};
struct Chihuahua : IDog {
std::string getName() const override { return "Chihuahua"; }
void describe() const override {
std::cout << getName() << " — a tiny but energetic dog.\n";
}
void bark(double volumeDb) const override {
if (volumeDb < 20.0)
std::cout << "Chihuahua yaps quietly (" << volumeDb << " dB).\n";
else if (volumeDb < 50.0)
std::cout << "Chihuahua yaps sharply (" << volumeDb << " dB).\n";
else
std::cout << "Chihuahua squeals at max power (" << volumeDb << " dB)!\n";
}
void walk(double x, double y) const override {
std::cout << "Chihuahua runs quickly to (" << x << ", " << y << ").\n";
}
};
struct Goldfish : IFish {
std::string getName() const override { return "Goldfish"; }
void describe() const override {
std::cout << getName() << " — a small shiny fish.\n";
}
void swim(double depthMeters) const override {
if (depthMeters < 1.0)
std::cout << "Goldfish swims near the surface (" << depthMeters << " m).\n";
else if (depthMeters < 5.0)
std::cout << "Goldfish swims deeper (" << depthMeters << " m).\n";
else
std::cout << "Goldfish rarely swims that deep (" << depthMeters << " m)!\n";
}
};
struct Shark : IFish {
std::string getName() const override { return "Shark"; }
void describe() const override {
std::cout << getName() << " — a large predator fish.\n";
}
void swim(double depthMeters) const override {
std::cout << "Shark swims powerfully at " << depthMeters << " meters depth.\n";
}
};
std::vector<std::shared_ptr<IObject>> internalMakeObjects()
{
std::vector<std::shared_ptr<IObject>> objects;
objects.push_back(std::make_shared<Bulldog>());
objects.push_back(std::make_shared<Chihuahua>());
objects.push_back(std::make_shared<Goldfish>());
objects.push_back(std::make_shared<Shark>());
return objects;
}
std::shared_ptr<IObject> externalAlgorithm(std::vector<std::shared_ptr<IObject>>& objects);
void processAnimal(const std::shared_ptr<IObject>& obj)
{
if (auto dog = std::dynamic_pointer_cast<IDog>(obj)) {
dog->bark(25.0);
dog->walk(1.0, 2.0);
} else if (auto fish = std::dynamic_pointer_cast<IFish>(obj)) {
fish->swim(0.5);
}
}
int main()
{
// Creates not only Dogs and Fishes, but objects of different types like cheburashkas.
std::vector<std::shared_ptr<IObject>> objects = externalMakeObjects();
std::cout << "--- Descriptions ---\n";
for (const auto& obj : objects)
obj->describe();
std::cout << "\n--- Actions ---\n";
// But we can work only with Dogs and Fishes.
for (const auto& obj : objects) {
processAnimal(obj);
}
{
std::shared_ptr<IObject> found = externalAlgorithm(objects);
processAnimal(found);
}
}