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.
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 Property1.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.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.