OO Programming  «Prev  Next»
Lesson 2 Traditional (Procedural) Programming
Objective Explain procedural programming, show top-down decomposition with code, and contrast it with an OOP solution in C++.

Traditional Approach: Procedural Programming

Procedural (structured) programming organizes software as a sequence of steps and subroutines. You begin with a top-level function, then decompose the task into smaller, testable procedures. This style excels when the workflow is linear and data transformations are clear.

Top-down decomposition

  1. Define the problem and inputs/outputs.
  2. Write a high-level procedure that expresses the main flow.
  3. Refactor repeated or complex steps into subroutines.
  4. Test each unit independently.

Procedural example (C): converting an image using explicit steps.

/* C (procedural): PNG → JPEG pipeline */
int convert_image(const char* inPath, const char* outPath) {
    Image img = read_png(inPath);
    if (!img.ok) return -1;
    Image ycbcr = to_ycbcr(img);
    return write_jpeg(ycbcr, outPath, /*quality*/ 85);
}

Here the program flow is explicit and easy to follow. Changes to the pipeline (e.g., switch PNG → WebP) typically require editing the control flow or adding new functions that the main procedure calls.

Where Procedural Shines

Comparing With an OOP Design in C++

OOP models the entities in the problem domain as classes with state and behavior. Instead of hard-wiring steps, you program to an abstraction and swap behaviors with polymorphism.

OOP example (C++): use a strategy to change formats without changing the pipeline.

// C++ (OOP): pluggable converters via an interface
struct Image { /* pixels, size, etc. */ };

struct ImageConverter {
    virtual ~ImageConverter() = default;
    virtual Image load(const std::string& in) = 0;
    virtual bool save(const Image& img, const std::string& out) = 0;
};

struct PngToJpeg : ImageConverter {
    Image load(const std::string& in) override { return readPng(in); }
    bool  save(const Image& img, const std::string& out) override {
        return writeJpeg(toYCbCr(img), out, 85);
    }
};

bool convert(ImageConverter& conv, const std::string& in, const std::string& out) {
    Image img = conv.load(in);
    return conv.save(img, out);
}

Now the pipeline stays stable while behaviors vary. To support WebP, implement struct PngToWebp : ImageConverter—no edits to convert(...).

Naming Conventions

There is no universal C++ style, but consistency matters. Common choices:
  • Types: PascalCase (e.g., ImageConverter).
  • Functions/methods: lower_snake_case or lowerCamelCase (pick one per project).
  • Constants: SCREAMING_SNAKE_CASE.
Use descriptive verbs for functions (read_png(), write_jpeg()), and nouns for types (Image, Buffer).

Pros & Cons (Reality-Checked)

  • Procedural pros: minimal ceremony, great for linear workflows, easier to trace step-by-step, tiny binaries.
  • Procedural cons: cross-cutting concerns can fragment across functions; large programs become hard to reason about.
  • OOP pros: encapsulation, substitution via interfaces, easier to extend without editing existing code (Open/Closed Principle).
  • OOP cons: over-engineering risk; dynamic dispatch and indirection can complicate debugging if overused.

Performance note: Modern compilers inline aggressively; OOP is not “inherently slow.” Costs come from poor design (excess allocation, deep hierarchies, virtual hotspots)—not from the paradigm itself.

When to Choose Which

Key takeaway: Use the simplest model that cleanly expresses the problem. Start procedural for small tasks; graduate to OOP when variability, reuse, or growth demand it.


SEMrush Software