Object-Oriented Programming with C++

Chapter 2
Object-Oriented Thinking and Modeling

Bachelor Program in Mechatronics Engineering
Object-Oriented Programming with C++   •   Bachelor

Object-Oriented Thinking and Modeling

Introduction and Context

Object-oriented programming (OOP) is not merely a collection of C++ language features. It is a way of thinking about complex systems as cooperating objects. Each object represents an identifiable entity, owns data describing its condition, and provides operations through which other objects may request services. This perspective is particularly valuable in mechatronics, where software models physical devices such as motors, sensors, actuators, controllers, robots, and communication interfaces.

A mechatronic product combines mechanical structure, electronics, control logic, embedded software, and often human interaction. Without a suitable modeling approach, these elements can become tightly coupled and difficult to test or modify. Object-oriented thinking helps engineers divide a system into meaningful components, assign responsibilities, protect internal details, and define controlled interactions.

Consider an automated conveyor. Its software may contain a motor controller, optical sensor, emergency-stop subsystem, item detector, timing mechanism, and supervisory controller. A procedural program might organize the system as a long sequence of functions and shared variables. An object-oriented model instead identifies components with distinct responsibilities and collaborations. The resulting design is easier to understand because its structure reflects the system being engineered.

Learning Objectives

By the end of this chapter, you should be able to:

  1. Explain abstraction, encapsulation, modularity, identity, state, behavior, and responsibility.
  2. Distinguish classes from objects and describe their relationship in C++.
  3. Model physical and software components as objects with appropriate interfaces.
  4. Analyze object collaboration using messages, associations, aggregation, and composition.
  5. Apply responsibility-driven design to a mechatronic application.
  6. Evaluate object-oriented models for cohesion, coupling, safety, maintainability, and testability.

1. From Physical Systems to Software Models

A model is a deliberately simplified representation of a real or proposed system. A software model does not reproduce every physical detail. Instead, it retains the information needed for a particular purpose. A motor model for a control algorithm may expose angular speed and commanded voltage, whereas a maintenance application may focus on operating hours, fault history, and service status.

Modeling therefore requires selection. Engineers ask:

  • What entities are important?
  • What information must each entity remember?
  • What actions can it perform?
  • Which actions may other components request?
  • What rules must always remain true?
  • How should components communicate?

The same physical device may be represented by different software objects in different contexts. This is not inconsistency; it is purposeful abstraction.

Objects as system participants

An object has four closely related characteristics:

  • Identity: what makes it a distinct entity from other objects.
  • State: the values describing its current condition.
  • Behavior: the operations it can perform or the responses it can produce.
  • Responsibilities: the services and decisions it is expected to provide.

For example, two temperature sensors may be instances of the same class. Their identity differs because one is installed near a motor and the other near a bearing. Their state may include the latest reading, calibration status, and communication condition. Their behavior may include reading a value, checking validity, and reporting a fault.

An object is not simply a data record. It combines data with the operations that govern access to and change of that data. This combination is central to reliable system design.

2. Core Principles of Object-Oriented Thinking

2.1 Abstraction

Abstraction is the selective representation of essential characteristics while suppressing irrelevant detail. A class named Motor might provide start(), stop(), and setSpeed() without requiring a user of the class to know the pulse-width modulation algorithm, transistor switching sequence, or encoder signal-processing method.

Good abstraction is purpose-dependent. An abstraction is not necessarily a complete description of reality. It is successful when users can accomplish their tasks without depending on unnecessary implementation details.

In C++, a class interface expresses an abstraction through public member functions and selected public types. The implementation can be changed while preserving the interface, provided its documented behavior remains compatible.

2.2 Encapsulation

Encapsulation means that an object controls access to its internal representation. In C++, data members are commonly declared private, while permitted operations are exposed through public member functions.

class Motor {
private:
    double speedRpm_ {0.0};
    bool enabled_ {false};

public:
    void enable() { enabled_ = true; }
    void disable() { enabled_ = false; speedRpm_ = 0.0; }

    bool setSpeed(double rpm) {
        if (!enabled_ || rpm < 0.0 || rpm > 3000.0) {
            return false;
        }
        speedRpm_ = rpm;
        return true;
    }

    double speed() const { return speedRpm_; }
};

The Motor object enforces two rules: a disabled motor cannot accept a positive speed command, and the requested speed must lie within the allowed range. If speedRpm_ were public, any caller could assign an invalid value and bypass these rules.

Encapsulation is therefore more than information hiding. It protects invariants—conditions that should remain true throughout an object's valid lifetime. Examples include a nonnegative battery charge, a closed safety gate before actuator activation, or a commanded speed within a hardware limit.

2.3 Modularity

Modularity divides a system into relatively independent units with defined interfaces. A module may be a class, subsystem, library, or hardware abstraction layer. A modular design allows one component to be developed, tested, replaced, or reused with limited impact on others.

Two qualities are especially important:

  • High cohesion: the responsibilities within a module strongly belong together.
  • Low coupling: a module depends on as few details of other modules as possible.

A class that reads a sensor, formats a network packet, controls a motor, and writes a user interface has low cohesion. Separating these concerns generally improves comprehension and testing.

Modularity does not mean that components never depend on one another. Collaboration is necessary. The goal is to make dependencies explicit, stable, and small.

2.4 Identity

Identity distinguishes one object from another even when their values are identical. Two encoders may both report 1024 counts per revolution, yet they are different physical devices installed at different locations. Identity may be represented by an object’s memory identity, a device address, a serial number, or a domain identifier.

Identity matters when objects have continuity over time. A maintenance record belongs to a particular motor, not merely to any motor having the same current speed.

2.5 State

State is the set of values that characterize an object at a particular time. A robot arm controller might have states such as Uninitialized, Ready, Moving, Fault, and EmergencyStop. State can be represented by data members, but it also includes relationships and conditions relevant to behavior.

A state transition occurs when an event causes the object to move from one valid state to another. For example:

Ready → Moving after a valid motion command

Moving → Fault after an encoder failure

Moving → EmergencyStop after activation of the safety circuit

Explicit state modeling is preferable to scattered Boolean flags when combinations of flags could create contradictory conditions. A state machine can make permitted transitions visible and testable.

2.6 Behavior

Behavior describes what an object does in response to operations, events, or changing conditions. Behavior includes normal actions, returned results, state changes, exceptions or error reports, and timing considerations.

For a proximity sensor, behavior may include sampling, filtering, validity checking, and notification of a detected object. The sensor's behavior should specify what happens when communication fails or data is out of range. In embedded systems, timing and failure behavior are part of the design—not optional details.

2.7 Responsibility

A responsibility is an obligation assigned to a class or object. Responsibilities commonly fall into two categories:

  • Knowing: maintaining information or providing derived information.
  • Doing: performing an operation, changing state, or coordinating another action.

A TemperatureSensor may be responsible for obtaining a measurement and reporting validity. A SafetyController may be responsible for deciding whether motion is permitted. A Motor may be responsible for enforcing its own speed and enable constraints. Assigning responsibility to the object that has the necessary information usually reduces coupling.

A controller should coordinate a process, but it should not manipulate every internal variable of every device. Excessive central control produces a “god object” that is difficult to test and modify.

3. Classes, Objects, and C++ Structure

A class is a type definition describing possible state and behavior. An object is a particular instance of that class. The class is a design or type; the object is a runtime entity.

class Sensor {
public:
    explicit Sensor(int channel) : channel_(channel) {}

    int channel() const { return channel_; }
    double reading() const { return reading_; }
    void update(double value) { reading_ = value; }

private:
    int channel_;
    double reading_ {0.0};
};

Sensor motorTemperature(1);
Sensor bearingTemperature(2);

Here, Sensor describes the common structure and behavior. motorTemperature and bearingTemperature are distinct objects with different identities and channel values.

Constructors and invariants

A constructor establishes an object's initial valid state. It should reject or prevent invalid initialization where practical. For example, a sensor channel might be required to lie within a hardware-defined range. A constructor should not merely copy values; it should establish the assumptions on which later member functions rely.

Public and private interfaces

A public member function is part of the class contract. Changing it may affect many clients. Private implementation details can change with less impact. A disciplined class interface should be small, meaningful, and expressed in domain terms rather than exposing internal data structures unnecessarily.

const and observable behavior

A member function marked const promises not to modify the observable state of the object. Functions such as speed() and channel() should normally be const. This communicates intent and enables use with const objects.

Value objects and entity objects

A value object is defined mainly by its value. Examples include a distance, angle, or three-dimensional position. Two positions with the same coordinates may be treated as equivalent.

An entity object has a continuing identity. A particular motor, robot, or sensor remains the same entity while its state changes. Confusing these categories can produce inappropriate equality tests and ownership designs.

4. Object Collaboration

Useful systems are not collections of isolated objects. Objects collaborate by sending messages, which in C++ commonly means calling member functions, passing values, or using callbacks and events.

Suppose a conveyor controller receives a start request. It may ask a safety object whether motion is permitted, ask a sensor whether an item is present, and command a motor to move. The controller coordinates the use case; it should not directly alter the motor's private state.

class SafetyGate {
public:
    bool motionAllowed() const { return closed_ && healthy_; }
    void setClosed(bool value) { closed_ = value; }
    void setHealthy(bool value) { healthy_ = value; }
private:
    bool closed_ {false};
    bool healthy_ {true};
};

class ConveyorController {
public:
    ConveyorController(Motor& motor, const SafetyGate& gate)
        : motor_(motor), gate_(gate) {}

    bool start(double speed) {
        if (!gate_.motionAllowed()) {
            return false;
        }
        motor_.enable();
        return motor_.setSpeed(speed);
    }

private:
    Motor& motor_;
    const SafetyGate& gate_;
};

The controller depends on the public behavior of Motor and SafetyGate, not their private representation. The reference members indicate collaboration with objects managed elsewhere; they do not necessarily indicate ownership.

Association, aggregation, and composition

An association is a general relationship in which objects know about or use one another. A controller is associated with a motor.

Aggregation describes a whole–part relationship in which parts can exist independently of the whole. A robot workstation may aggregate tools that can be removed and used elsewhere.

Composition is stronger: the whole owns the lifetime of its parts. A RobotArm may contain joints created and destroyed with the arm. In C++, composition is often expressed using direct data members or smart pointers with exclusive ownership.

These terms describe design semantics, not merely syntax. A pointer does not automatically mean aggregation, and a data member does not always communicate all lifecycle assumptions. Ownership should be explicit in the design and implementation.

Inheritance and polymorphism

Inheritance can express an “is-a” relationship and support substitutability. For example, a TemperatureSensor and a PressureSensor might implement a common Sensor interface. Polymorphism allows a client to use a common interface while the actual behavior varies by object.

Inheritance should not be used merely to reuse code. If the derived type cannot validly be used wherever the base type is expected, the relationship is conceptually wrong. Composition—building an object from other objects—is often more flexible.

For hardware-independent software, an interface can separate policy from mechanism:

class MotorDriver {
public:
    virtual ~MotorDriver() = default;
    virtual bool enable() = 0;
    virtual bool commandSpeed(double rpm) = 0;
    virtual void disable() = 0;
};

A real driver and a test double can implement this interface. The controller can then be tested without connecting a physical motor. In safety-critical systems, however, abstraction must not hide essential timing, diagnostic, or failure guarantees.

5. Responsibility-Driven Modeling Process

A practical modeling process can proceed as follows.

Step 1: Define the system boundary

State what the software controls, observes, or records. Identify external actors, hardware devices, operators, and neighboring systems. A clear boundary prevents the model from becoming an unmanageable description of the entire organization or machine.

Step 2: Identify candidate objects

Extract nouns from requirements, but do not accept every noun as a class. Candidates may include Motor, Encoder, SafetyGate, MotionCommand, FaultLog, and ConveyorController.

Step 3: Identify responsibilities

For each candidate, ask what it must know and do. Assign a responsibility to the object with the best information and authority to perform it.

Step 4: Define invariants and states

Write conditions that must always hold. Define meaningful states and permitted transitions. Include abnormal and recovery behavior, not only the normal operating path.

Step 5: Define collaborations

Determine which objects need to communicate, what information crosses each interface, and who initiates an interaction. Avoid passing an entire object when a small value or domain command is sufficient.

Step 6: Evaluate cohesion and coupling

Look for classes with unrelated responsibilities, excessive dependencies, or direct access to other objects’ internal data. Refactor before implementation becomes difficult.

Step 7: Test the model against scenarios

Walk through startup, normal operation, sensor failure, emergency stop, restart, and shutdown. A model that works only for the “happy path” is incomplete for engineering use.

6. Worked Modeling Example: Automated Conveyor

Background

The following is an illustrative case. A conveyor transports components past an optical sensor. A motor drives the belt. An operator can start or stop the conveyor. A guard switch and emergency-stop circuit must prevent motion when unsafe conditions exist.

Problem analysis

The software must:

  • read whether the guard is closed;
  • verify that the emergency stop is released;
  • command the motor to start or stop;
  • count components detected by the optical sensor;
  • report faults without falsely indicating safe operation.

A poor design might expose global variables such as motorOn, guardClosed, and itemCount. Any function could change them, making it difficult to determine whether safety rules were respected.

Object model

Candidate objects and responsibilities are as follows:

ObjectImportant stateMain responsibilities
Motorenabled, commanded speed, faultenforce motor commands and report motor status
OpticalSensorcurrent signal, validitysample and interpret item detection
SafetySystemguard state, emergency-stop statedetermine whether motion is permitted
ConveyorControlleroperating state, item countcoordinate start, stop, counting, and fault response
FaultLogrecorded eventspreserve diagnostic history

The controller should not decide that a physically faulty motor is healthy merely because a command was sent. The motor object or driver should report actual diagnostic status, while the safety system should determine whether safety conditions permit motion.

State model

A useful controller state set is:

  • Stopped: no motion command is active.
  • Running: motion is commanded and required permissive conditions remain true.
  • Fault: operation has been inhibited because a fault requires attention.
  • EmergencyStop: motion is immediately inhibited by the safety circuit.

A start request from Stopped may lead to Running only if all safety conditions and device checks pass. A safety violation from Running leads to EmergencyStop, not simply to ordinary Stopped, because the event has safety significance. Resetting should require explicit conditions rather than an automatic return to operation.

Collaboration sequence

  1. The operator requests start.
  2. ConveyorController asks SafetySystem whether motion is allowed.
  3. The controller asks Motor to enable and command a safe speed.
  4. The sensor reports an item transition.
  5. The controller increments the count only after validating the transition, avoiding repeated counts while an item remains in view.
  6. If the guard opens, SafetySystem reports a violation and the controller commands the motor to stop.
  7. The fault or emergency event is recorded in FaultLog.

Outcome

This design separates safety decisions, motor control, sensing, coordination, and historical recording. Each part can be tested independently. A simulated sensor can test counting logic; a test motor can verify command sequences; safety tests can verify that motion is never initiated when a permissive condition is absent.

Learning points

The model is not valuable because it has many classes. It is valuable because responsibilities match system concerns, internal state is protected, and collaborations reflect real operating rules. The model also exposes issues requiring further engineering: sensor debounce, communication timeouts, watchdog behavior, emergency-stop response time, and restart authorization.

7. Quality, Safety, and Professional Practice

Cohesion, coupling, and maintainability

High cohesion makes a class easier to explain and test. Low coupling limits the consequences of change. A practical warning sign is a class that requires knowledge of many unrelated classes or frequently accesses their internals.

Interfaces should expose stable domain operations. Passing raw arrays, hardware registers, or global flags across the entire application increases coupling. A command such as requestStop(StopReason::GuardOpened) conveys more meaning than assigning several unrelated variables.

Testability

Object-oriented modeling supports unit tests when dependencies can be replaced with controlled test doubles. A test should verify observable behavior and safety properties, not private implementation details. Integration and hardware-in-the-loop tests remain necessary because timing, electrical noise, communication faults, and mechanical effects cannot always be represented by unit tests.

Safety and fault handling

In mechatronics, failure behavior is part of object behavior. A class should specify what happens if a reading is invalid, a command times out, or a device disconnects. “No data” must not silently become “zero speed” or “safe.” Safety-related functions should be designed according to the applicable organizational and industry standards, with traceable requirements, verification evidence, and controlled changes.

Object-oriented abstraction must never obscure a safety function. For example, a convenient move() method must document its limits, units, preconditions, timeout behavior, and response to an emergency stop. Software must not be treated as the only layer of protection; mechanical, electrical, and procedural safeguards may be required.

Units and domain types

Many mechatronic errors arise from confusing units. A function accepting double cannot communicate whether a value is measured in degrees per second, radians per second, or revolutions per minute. Strong domain types or clearly named interfaces reduce this risk. At minimum, names should include units, such as speedRpm or angleRadians. Conversion should occur at explicit boundaries.

AI-assisted development

AI coding tools can generate class skeletons, tests, and documentation, but they do not replace engineering judgment. Generated code may contain unsafe assumptions, incorrect ownership, hidden global state, unit errors, or inappropriate exception behavior. Engineers remain responsible for requirements traceability, code review, license compliance, cybersecurity, testing, and safety validation. AI output should be treated as a proposal requiring inspection and evidence.

8. Common Design Errors

  1. Modeling every noun as a class. Some nouns are attributes, measurements, or events rather than independent objects.
  2. Public data everywhere. This bypasses invariants and makes change expensive.
  3. The god object. One controller knows and does nearly everything.
  4. Inheritance for convenience. Code reuse alone does not establish a valid subtype relationship.
  5. Hidden ownership. Unclear lifetime and pointer responsibilities cause leaks, dangling references, or double deletion.
  6. Ignoring abnormal behavior. A design that covers only normal operation is unsuitable for physical systems.
  7. Over-abstraction. Excessive layers can obscure timing, diagnostics, and hardware behavior.
  8. Weak interfaces. Interfaces that expose implementation details couple clients to internal design.

Best-Practice Checklist

Before implementing an object-oriented mechatronic design, ask:

  • Does every major class have a clear purpose?
  • Are its responsibilities cohesive and stated in domain language?
  • Are important invariants enforced internally?
  • Are state transitions and failure responses explicit?
  • Is ownership clear?
  • Are units and ranges documented?
  • Can hardware-dependent parts be tested independently?
  • Are safety decisions separated from convenience functions?
  • Do interfaces minimize unnecessary coupling?
  • Has the design been tested against startup, shutdown, fault, and recovery scenarios?

Chapter Summary

Object-oriented thinking models a system as cooperating objects with identity, state, behavior, and responsibilities. Abstraction selects the details relevant to a purpose; encapsulation protects state and invariants; modularity structures the system into cohesive components with limited coupling. Classes define types, while objects are runtime instances with their own identities and states.

In C++, public interfaces should expose meaningful operations while private data preserves implementation freedom and protects valid states. Object collaboration may involve associations, aggregation, composition, or polymorphism, but these relationships must express genuine domain and ownership semantics. Responsibility-driven design assigns knowledge and actions to the objects best placed to perform them.

For mechatronic systems, modeling must include safety conditions, faults, timing, units, diagnostics, and recovery. A well-designed object model supports testing, maintenance, hardware substitution, and controlled evolution. It does not eliminate engineering complexity, but it makes that complexity visible, organized, and manageable.

Key Terms

Abstraction: Representation of essential features while omitting irrelevant implementation detail.

Behavior: The actions and responses an object exhibits.

Class: A C++ type definition describing possible state and operations.

Cohesion: Degree to which the responsibilities of a module belong together.

Composition: A whole–part relationship involving strong ownership and commonly shared lifetime.

Coupling: Degree of dependency between modules or objects.

Encapsulation: Control of access to an object’s internal representation.

Entity object: An object whose continuing identity matters independently of its current values.

Identity: Property that distinguishes one object from another.

Invariant: A condition that must remain true for a valid object or system state.

Message: A request or communication sent from one object to another.

Modularity: Division of a system into manageable units with defined interfaces.

Object: A runtime instance of a class with identity, state, behavior, and responsibilities.

Polymorphism: Ability to use a common interface while allowing type-specific behavior.

Responsibility: An obligation assigned to an object, involving what it knows or does.

State: Values and conditions describing an object at a particular time.

State transition: Change from one defined state to another caused by an event or condition.

Value object: An object characterized primarily by its value rather than continuing identity.

Review Questions

Short Answer Questions

  1. Distinguish a class from an object.
  2. Why is encapsulation important in a motor-control class?
  3. Define cohesion and coupling.
  4. What is an invariant? Give one mechatronic example.
  5. Distinguish composition from a general association.

Long Answer Questions

  1. Explain abstraction, encapsulation, modularity, identity, state, behavior, and responsibility using a robotic arm as an example.
  2. Describe a responsibility-driven process for modeling an automated conveyor.
  3. Explain how inheritance and composition differ, and discuss when composition may be preferable.
  4. Design a conceptual object model for a temperature-controlled chamber, including states, responsibilities, and collaborations.
  5. Discuss why fault handling, timing, units, and safety must be included in object-oriented models of mechatronic systems.

Analytical / Application Questions

  1. A controller directly modifies motor.enabled, motor.speed, and motor.fault fields from several functions. Analyze the design weaknesses and propose an encapsulated alternative.
  2. A robot system contains one class that reads sensors, plans paths, drives motors, logs faults, and displays status. Identify the cohesion and coupling problems and recommend a decomposition.
  3. Model the response of a conveyor when an item is detected, the guard opens, and the system is later reset. Specify objects, responsibilities, state transitions, and safety-related conditions.

Multiple Choice Questions with Answers

Question 1

Which concept protects an object's internal representation by requiring access through controlled operations?

A. Encapsulation

B. Inheritance

C. Aggregation

D. Polymorphism

Correct Answer: A. Encapsulation

Question 2

In the conveyor case, which object is primarily responsible for determining whether motion is permitted?

A. OpticalSensor

B. SafetySystem

C. FaultLog

D. ItemCounter

Correct Answer: B. SafetySystem

Question 3

What is the principal distinction between a class and an object?

A. A class is always private, whereas an object is always public.

B. A class defines a type, whereas an object is an instance of that type.

C. A class stores only behavior, whereas an object stores only data.

D. A class exists only at compile time and cannot describe behavior.

Correct Answer: B. A class defines a type, whereas an object is an instance of that type.

Question 4

Which design characteristic means that the responsibilities within a module strongly belong together?

A. High coupling

B. Low cohesion

C. High cohesion

D. Multiple inheritance

Correct Answer: C. High cohesion

Question 5

Why should a motor's speed data usually be private?

A. To prevent the compiler from optimizing access

B. To allow any function to change it freely

C. To enforce limits and preserve valid-state rules

D. To make the motor impossible to test

Correct Answer: C. To enforce limits and preserve valid-state rules

Question 6

Two temperature sensors report the same value but are installed at different locations. What distinguishes them as objects?

A. Their identity

B. Their inheritance hierarchy

C. Their public visibility

D. Their cohesion

Correct Answer: A. Their identity

Question 7

Which relationship most strongly indicates that a whole owns the lifetime of its parts?

A. Association

B. Composition

C. Dependency inversion

D. Generalization

Correct Answer: B. Composition

Question 8

What is a major benefit of an interface such as MotorDriver in the chapter example?

A. It eliminates the need for testing.

B. It allows controllers to depend on behavior rather than a specific hardware implementation.

C. It makes all motor failures impossible.

D. It exposes private motor registers to every client.

Correct Answer: B. It allows controllers to depend on behavior rather than a specific hardware implementation.

Question 9

Which is an example of a state transition in the conveyor controller?

A. Changing a class name from Motor to Driver

B. Moving from Ready to Moving after a valid motion command

C. Converting a private member to a public member

D. Replacing a sensor with a different variable name

Correct Answer: B. Moving from Ready to Moving after a valid motion command

Question 10

Why must AI-generated C++ for a mechatronic system be reviewed by an engineer?

A. AI-generated code can never compile.

B. Generated code may contain unsafe assumptions, unit errors, or incorrect ownership.

C. Review is unnecessary when the code is short.

D. AI tools automatically certify safety compliance.

Correct Answer: B. Generated code may contain unsafe assumptions, unit errors, or incorrect ownership.

Object-Oriented Programming with C++
Chapter 2 of 14