Fundamentals 38 min read

Object-Oriented Analysis and Design: Exception Handling, RAII, and Error‑Code Practices

The article explains how object‑oriented analysis and design should treat exceptions by using RAII for automatic resource cleanup, avoiding error‑code patterns, and adopting a throw‑by‑default policy with strongly‑typed exceptions, illustrating concepts with C++ and VB.NET examples and framework design recommendations.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Object-Oriented Analysis and Design: Exception Handling, RAII, and Error‑Code Practices

This article is the second part of the "Exception Thinking" series and discusses how to treat exceptions in object‑oriented analysis and design (OOAD). It starts with a brief introduction that software inevitably encounters exceptions and that systematic handling of these exceptions is essential for developers.

1. Object‑Oriented Analysis and Design

1.1 Attributes, Methods, and Events

The author explains the five core OO concepts (object/class, encapsulation, inheritance, interface, polymorphism) and then focuses on the three basic constituents of an object: attributes (state), methods (behaviour), and events (state‑change notifications). Code examples in C++ and VB.NET illustrate how attributes are accessed via getters/setters and how events can be used to decouple components.

class Car {
public:
  void set_finish_time(std::chrono::system_clock::time_point t) { finish_time_ = t; }
  std::chrono::system_clock::time_point finish_time() const { return finish_time_; }
  int year() const { return std::localtime(&std::chrono::system_clock::to_time_t(finish_time_))->tm_year + 1900; }
private:
  std::chrono::system_clock::time_point finish_time_;
};

It also shows a VB.NET example of defining delegates and raising events when a property changes.

Delegate Sub BeforeSpeedChange(ByRef sender As Object, ByVal oldSpeed As Integer, ByRef speed As Integer)
Public Event OnBeforeSpeedChange As BeforeSpeedChange
Public Property Speed() As Integer
  Get
    Return _speed
  End Get
  Set(value As Integer)
    RaiseEvent OnBeforeSpeedChange(Me, _speed, value)
    _speed = value
    RaiseEvent OnAfterSpeedChange(Me, _speed)
  End Set
End Property

1.2 Resource Acquisition Is Initialization (RAII)

RAII ties resource acquisition to object construction and resource release to destruction, guaranteeing exception‑safe cleanup. The article provides a C++ example that uses a mutex and a file stream inside a function; both are automatically released when the scope ends, even if an exception is thrown.

void WriteToFile(const std::string& message) {
  static std::mutex mutex;
  std::lock_guard
lock(mutex);
  std::ofstream file("example.txt");
  if (!file.is_open()) {
    throw std::runtime_error("unable to open file");
  }
  file << message << std::endl;
}

The author argues that RAII should be the default in domain‑driven design because it naturally maps UML sequence diagrams to code.

1.3 Exception‑Centric Thinking

The article critiques the widespread use of error codes (return‑int) in large C/C++ systems, listing perceived advantages (clarity, compatibility, performance) and many disadvantages (usability, readability, consistency, violation of natural language semantics, forced checks). It shows typical error‑code‑centric code and explains how error‑code propagation leads to tangled logic and loss of domain intent.

// lib component
int foo_in_lib() { if (/* error */) return LIB_ERROR; return 0; }
// business layer
int foo_in_exe() { if (foo_in_lib()) return MY_TRANSLATED_ERROR; return 0; }
// framework layer
int foo_in_framework() { int ret = foo_in_exe(); if (ret == LIB_ERROR) { /* retry */ } return ret; }

Through benchmarks the author demonstrates that the performance penalty of using exceptions is negligible compared with network or serialization costs, and that forcing error‑code checks (especially without [[nodiscard]]) creates maintenance hazards.

2. Framework Designer’s Thoughts

The author advises framework authors to think about how their features will be used, not just about language features. He discusses the pitfalls of error‑code handling in a large payment system, the need for a unified error‑code management platform, and the importance of propagating domain‑level exceptions rather than low‑level error numbers.

Key recommendations include:

Use strongly‑typed error enums or standard exception types instead of raw integers.

Leverage RAII and scope‑exit utilities (e.g., BOOST_SCOPE_EXIT ) to guarantee cleanup.

Design APIs so that constructors and setters can throw on invalid state, preserving object invariants.

Adopt a “throw‑by‑default” policy: if a function can fail, let it throw; callers handle or let the exception unwind.

Finally, the article points to upcoming posts that will explore concrete business‑level exception‑handling cases.

Exception HandlingC++RAIIError CodesObject-Oriented Design
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.