Software Engineering Principles, UML Modeling, and Algorithm Analysis

Fundamentals and Goals of Software Engineering

  • Software Engineering is the study of the techniques and theory that support the development of high-quality software.

  • The central discipline focuses on controlling the development process to achieve consistently good, predictable results.

  • Core participant objectives in software development:

    • Satisfying the client: The individual or organization sponsoring and financing the software development.

    • Meeting the needs of the user: The individuals who actively operate and interact with the software for its intended operational purpose.

  • Primary engineering goals:

    • Solving the right problem: Client interaction is critical; identifying true underlying needs is often far more complex than initial assumptions suggest.

    • Delivering a solution on time and under budget: Engineering execution requires navigating continuous practical trade-offs among scope, time, and financial constraints.

    • Delivering a high-quality solution: Quality perception varies across observers ("beauty is in the eye of the beholder"), requiring software to satisfy the divergent priorities of multiple stakeholders.

  • Definition of a stakeholder:

    • A stakeholder is a party that has an interest in a company and can either affect or be affected by the business.

    • In a typical corporation, primary stakeholders include investors, employees, customers, and suppliers.

  • The communication disconnect in software engineering:

    • Mismatches in communication and perception frequently disrupt software development projects, as illustrated by the classic tree-swing lifecycle analogy:


The tree swing cartoon illustrating miscommunications across software engineering roles
  • Breakdown of project perspectives:

    • How the customer explained it: A wooden swing seat composed of three boards arranged like a ladder, suspended by two ropes from a single tree branch.

    • How the Project Leader understood it: A swing seat tied around the central tree trunk with ropes, preventing the swing from ever moving.

    • How the Analyst designed it: A swing suspended beneath a cut-out section of the tree trunk, supported on both sides by wooden props to keep the tree upright.

    • How the Programmer wrote it: Ropes wrapped around the trunk with the seat resting flat on the ground.

    • How the Business Consultant described it: A luxurious, plush blue armchair suspended from the tree branch against a bright sunrise.

    • How the project was documented: An empty landscape with no tree or swing; completely undocumented.

    • What operations installed: A single bare rope hanging from a branch without any swing seat attached.

    • How the customer was billed: An elaborate, multi-hill roller coaster track with flags and cars.

    • How it was supported: The tree completely chopped down to a bare stump.

    • What the customer really needed: A simple, functional tire swing hanging by a single rope from a branch.

Dimensions of Software Quality

  • Correctness: The degree to which software adheres to its specific requirements.

  • Reliability: The frequency and criticality of software failure.

  • Robustness: The degree to which erroneous situations are handled gracefully.

  • Usability: The ease with which users can learn and execute tasks within the software.

  • Maintainability: The ease with which changes can be made to the software.

  • Reusability: The ease with which software components can be reused in the development of other software systems.

  • Portability: The ease with which software components can be used in multiple computer environments.

  • Efficiency: The degree to which the software fulfills its purpose without wasting resources (such as CPU cycles and storage space).

Software Development Life Cycle Phases and Organizational Roles

  • A development life cycle defines a structured process to be followed during the creation of a software product.

  • Across differing methodologies, every software life cycle model addresses six fundamental stages:


Software development lifecycle phases and corresponding organizational roles
  • Stage-by-stage breakdown of the software lifecycle:

    • 1. Problem Analysis:

    • Focus: Specifying the detailed requirements of the software system based on accurate, verified data.

    • Elicitation techniques: In-depth discussions and negotiations with the client; formal modeling of problem structure and data flow; direct observation of client operational activities; comprehensive analysis of existing systems and legacy solutions.

    • Associated organizational roles: Product Owner, Project Manager, Business Analyst, Chief Technology Officer (CTO).

    • 2. Design:

    • Focus: Specifying the structural solution prior to construction (analogous to producing architectural blueprints before erecting a bridge).

    • Key activities: Determining the overall software structure (architecture); identifying key objects, classes, and their associations; evaluating design alternatives.

    • Associated organizational roles: System Architect, UX/UI Designers.

    • 3. Implementation / Development:

    • Focus: Transforming architectural designs into executable software.

    • Context: Often mistakenly treated as the primary or sole act of software engineering, whereas it represents only one phase.

    • Practice: Encourages strategic software reuse ("not reinventing the wheel") to accelerate delivery and increase code stability.

    • Associated organizational roles: Front-end Developers, Back-end Developers.

    • 4. Evaluation / Testing:

    • Focus: Verifying that the developed system strictly conforms to user requirements.

    • Scope: Encompasses unit testing, integration testing, system validation, and acceptance testing.

    • Critical axiom: It is entirely possible to construct a software system that contains zero bugs yet is completely wrong because it fails to satisfy the actual operational need.

    • Associated organizational roles: Solutions Architect, QA Engineer, Tester, DevOps.

    • 5. Deployment:

    • Focus: The operational mechanism through which applications, components, updates, and patches are distributed from engineers to users.

    • Status: Represents the milestone where the product becomes accessible for real-world usage, commonly designated initially as a Beta version.

    • Associated organizational roles: Database Administrator, DevOps.

    • 6. Maintenance:

    • Focus: Sustaining and adapting software after initial release.

    • Key activities: Correcting operational defects; implementing feature enhancements to meet shifting user workflows; adapting code to underlying platform and operating system upgrades (such as new Android OS versions or updated web browser engines).

    • Development correlation: The higher the quality and rigor of the initial analysis and design efforts, the easier the subsequent maintenance tasks will be.

    • Associated organizational roles: Users, Testers, Support Managers.

Traditional and Modern Software Life Cycle Models

  • The Waterfall Model:

    • One of the earliest formal development models in software engineering.

    • Characterized by a strictly linear, sequential progression where each stage flows directly into the subsequent one.

    • Fundamentally driven by phase-gate documentation.


The waterfall software development model flowchart
  • Advantages:

    • Establishes clearly defined milestones and deliverables.

    • Provides high visibility, enabling managers and clients to monitor project status against static checkpoints.

  • Disadvantages:

    • Testing and evaluation occur late in the process, making defect remediation costly.

    • Lacks flexibility to handle shifting client requirements and proves unrealistic for most dynamic projects.

    • The Spiral Model:

  • Developed by Barry Boehm in the mid-1980s.

  • Embraces an iterative engineering philosophy where activities are repeated cyclically across different subsystems.

  • Specifically designed to minimize technical and operational project risks while continually refining system requirements.

  • Each loop through the spiral constitutes a complete development phase encompassing:

    • Determine Objectives

    • Consider Alternatives

    • Risk Analysis

    • Prototyping

    • Development

    • Evaluation

    • Planning


The spiral model of software development
  • The Evolutionary Model:

    • Adopts continuous, iterative refinement similar to the spiral model.

    • Produces functional intermediate versions of the system for successive evaluation by both developers and clients.


The evolutionary software development model
  • Architectural tradeoff: Subsequent code maintenance can become difficult if iterative changes are introduced without disciplined refactoring.

    • Agile Software Development:

  • Recognized as the modern software industry standard.

  • Key operational characteristics: Self-documented code; pair programming; cross-functional SCRUM teams; user-centered architecture; recurring team ceremonies (daily stand-ups, retrospectives, sprint planning); iterative design and incremental implementation; active user engagement throughout the entire lifecycle.

Unified Modeling Language Class Diagram Architecture

  • Principles of the Unified Modeling Language (UML):

    • A standardized graphical notation used for modeling and designing object-oriented software systems.

    • Represents a unified synthesis of three historically separate modeling notations (Booch, Object Modeling Technique [OMT], and Object-Oriented Software Engineering [OOSE]).

    • Language-independent, allowing design concepts to be implemented across languages such as Java, C++, and Python.

    • Flexible and selective: diagrams may capture high-level overviews or precise implementation details depending on communication goals.

  • Core components of UML class diagrams:

    • The structural classes utilized within the system.

    • The static relationships connecting classes.

    • The attributes (class-level data, including instance variables and constants) of each class.

    • The operations (equivalent to methods) executed by instances of each class.

    • Constraints and multiplicities governing object connections.

  • Anatomy of a UML class representation:

    • A standard class icon is partitioned into three stacked rectangular compartments:

    • Top compartment: The name of the class.

    • Middle compartment: The names and data types of attributes/fields.

    • Bottom compartment: The names, parameter lists, and return types of operations/methods.

    • Visibility indicators:

    • A minus sign (-) designates private member access.

    • A plus sign (+) designates public member access.


Anatomy of UML class diagrams showing Person and Book classes
  • Detailed class specifications:

    • Person class:

      • Attributes: -name : String, -birthDate : Date

      • Operations: +getName() : String, +setName(name) : void, +isBirthday() : boolean

    • Book class:

      • Attributes: -title : String, -authors : String[]

      • Operations: +getTitle() : String, +getAuthors() : String[], +addAuthor(name)

    • LibraryItem class:

      • Attributes: title, callNumber

      • Operations: checkout(), return()

    • Structural relationships in UML diagrams:

  • Generalization / Inheritance:

    • Defines an "is-a" inheritance relationship where child classes inherit state and behavior from a parent class.

    • Visual notation: A solid line originating at the subclass, ending with an open triangular arrowhead pointing directly at the superclass.

    • Illustrated structure: LibraryItem serves as the parent class for both Book (which introduces author and publisher) and Video (which introduces producer and studio).


UML class diagram showing inheritance relationships
  • Association:

    • Indicates a structural link or operational relationship between two independent classes.

    • Visual notation: A solid line connecting the classes, often annotated with multiplicity bounds at each end.

    • Multiplicity notation: Multiplicity of 0..* denotes zero or more related instances.

    • Illustrated structure: An association between LibraryCustomer (attributes: name, address; operations: register(), deregister()) and LibraryItem (attributes: title, callNumber; operations: checkout(), return()) with 0..* on both ends indicates that a customer may borrow zero or more items, and an item may be checked out across time by zero or more customers.


UML class diagram showing an association with multiplicities
  • Aggregation:

    • Models a whole-part ("has-a") relationship where child components can exist independently of the parent composite.

    • Visual notation: A solid line ending in an open, hollow diamond attached to the composite (whole) class.

    • Illustrated structure: CourseMaterials (attributes: courseTitle, courseNumber) serves as an aggregate composed of Book, Video, and CourseNotes (attributes: author, format).


UML class diagram displaying aggregation
  • Realization / Interface Implementation:

    • Denotes a class implementing a formal contract established by an interface.

    • Visual notation: A dashed line terminating with an open, hollow triangular arrowhead pointing toward the interface icon (labeled with the <<interface>> stereotype).

    • Illustrated structure: The class Book implements the <<interface>> Copyrighted, fulfilling operations setCopyright() and getCopyright().


UML class diagram showing a class implementing an interface
  • Dependency / Use Relationship:

    • Indicates that one class relies upon the operations or services of another class to carry out its responsibilities.

    • Visual notation: A dashed line terminating with an open arrow pointing toward the class being used, optionally labeled with the nature of the action.

    • Illustrated structure: LibraryCustomer uses Computer (attributes: location, ipAddress; operations: logon(), logoff()), labeled with the operation "searches online catalog".


UML class diagram displaying a dependency usage relationship
  • Practical application domains for UML structural modeling:

    • Hospital clinical and patient administration systems.

    • Restaurant franchise point-of-sale and kitchen workflows.

    • Digital media streaming networks (such as Netflix).

    • Law enforcement telecommunication and police dispatch networks.

    • Retail inventory and supply-chain logistics (such as IKEA).

    • University administrative and course registration infrastructure (such as European University Cyprus).

  • The role of software engineering in data structure design:

    • Data structures provide foundational building blocks for managing data within complex software.

    • Robust software engineering principles (including design patterns such as Model-View-Controller [MVC]) are essential as complexity increases; writing large-scale enterprise platforms (like Windows 10) as single, monolithic scripts using primitive types would result in unmaintainable software.

Principles of Algorithm Analysis and Resource Efficiency

  • Algorithmic efficiency evaluates an algorithm's consumption of computational resources.

  • Core computational resource metrics:

    • Time complexity: The amount of CPU processing time an algorithm requires to execute.

    • Space complexity: The amount of physical memory (RAM) consumed during execution.

    • CPU time is generally the primary focus of algorithmic efficiency analysis.

  • Defining problem size (nn):

    • Algorithm analysis evaluates performance relative to an input size parameter, denoted as nn.

    • In search algorithms: nn represents the total size of the search pool.

    • In sorting algorithms: nn represents the total number of items to be sorted.

    • In dishwashing workflows: nn represents the total number of dishes to be cleaned and dried.

  • The dishwashing metaphor (linear vs. quadratic scaling):

    • Baseline linear algorithm:

    • Washing an individual dish takes 30 seconds30\text{ seconds}.

    • Drying that dish takes an additional 30 seconds30\text{ seconds}.

    • Processing one dish requires 60 seconds60\text{ seconds} (1 minute1\text{ minute}).

    • To process nn dishes under this linear sequence requires n minutesn\text{ minutes}, representing an O(n)O(n) time complexity.

    • Inefficient quadratic algorithm:

    • After washing each new dish, the washer re-dries all previously washed dishes.

    • Mathematical derivation of total elapsed execution time for nn dishes:

time(n dishes)=n×(30 seconds wash time)+∑i=1n(i×30)\text{time}(n\text{ dishes}) = n \times (30\text{ seconds wash time}) + \sum_{i=1}^{n} (i \times 30)

time(n dishes)=30n+30n(n+1)2\text{time}(n\text{ dishes}) = 30n + \frac{30n(n+1)}{2}

time(n dishes)=30n+15n(n+1)=30n+15n2+15n\text{time}(n\text{ dishes}) = 30n + 15n(n+1) = 30n + 15n^2 + 15n

time(n dishes)=15n2+45n seconds\text{time}(n\text{ dishes}) = 15n^2 + 45n\text{ seconds}

  • Scaling comparison of term contributions across problem sizes (nn):

    • At n=1n = 1:

    • 15n2=1515n^2 = 15

    • 45n=4545n = 45

    • Total =60 seconds= 60\text{ seconds}

    • At n=2n = 2:

    • 15n2=6015n^2 = 60

    • 45n=9045n = 90

    • Total =150 seconds= 150\text{ seconds}

    • At n=5n = 5:

    • 15n2=37515n^2 = 375

    • 45n=22545n = 225

    • Total =600 seconds= 600\text{ seconds}

    • At n=10n = 10:

    • 15n2=1,50015n^2 = 1,500

    • 45n=45045n = 450

    • Total =1,950 seconds= 1,950\text{ seconds}

    • At n=100n = 100:

    • 15n2=150,00015n^2 = 150,000

    • 45n=4,50045n = 4,500

    • Total =154,500 seconds= 154,500\text{ seconds}

    • At n=1,000n = 1,000:

    • 15n2=15,000,00015n^2 = 15,000,000

    • 45n=45,00045n = 45,000

    • Total =15,045,000 seconds= 15,045,000\text{ seconds}

    • At n=10,000n = 10,000:

    • 15n2=1,500,000,00015n^2 = 1,500,000,000

    • 45n=450,00045n = 450,000

    • Total =1,500,450,000 seconds= 1,500,450,000\text{ seconds}

    • At n=100,000n = 100,000:

    • 15n2=150,000,000,00015n^2 = 150,000,000,000

    • 45n=4,500,00045n = 4,500,000

    • Total =150,004,500,000 seconds= 150,004,500,000\text{ seconds}

    • At n=1,000,000n = 1,000,000:

    • 15n2=15,000,000,000,00015n^2 = 15,000,000,000,000

    • 45n=45,000,00045n = 45,000,000

    • Total =15,000,045,000,000 seconds= 15,000,045,000,000\text{ seconds}

    • At n=10,000,000n = 10,000,000:

    • 15n2=1,500,000,000,000,00015n^2 = 1,500,000,000,000,000

    • 45n=450,000,00045n = 450,000,000

    • Total =1,500,000,450,000,000 seconds= 1,500,000,450,000,000\text{ seconds}

  • Analytical conclusion: As nn scales upward, the quadratic term 15n215n^2 dominates the overall time, while the linear term 45n45n becomes negligible.

Growth Functions and Asymptotic Analysis

  • Definition of a growth function:

    • A growth function shows the mathematical relationship between the size of the problem (nn) and the resources (CPU time or memory space) required to solve it.

    • Deriving the exact algebraic growth function (such as T(n)=15n2+45nT(n) = 15n^2 + 45n) is rarely necessary in practical algorithm analysis.

  • Asymptotic complexity:

    • Examines how resource requirements change as the input size nn grows arbitrarily large.

    • Governed by the dominant term of the growth function—the specific term that increases most rapidly as nn approaches infinity.

    • In the growth function T(n)=15n2+45nT(n) = 15n^2 + 45n, the dominant term is n2n^2.

  • Big-O notation:

    • Constant coefficients and lower-order terms become increasingly irrelevant as nn increases, and are omitted.

    • If an algorithm's dominant growth term is n2n^2, it has order n2n^2, expressed in Big-O notation as O(n2)O(n^2).

    • Algorithms within the same Big-O classification share the same asymptotic growth profile, but they do not necessarily have identical growth functions or identical performance on small inputs.

  • Big-O complexity classes:

    • Constant complexity:

t(n)=17  ⟹  O(1)t(n) = 17 \implies O(1)

  • Logarithmic complexity:

t(n)=3log⁡(n)  ⟹  O(log⁡(n))t(n) = 3\log(n) \implies O(\log(n))

  • Linear complexity:

t(n)=20n−4  ⟹  O(n)t(n) = 20n - 4 \implies O(n)

  • Linearithmic (nlog⁡(n)n\log(n)) complexity:

t(n)=12nlog⁡(n)+100n  ⟹  O(nlog⁡(n))t(n) = 12n\log(n) + 100n \implies O(n\log(n))

  • Quadratic complexity:

t(n)=3n2+5n−2  ⟹  O(n2)t(n) = 3n^2 + 5n - 2 \implies O(n^2)

  • Cubic complexity:

t(n)=8n3+3n2  ⟹  O(n3)t(n) = 8n^3 + 3n^2 \implies O(n^3)

  • Exponential complexity:

t(n)=2n+18n2+3n  ⟹  O(2n)t(n) = 2^n + 18n^2 + 3n \implies O(2^n)

  • Relative growth-rate hierarchy:

    • The standard efficiency classes rank from lowest resource growth to highest resource growth as follows:

O(1)<O(log⁡2(n))<O(n)<O(nlog⁡2(n))<O(n2)<O(n3)<O(2n)O(1) < O(\log_2(n)) < O(n) < O(n\log_2(n)) < O(n^2) < O(n^3) < O(2^n)


Comparison of typical growth functions plotting growth-rate value against problem size n

Code-Level Complexity Analysis and Loop Execution Patterns

  • Fundamental loop execution principles:

    • A loop executes a specified number of times (e.g., nn times).

    • The total complexity of a single loop equals nn multiplied by the complexity of the body of the loop:

Loop Complexity=(Iteration Count)×(Body Complexity)\text{Loop Complexity} = (\text{Iteration Count}) \times (\text{Body Complexity})

  • When loops are nested, the body of the outer loop includes the total operational complexity of the inner loop.

    • Pattern 1: Standard linear loop (O(n)O(n)):

  • Code implementation:

for (int count = 0; count < n; count++)
    x = x + 1; // some sequence of O(1) steps
  • Analysis: The loop condition and increment execute nn times; the body contains constant-time operations (O(1)O(1)). Multiplying nn executions by O(1)O(1) operations yields O(n)O(n) efficiency.

    • Pattern 2: Detailed exact step-cost analysis (array summation):

  • Code implementation:

int list_Sum(int A[], int n) {
    int sum = 0;               // cost=1, no of times=1
    for (int i = 0; i < n; i++) // cost=2, no of times=n+1 (+1 for the final false condition)
        sum = sum + A[i];      // cost=2, no of times=n
    return sum;                // cost=1, no of times=1
}
  • Step-by-step cost derivation:

Total Cost=1+2(n+1)+2n+1\text{Total Cost} = 1 + 2(n + 1) + 2n + 1

Total Cost=1+2n+2+2n+1=4n+4\text{Total Cost} = 1 + 2n + 2 + 2n + 1 = 4n + 4

Total Cost=C1n+C2  ⟹  O(n)\text{Total Cost} = C_1 n + C_2 \implies O(n)

  • Note on the loop condition: The test expression i < n executes n+1n + 1 times because the final evaluation evaluates to false when i=ni = n, triggering loop termination.

    • Pattern 3: Logarithmic counter doubling (O(log⁡(n))O(\log(n))):

  • While loop structure:

count = 1;
while (count < n) {
    count *= 2;
    // some sequence of O(1) steps
}
  • Analysis: The variable count doubles during each pass. The loop executes log⁡2(n)\log_2(n) times, resulting in an overall complexity of O(log⁡(n))O(\log(n)).

  • For loop structure with trace analysis:

for (i = 1; i <= n; i = i * 2) {
    cout << "Hello World ! ! !
";
}
  • Step-by-step execution trace for n=8n = 8:

    • Iteration 1: Initial i=1i = 1; evaluation 1≤81 \le 8 is True; outputs Hello World !!!

    • Iteration 2: Next i=1×2=2i = 1 \times 2 = 2; evaluation 2≤82 \le 8 is True; outputs Hello World !!!

    • Iteration 3: Next i=2×2=4i = 2 \times 2 = 4; evaluation 4≤84 \le 8 is True; outputs Hello World !!!

    • Iteration 4: Next i=4×2=8i = 4 \times 2 = 8; evaluation 8≤88 \le 8 is True; outputs Hello World !!!

    • Iteration 5: Next i=8×2=16i = 8 \times 2 = 16; evaluation 16≤816 \le 8 is False; loop terminates.

  • Conclusion: For n=8n = 8, the statement executes 4 times (log⁡2(8)+1\log_2(8) + 1 iterations), confirming O(log⁡(n))O(\log(n)) time complexity.

    • Pattern 4: Iterative squaring (O(log⁡(log⁡(n)))O(\log(\log(n)))):

  • Code implementation:

for (i = 2; i <= n; i = pow(i, 2)) {
    cout << "Hello World ! ! !
";
}
  • Step-by-step execution trace for n=8n = 8:

    • Iteration 1: Initial i=2i = 2; evaluation 2≤82 \le 8 is True; outputs Hello World !!!

    • Iteration 2: Next i=pow(2,2)=4i = \text{pow}(2, 2) = 4; evaluation 4≤84 \le 8 is True; outputs Hello World !!!

    • Iteration 3: Next i=pow(4,2)=16i = \text{pow}(4, 2) = 16; evaluation 16≤816 \le 8 is False; loop terminates.

  • Conclusion: For n=8n = 8, the statement executes 2 times, confirming a doubly logarithmic time complexity of O(log⁡(log⁡(n)))O(\log(\log(n))).

    • Pattern 5: Nested loops (O(n2)O(n^2)):

  • Standard nested structure:

for (int count = 0; count < n; count++)
    for (int count2 = 0; count2 < n; count2++) {
        // some sequence of O(1) steps
    }
  • Analysis: Both the outer and inner loops possess individual complexities of O(n)O(n). Multiplying the outer execution count by the inner loop complexity yields n×O(n)=O(n2)n \times O(n) = O(n^2).

  • Nested structure with statements in the outer body:

for (int i = 0; i < n; i++) { // n times
    x = x + 1; // some sequence of O(1) steps
    for (int j = 0; j < n; j++) { // n times
        y = y - 1; // some sequence of O(1) steps
    }
}
  • Analysis: The outer loop executes nn times. The body of the outer loop consists of an O(1)O(1) statement plus an inner loop running nn times (O(n)O(n)). Total outer loop body complexity is O(1)+O(n)=O(n)O(1) + O(n) = O(n). Multiplying by the nn outer executions yields an overall efficiency of O(n2)O(n^2).

    • Pattern 6: Method calls inside loop bodies:

  • When a loop body invokes external methods, the computational order of the invoked method must be factored into the overall complexity of the loop body.

  • The operational overhead associated with executing the method call mechanism itself is typically ignored as an O(1)O(1) constant factor in asymptotic evaluation.