Computer Programming for Engineers

Chapter 5
Modular and Object-Oriented Engineering Software

Bachelors Program in Mechnical Engineering
Computer Programming for Engineers   •   Bachelor

Modular and Object-Oriented Engineering Software

Introduction

Engineering software is rarely a single calculation. A mechanical engineer may need to represent materials, geometry, loads, machines, sensors, controllers, and numerical procedures in one system. If all logic is placed in one long program, even a correct prototype becomes difficult to test, modify, reuse, or trust. Modular and object-oriented programming provide methods for organizing engineering knowledge into dependable software components.

A module is a cohesive unit of code with a defined purpose and interface. An object combines data with operations that act on that data. A well-designed engineering program hides implementation details, exposes meaningful operations, and allows components to be assembled into larger physical or computational systems.

This chapter uses Python-like examples because Python is widely used in engineering analysis, automation, simulation, and data processing. The principles also apply to C++, Java, MATLAB, and other languages.

Learning Objectives

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

  1. Explain modularity, abstraction, encapsulation, cohesion, and coupling.
  2. Design classes and objects representing engineering entities.
  3. Apply constructors, methods, properties, and validation appropriately.
  4. Distinguish inheritance from composition and select between them.
  5. Use interfaces and polymorphism to create interchangeable models.
  6. Apply a simple design pattern to an engineering software problem.
  7. Evaluate maintainability, testability, safety, and ethical implications of engineering code.
  8. Develop a reusable software structure for components, materials, machines, and physical systems.

1. From Procedures to Engineering Models

A procedural program organizes instructions: read inputs, calculate intermediate values, and display results. This is appropriate for a short, stable calculation. However, engineering systems contain entities with identity, state, behavior, and relationships. For example, a beam has material, cross-sectional properties, length, supports, and loads. It can calculate stress or deflection, but it should also reject physically invalid inputs.

Object-oriented programming (OOP) models such entities explicitly. A class is a specification or blueprint; an object is an instance of that class. A Beam class may describe every beam, while beam_1 represents one particular beam.

A useful design question is: “What engineering concept does this data and behavior belong to?” If dimensions and mass belong together, they may form a Component. If a calculation can be performed independently of an object’s state, it may be better represented as a function in a calculation module.

Modularity

Modularity divides a program into units that can be developed, tested, and replaced independently. A mechanical analysis package might contain:

ModuleResponsibility
materialsMaterial properties and validation
geometryAreas, volumes, and section properties
loadsForces, moments, and load combinations
componentsBeams, shafts, gears, or brackets
solversNumerical solution procedures
reportsResults, units, and presentation

A module should have a narrow and clear responsibility. This principle is closely related to cohesion: the degree to which the elements of a module belong together. High cohesion is desirable. Coupling describes dependence between modules; low coupling generally improves maintainability.

Abstraction

Abstraction represents essential features while omitting irrelevant implementation detail. A Material object may expose density, yield strength, and elastic modulus without revealing how its data are stored. A user of the object needs to know the meaning and units of its properties, not the internal data structure.

Abstraction is not the same as simplification without conditions. An engineering abstraction is valid only within a stated domain. For example, a linear-elastic material model is useful when strains are sufficiently small and the material remains within its elastic range. It should not silently be used to predict plastic collapse.

2. Encapsulation and Invariants

Encapsulation combines data and related operations while controlling access to internal state. It protects invariants, which are conditions that must remain true for an object to be valid.

For a uniform cylindrical shaft, radius and length must be positive. For a material, density should be positive and Young’s modulus should normally be positive. If unrestricted public variables are used, invalid states can enter the program and contaminate later calculations.

class Material:
    def __init__(self, name, density, youngs_modulus, yield_strength):
        if density <= 0:
            raise ValueError("density must be positive")
        if youngs_modulus <= 0:
            raise ValueError("Young's modulus must be positive")
        if yield_strength <= 0:
            raise ValueError("yield strength must be positive")

        self.name = name
        self.density = density              # kg/m^3
        self.youngs_modulus = youngs_modulus  # Pa
        self.yield_strength = yield_strength  # Pa

    def elastic_stress(self, strain):
        return self.youngs_modulus * strain

The method above uses Hooke’s law for uniaxial linear elasticity:

σ = Eε

Here, σ is normal stress in pascals (Pa), E is Young’s modulus in Pa, and ε is dimensionless normal strain. The relation assumes small deformation, uniaxial loading, homogeneous material behavior, and elastic response. It is not a general law for plastic, viscoelastic, or large-deformation behavior.

Encapsulation does not mean that every attribute must be hidden. It means that access should be designed. A property can validate assignments or calculate a value from more fundamental data.

class Shaft:
    def __init__(self, diameter, length, material):
        if diameter <= 0 or length <= 0:
            raise ValueError("dimensions must be positive")
        self.diameter = diameter
        self.length = length
        self.material = material

    @property
    def cross_sectional_area(self):
        import math
        return math.pi * self.diameter**2 / 4

    @property
    def mass(self):
        return (self.cross_sectional_area * self.length
                * self.material.density)

The mass calculation is:

m = ρAL

where m is mass in kg, ρ is density in kg/m³, A is cross-sectional area in m², and L is length in m. The equation assumes a uniform shaft and constant density. Unit consistency is essential: millimetres must be converted to metres before using SI density.

3. Classes, Objects, and Responsibilities

A class normally contains:

  • State: data such as dimensions, temperature, or position.
  • Behavior: operations such as mass(), stress(), or energy()`.
  • Identity: the ability to distinguish one component or simulation entity from another.
  • Invariants: rules that define valid state.

A constructor establishes an initial valid state. Methods should be named after meaningful operations and should avoid surprising side effects. A method called stress() should not silently change the component’s material.

A useful engineering class should also document:

  1. Units of inputs and outputs.
  2. Sign conventions.
  3. Assumptions and validity limits.
  4. Exceptions raised for invalid data.
  5. Whether results are exact, approximate, or numerical.

Worked Example: A Circular Rod

Consider a circular rod with diameter 20 mm, length 0.50 m, and density 7,850 kg/m³. First convert the diameter:

d = 20 mm = 0.020 m

The area is:

A = πd²/4

Thus,

A = π(0.020 m)2/4 = 3.1416 × 10^−4 m²

The volume is V = AL = (3.1416 × 10^−4)(0.50) = 1.5708 × 10^−4 m³. Therefore,

m = ρV = 7850 × 1.5708 × 10^−4 ≈ 1.233 kg

The result is physically plausible and has units of kilograms. A reusable Shaft class can perform the same calculation for many rods without duplicating the formula.

4. Inheritance, Composition, and Polymorphism

Inheritance

Inheritance expresses an “is-a” relationship. A SteelMaterial could be a specialized Material, or a CentrifugalPump could be a specialized Machine. The derived class receives behavior from a base class and may extend or override it.

Inheritance is useful when the subtype genuinely satisfies the behavioral contract of the base type. It becomes harmful when used only to reuse a few lines of code or when the hierarchy becomes deep and fragile.

Composition

Composition expresses a “has-a” relationship. A RobotArm has joints, actuators, and sensors. A Gearbox has gears and bearings. Composition is usually more flexible because parts can be replaced independently.

class Sensor:
    def __init__(self, name):
        self.name = name

    def read(self):
        raise NotImplementedError

class TemperatureSensor(Sensor):
    def __init__(self, name, temperature):
        super().__init__(name)
        self.temperature = temperature

    def read(self):
        return self.temperature

class Machine:
    def __init__(self, name, sensors):
        self.name = name
        self.sensors = list(sensors)

    def readings(self):
        return {sensor.name: sensor.read() for sensor in self.sensors}

Here, Machine is composed of sensors. The machine does not need to know the internal implementation of each sensor. This supports polymorphism: different objects respond to the same operation, read(), in their own way.

Interfaces

An interface is a contract describing operations that a component must provide. In Python, an abstract base class can make this contract explicit:

from abc import ABC, abstractmethod

class StressModel(ABC):
    @abstractmethod
    def stress(self, strain):
        pass

class LinearElasticModel(StressModel):
    def __init__(self, youngs_modulus):
        if youngs_modulus <= 0:
            raise ValueError("modulus must be positive")
        self.E = youngs_modulus

    def stress(self, strain):
        return self.E * strain

A solver can accept any StressModel rather than depending on one specific material law. A nonlinear model, temperature-dependent model, or experimentally fitted model can be substituted if it obeys the same interface.

5. Engineering Design Patterns

A design pattern is a general, reusable solution to a recurring design problem. Patterns are not mandatory recipes; they are vocabulary and guidance.

Strategy Pattern

The Strategy pattern places alternative algorithms behind a common interface. It is particularly suitable for engineering software because a design may require alternative models: Euler–Bernoulli or Timoshenko beam theory, explicit or implicit time integration, or different fatigue criteria.

class Solver:
    def __init__(self, stress_model):
        self.stress_model = stress_model

    def calculate_stress(self, strain):
        return self.stress_model.stress(strain)

The solver depends on the abstraction stress(), not on a particular formula. This reduces coupling and makes testing easier.

Factory Pattern

A factory centralizes object creation. For example, a material factory can create a validated material from a standard name. This prevents material definitions from being duplicated throughout an application. However, a factory should not conceal important configuration or make object creation unnecessarily difficult to trace.

Observer Pattern

The Observer pattern allows interested components to receive notifications when a subject changes. In a monitoring system, a machine can notify a logger, alarm service, and dashboard when temperature exceeds a limit. Safety-critical systems must still use deterministic alarm logic and independent verification; an observer mechanism alone is not a safety guarantee.

6. A Layered Architecture for Engineering Programs

A maintainable application often separates concerns into layers:

  1. Domain layer: materials, components, loads, and physical rules.
  2. Application layer: workflows such as sizing or simulation.
  3. Infrastructure layer: files, databases, numerical libraries, and communication.
  4. Presentation layer: reports, plots, user interfaces, or APIs.

For example, a beam-sizing application should not place file-reading code inside the equation for bending stress. The domain calculation should be testable with direct numerical inputs.

For a beam under a bending moment, the nominal bending stress is:

σ = Mc/I

where M is bending moment in N·m, c is the distance from the neutral axis to the outermost fiber in m, and I is the second moment of area in m⁴. The formula assumes elastic bending, a beam model appropriate to the geometry, and a valid cross-sectional second moment. For a solid circular section of diameter d, I = πd4/64 and c = d/2.

A domain object may calculate this stress; a separate application service may compare it with allowable stress; and a reporting layer may format the result with units and warnings.

7. Testing and Verification

Object-oriented structure does not automatically produce correct engineering software. Verification must address both code behavior and physical validity.

Unit Tests

A unit test checks a small behavior in isolation. For the circular area calculation, a test can compare the computed result with an independently calculated expected value. Tests should include normal, boundary, and invalid cases:

  • Positive dimensions.
  • Zero or negative dimensions.
  • Unit conversions.
  • Very large or very small values.
  • Material values outside the model’s validity range.

Engineering Verification

Software verification asks whether the implementation matches its specification. Validation asks whether the model represents the real physical system adequately. A program can be verified as correctly implementing Hooke’s law and still be invalid for a rubber component undergoing large deformation.

Useful practices include dimensional checks, conservation checks, comparison with hand calculations, benchmark problems, sensitivity studies, and independent implementations for critical calculations. Numerical tolerances must be stated rather than hidden.

8. Illustrative Case: Modular Thermal-Mechanical Machine Model

This is a fictional illustrative case.

Background

An engineering team develops software for a motor-driven pump. The pump contains a shaft, bearings, a motor, temperature sensors, and an alarm system. The first prototype is a 900-line script that reads a spreadsheet and calculates shaft stress, bearing temperature, and a pass/fail result.

Problem

The script duplicates material data, mixes millimetres and metres, embeds alarm thresholds in several functions, and cannot support a new motor model without editing unrelated calculations. A change to the shaft diameter causes inconsistent updates in mass and stress calculations.

Analysis

The team identifies three major design defects:

  1. Low cohesion: file reading, physics, reporting, and alarms are mixed.
  2. High coupling: calculations depend directly on spreadsheet column positions.
  3. Weak encapsulation: any function can overwrite material properties or units.

The team also identifies a physical limitation: the shaft stress model is valid only for elastic, quasi-static loading. It cannot be presented as a fatigue-life prediction.

Approach

The software is reorganized into:

  • Material, Shaft, and Bearing domain classes.
  • A StressModel interface with linear-elastic and fatigue-analysis strategies.
  • A Machine composed of components and sensors.
  • A separate input adapter for spreadsheets.
  • A reporting module that displays units, assumptions, and warnings.
  • Unit tests for geometry, mass, stress, and invalid inputs.

A configuration object stores alarm limits in one location. Each calculation returns a structured result containing value, unit, assumptions, and status rather than only a number.

Outcome

The team can add an aluminium shaft by creating a new material instance rather than modifying the stress algorithm. A different sensor can be substituted without changing the machine class. Tests detect a mistaken millimetre-to-metre conversion before deployment. Reports distinguish “elastic stress below allowable limit” from “fatigue life not evaluated.”

Learning Points

The principal improvement is not merely shorter code. The redesigned program makes engineering concepts explicit, limits invalid states, separates models from infrastructure, and communicates uncertainty and applicability. These properties improve maintainability and engineering judgment.

9. Limitations, Ethics, and Professional Practice

Model limitations

Every class that represents a physical system embeds assumptions. A Material class may assume isotropy, constant temperature, and rate-independent behavior. A Bearing class may use a simplified load-life relationship. Such assumptions must be documented and tested.

Units and numerical issues

SI units reduce ambiguity, but software should still make units visible. Mixing degrees and radians, gauge and absolute pressure, or millimetres and metres can produce plausible but incorrect results. For high-consequence applications, a unit-aware library or explicit unit type may be justified.

Floating-point arithmetic introduces rounding error. Equality tests on calculated real numbers should normally use a stated tolerance. Ill-conditioned numerical problems may amplify small input errors, so software design should expose conditioning and convergence information rather than reporting false precision.

Safety and governance

Engineering software can influence public safety. Critical calculations require requirements traceability, code review, version control, documented verification, independent checking, and controlled release. Access permissions and audit logs may be necessary when results support regulated decisions.

An engineer must not claim that software is validated merely because it produces expected output for one example. Nor should AI-generated code be accepted without inspection. AI tools can assist with scaffolding, test generation, and documentation, but responsibility for equations, assumptions, data provenance, licensing, cybersecurity, and final decisions remains with qualified professionals.

Best practices

  • Give each module one clear responsibility.
  • Prefer composition when a system contains replaceable parts.
  • Use inheritance only for genuine substitutable subtypes.
  • Make units, assumptions, and validity ranges explicit.
  • Validate inputs at system boundaries.
  • Keep physical models independent from user interfaces and files.
  • Use interfaces to support alternative models and testing.
  • Test edge cases and compare important results with independent calculations.
  • Avoid deep inheritance hierarchies and global mutable state.
  • Record software version, input data, model assumptions, and numerical settings.

Contemporary Developments

Modern engineering software increasingly combines object-oriented domain models with data-oriented numerical kernels, automatic differentiation, cloud services, digital twins, and machine-learning models. These technologies do not eliminate fundamental design principles. A digital twin still requires a clear representation of components, interfaces, state, sensor data, and model validity. A machine-learning component should be treated as a model with documented training data, uncertainty, operating domain, and monitoring requirements.

A practical contemporary architecture may use Python classes for domain concepts, vectorized numerical arrays for large computations, and a service interface for simulation or monitoring. The boundary between these parts should be explicit so that performance optimizations do not obscure physical assumptions.

Chapter Summary

Modular and object-oriented programming enables engineers to transform physical concepts into reusable, testable software components. Modularity improves organization by separating responsibilities, while cohesion and low coupling support change. Abstraction presents essential engineering behavior without exposing unnecessary implementation detail. Encapsulation protects invariants and helps prevent invalid physical states.

Classes define state and behavior; objects represent particular components or systems. Inheritance can express valid “is-a” relationships, but composition is often more flexible for machines made of interacting parts. Interfaces and polymorphism allow alternative material laws, solvers, sensors, and design strategies to be substituted without rewriting the surrounding application. Patterns such as Strategy, Factory, and Observer provide reusable design guidance.

Engineering software must be verified computationally and validated physically. Units, assumptions, model limits, numerical precision, safety, traceability, and professional responsibility are as important as program syntax. The most reliable designs make both software structure and engineering assumptions visible.

Key Terms

Modularity: Division of software into cohesive, manageable units.

Module: A code unit with a defined responsibility and interface.

Class: A blueprint defining the state and behavior of objects.

Object: An instance of a class.

Abstraction: Representation of essential features while hiding irrelevant detail.

Encapsulation: Bundling data and behavior while controlling access to state.

Invariant: A condition that must remain true for an object to be valid.

Cohesion: Degree to which elements of a module belong together.

Coupling: Degree of dependence between software units.

Inheritance: Mechanism for deriving a specialized class from a general class.

Composition: Construction of a system from cooperating component objects.

Polymorphism: Ability to use different objects through a common operation or interface.

Interface: Contract specifying operations a component provides.

Strategy pattern: Encapsulation of interchangeable algorithms behind a common interface.

Factory pattern: Centralized mechanism for creating related objects.

Observer pattern: Mechanism for notifying dependent objects when a subject changes.

Verification: Demonstration that software implements its specification correctly.

Validation: Evaluation of whether a model adequately represents the intended physical system.

Domain model: Software representation of entities, rules, and behavior in an engineering field.

Technical debt: Future cost created by expedient but poorly maintainable design decisions.

Review Questions

Short Answer Questions

  1. Define abstraction and explain why it is useful in engineering software.
  2. Distinguish cohesion from coupling.
  3. What is an invariant? Give one example for a shaft class.
  4. Explain the difference between inheritance and composition.
  5. Why must model assumptions be documented alongside engineering results?

Long Answer Questions

  1. Design a class structure for a gearbox containing gears, bearings, lubrication data, and sensors. Justify your use of composition and interfaces.
  2. Explain how encapsulation can prevent unit and validity errors in a material or component class.
  3. Describe the Strategy pattern and show how it could support alternative beam or material models.
  4. Discuss verification, validation, and independent checking in safety-related engineering software.
  5. Propose a layered architecture for a machine-monitoring application and explain the responsibility of each layer.

Analytical / Application Questions

  1. A program calculates shaft mass using diameter in millimetres while density is in kg/m³. Identify the error mechanism and propose two design controls that would prevent it.
  2. A new fatigue model must be added without changing an existing machine-monitoring workflow. Recommend an object-oriented design and explain where polymorphism is used.
  3. A linear-elastic stress calculation agrees with a benchmark, but the component operates beyond yield. Analyze why verification alone is insufficient and specify what additional validation is required.

Multiple Choice Questions with Answers

Question 1

Which design property describes how strongly the elements of a module belong together?

A. Cohesion

B. Inheritance

C. Polymorphism

D. Instantiation

Correct Answer: A. Cohesion

Question 2

For a uniform cylindrical shaft, which expression calculates mass from density, area, and length?

A. m = ρA/L

B. m = ρAL

C. m = A/(ρL)

D. m = ρL/A

Correct Answer: B. m = ρAL

Question 3

Which relationship is best represented by composition?

A. A steel material is a material

B. A pump has sensors

C. A class is an object

D. A solver is a number

Correct Answer: B. A pump has sensors

Question 4

What is the main purpose of encapsulation?

A. To eliminate all numerical error

B. To make every variable globally accessible

C. To protect state and maintain valid object conditions

D. To replace testing

Correct Answer: C. To protect state and maintain valid object conditions

Question 5

The Strategy pattern is most appropriate when a program must:

A. Store all data in one global variable

B. Select among interchangeable algorithms

C. Prevent object creation

D. Convert all units automatically

Correct Answer: B. Select among interchangeable algorithms

Question 6

For linear uniaxial elasticity, stress is related to strain by which equation?

A. σ = Eε

B. σ = E/ε

C. σ = ε/E

D. σ = E + ε

Correct Answer: A. σ = Eε

Question 7

Which statement correctly distinguishes verification from validation?

A. Verification checks implementation against specification; validation checks physical adequacy of the model

B. Verification applies only to hardware; validation applies only to software

C. Verification proves a model is physically exact; validation checks spelling

D. They are identical terms

Correct Answer: A. Verification checks implementation against specification; validation checks physical adequacy of the model

Question 8

A class that requires positive diameter and length is protecting what?

A. An invariant

B. A factory

C. A namespace

D. A user interface

Correct Answer: A. An invariant

Question 9

A machine accepts any sensor object that provides a compatible read operation. This is an example of:

A. Polymorphism

B. Hard-coded coupling

C. Procedural recursion

D. Data duplication

Correct Answer: A. Polymorphism

Question 10

For elastic bending stress σ = Mc/I, which quantity has units of m⁴?

A. M

B. c

C. I

D. σ

Correct Answer: C. I

Computer Programming for Engineers
Chapter 5 of 15