CS 446: Software Design and Architectures

Shane McIntosh

Estimated study time: 50 minutes

Table of contents

Stakeholders and Non-Functional Properties

2.1 Who Is a System Stakeholder?

A stakeholder is broadly defined as a person with an interest or concern in something. In Rozanski and Woods’ more precise formulation: a stakeholder in the architecture of a system is an individual, team, organization, or classes thereof, having an interest in the realization of the system.

The key insight is that stakeholders extend far beyond the customer who uses the software and the developers who build it. Consider the ION light rail ticket vending machine in Waterloo — a system that, on the surface, seems to serve two groups. In reality its stakeholder community is much larger.

Customer-side stakeholders include the commuter who buys a single ticket, those paying by cash or credit, those buying a refillable card, and the Region of Waterloo that funded the entire system.

Developer-side stakeholders include software developers, user interface designers, test engineers, release engineers, operators, maintenance engineers, hardware engineers, and senior management — each with their own perspective on what the system should prioritise.

Additional stakeholders include: the person who physically installs and inspects the machines; the person who collects cash from them; network engineers who keep the machines connected; electricians who supply power; customer service operators who field complaints; and city officials who liaise with the vendor on upgrades and fixes.

The TL;DR: systems are more complex than they initially appear, and their stakeholder communities are larger than first imagined. Every stakeholder has some say in how some part of the system is designed and built — and their priorities frequently conflict.

2.2 Why Stakeholders Matter for Architecture

Different stakeholders have different concerns that may not be compatible with each other:

StakeholderTypical Question
ManagementAre we on schedule?
DevelopersWho is responsible for what?
SalesCan we claim it does X?
QAWhich team do we contact about defects?
DevOpsWhere should this component be deployed?
SupportWhich QA team signed off on this?
MaintenanceHow can we add this feature?

It is impossible to satisfy all stakeholders simultaneously, and it may be impossible to talk to all of them. At a minimum, an architect should identify who the stakeholders are and consider what their concerns might be — even if only a subset can be consulted directly. Ignoring a stakeholder (e.g., machine inspectors who need audit logs) leads to design decisions that make their work difficult or impossible.

2.3 Non-Functional Properties (NFPs)

System requirements fall into two broad categories:

  • Functional Properties (FPs) — what the system is supposed to do (“the system shall do X”). These are features.
  • Non-Functional Properties (NFPs) — what the system is supposed to be (“the system shall be Y”). These are the constraints under which the system delivers its functionality.

NFPs constrain the manner in which the system implements and delivers its features (Tailor et al.). Products are sold based on their FPs, but NFPs often determine whether users continue to use a product: “This program keeps crashing,” “It doesn’t work with my phone,” “It’s too slow” are all NFP failures.

Key NFP Definitions

NFPDescription
Efficiency / PerformanceHow quickly the system responds to inputs and processes requests
ComplexityThe degree to which the system’s structure is understandable
ScalabilityAbility to handle increasing load without degrading performance
HeterogeneityAbility to operate across diverse hardware, OS, or network environments
PortabilityEase with which the system can be moved to a new environment
EvolvabilityEase with which the system can be modified to accommodate new requirements
ReadabilityEase with which the codebase can be understood by developers
SecurityAbility to prevent unauthorised access or manipulation
PrivacyAbility to protect personal data
UsabilityEase with which users can accomplish tasks
AccessibilityAbility to serve users with disabilities
ReliabilityProbability that the system performs correctly over a period of time
AvailabilityProportion of time the system is operational
RobustnessAbility to handle erroneous inputs or unexpected conditions gracefully
Fault-toleranceAbility to continue operating in the presence of faults
SurvivabilityAbility to recover from catastrophic failure
SafetyAbility to avoid states that cause physical harm or damage

Specifying NFPs Concretely

Vague NFP statements are useless for validation. An architect must think concretely about how each NFP would be measured:

  • Bad: “The system shall issue tickets quickly.”
  • Good: “The time it takes for a ticket to be issued will be under 10 seconds.”
  • Bad: “The system shall be usable.”
  • Good: “A user shall be able to buy a ticket within 4 clicks.”

If an NFP cannot be operationalised as a measurable criterion, it is impossible to evaluate whether an architectural decision supports or inhibits it.

NFP Trade-offs

Stakeholders have different NFP priorities. The development team cares more about maintainability; customers care more about usability; QA cares more about testability. Some NFPs fundamentally conflict:

Trade-offExample
Functionality vs. UsabilityAdding features increases cognitive load
Efficiency vs. PortabilityPlatform-specific optimisations reduce portability
Reusability vs. CostGeneric components take longer to build

Architectural decisions are often exercises in navigating these tensions — choosing which NFPs to optimise and which to trade away.


Introduction to Software Architecture

3.1 The Software Development Life Cycle

Software development is commonly divided into five phases:

  1. Requirements — gathering and specifying what the system must do
  2. Architecture — high-level structure and organisation of the system
  3. Design — detailed, implementation-level decisions
  4. Implementation — writing code
  5. Quality Assurance — testing, verification, validation

Undergraduate education typically emphasises phases 3–5. However, the most costly problems tend to originate in phases 1–3, long before a single line of code is written. This course focuses on phase 2 — software architecture — and the design decisions that shape the rest of development.

3.2 What Is Architecture?

The Britannica definition covers both process and product: “Architecture is both the process and the product of planning, designing, and constructing buildings or any other structures.”

The Roman architect Vitruvius (1st century AD) articulated three principles that have endured for two millennia:

  • Durability — the structure must stand up robustly
  • Utility — it must be useful for its intended purpose
  • Beauty — it must be a source of pleasure to the senses

All three apply directly to software systems.

3.3 Why Architecture Is Needed

Not every system requires explicit architectural planning. Simple, well-understood systems — a pocket calculator, a dog house — can be built without a blueprint. But large, complex systems absolutely require it:

  • The New York Stock Exchange (NYSE) processes millions of transactions per second under regulatory constraints.
  • The CN Tower required careful structural engineering before a single beam was placed.

Similarly, complex software systems cannot be reliably built without an architectural blueprint.

3.4 The Software Architect

According to Tailor et al., the software architect must be:

  • Broadly trained across all lifecycle phases (requirements, design, implementation, testing, deployment)
  • Aesthetically minded — architecture involves choices that are both technical and elegant
  • Equipped with deep domain knowledge, strong coding skills, and knowledge of the deployment platform

The architect provides three things for a project:

  1. Intellectual control and conceptual integrity — ensuring the system coheres as a whole
  2. Leverage with senior stakeholders — translating technical constraints into business language
  3. Team communication and management — providing a shared vocabulary and structure

3.5 The Architecture-as-Blueprint Analogy

Like a building architect, a software architect:

  • Iterates on blueprints before construction begins
  • Produces intermediate plans, mockups, and prototypes
  • Recognises that structure induces properties: just as tall castle walls cause unexpected airflow problems, a client–server structure necessarily introduces network latency

3.6 Differences from Building Architecture

DimensionBuilding ArchitectureSoftware Architecture
Age of disciplineMillenniaDecades
MaterialPhysical (stone, steel, wood)Intangible
DeliveryThe building itselfDeployed/released binary
Change expectationChange is expensive, rareChange is expected, routine
Expertise differentiationArchitects vs. builders (legal barrier)Anyone can write code

The intangibility of software is simultaneously its greatest strength and greatest weakness: there are no physical laws constraining a design, but this means discipline must be self-imposed.

3.7 What Architecture IS and IS NOT

Architecture IS:

  • A means of communication among stakeholders
  • The definition of a system’s parts and how they fit together
  • Focus on aspects that are difficult to change once the system is built

Architecture IS NOT:

  • About individual algorithms
  • About data structures
  • About development methodology

“All architecture is design, but not all design is architecture.”

Architecture sits at the intersection of three primary dimensions:

  1. Structure — subsystems and components
  2. Communication — how parts interact
  3. Non-functional requirements — performance, security, scalability, maintainability

3.8 The ANSI/IEEE 1471-2000 Definition

“Architecture is the fundamental organization of a system, embodied in its [subsystems], their relationships to each other and the environment, and the principles governing its design and evolution.”

3.9 Logical vs. Physical Architecture

Consider a simple course website:

Logical architecture (what the system does):

index.html → cs846.html
           → cs446.html
           → a1.html
           → a2.html
           → project.html

Physical architecture (how it runs):

  • Files are hosted on one or more servers
  • Clients connect via the internet using HTTP/HTTPS

The same logical structure can be realised by many different physical architectures. Separating these concerns is one of the fundamental exercises in architectural thinking.

3.10 Why Software Architecture Matters

Eoin Woods captures the stakes succinctly:

“Software architecture is the set of design decisions which, if made incorrectly, may cause your project to be cancelled.”

Architecture matters because it focuses attention on aspects that are difficult — or prohibitively expensive — to change once the system is built. Retrofitting a monolith into a microservices architecture, or adding security to a system designed without it, costs orders of magnitude more than addressing these concerns upfront.


Why Software Architecture Is Difficult

4.1 The Fundamental Challenge

Philippe Krutchen, one of the leading researchers in software architecture, frames the challenge starkly:

“The life of a software architect is a long (and sometimes painful) succession of suboptimal decisions made partly in the dark.”

Architectural decisions must be made early, when information is least complete, and they have the longest-lasting consequences.

4.2 What Makes Building Systems Hard?

Three root causes (Tailor et al.):

  1. Young field — Software engineering has existed for roughly 60 years. Users cannot see or feel the constraints of a software system, so they tend to have very high and sometimes unrealistic expectations.

  2. High user expectations — Because software is invisible and perceived as “just logic,” users often underestimate what is technically feasible or affordable.

  3. Software cannot execute independently — Software relies on:

    • A software platform (operating system, language runtime, libraries) that may have defects of its own
    • Hardware that may be faulty (e.g., GPS-dependent applications break when positioning hardware fails)

4.3 Specific Difficulties

Four properties of software make it inherently hard to engineer:

PropertyDescription
ComplexityComplexity grows non-linearly with program size. Double the code, more than double the potential for unexpected interactions.
ConformitySoftware must conform to the hard and soft constraints of the environment in which it executes.
ChangeabilitySoftware is perceived to be easily modified (“we just need to change a few lines”) — this perception causes scope drift. Changeability is both a blessing and a curse.
IntangibilitySoftware is not constrained by the laws of physics. There is no natural boundary to complexity.

4.4 Attacks on These Difficulties

How does the field respond?

  • High-level languages and development tools — modern IDEs, compilers, and frameworks tame low-level complexity
  • Component-based reuse — package managers and open-source ecosystems prevent reinventing the wheel
  • Development strategies — incremental, evolutionary, and spiral models (Agile, Scrum) embrace change in a semi-structured way; MVP thinking gets early feedback before too much is invested
  • Emphasis on architecture and design — taking a design-centric approach from the outset avoids implementing solutions that don’t scale or don’t align with user expectations

Software Architecture Styles — Introduction

5.1 A Historical Perspective on Abstraction

Programming abstractions have evolved over decades to help engineers manage growing complexity:

EraAbstraction
1950sHardwired / machine language; then assembly language
1960s3rd-generation languages: FORTRAN, COBOL, Algol, Pascal, Modula, C, PL/1
1960s onward3.5GL functional languages: Scheme, Scala
Late 1960sTyped variables and user-defined types
Early 1970sModules with interfaces / information hiding (triggered by the 1968 “software crisis”)
Mid-1970sAbstract Data Types (ADTs) and Object-Oriented programming
1990sOO Design Patterns and refactoring
1990sSoftware Architecture (though the ideas existed since the 1960s)

5.2 The Abstraction Pyramid

Three levels of abstraction in software:

┌────────────────────────────────────────────────────┐
│           SW Architecture                          │  ← Components/modules + interdependencies
│         (the "big boxes")                          │
├────────────────────────────────────────────────────┤
│          Design Patterns                           │  ← Class-level recurring solutions
├────────────────────────────────────────────────────┤
│           Algorithms                               │  ← Single method / function level
└────────────────────────────────────────────────────┘

As size and complexity grow, design goes beyond algorithms and data structures. Architecture addresses the “big boxes” — the major components and how they are connected.

5.3 Types of Software Architectures

  • Reference Architecture — a general architecture for an entire application domain. Example: a compiler pipeline (pre-process → compile → link) is a reference architecture for all compilers.
  • Product Line Architecture (PLA) — an architecture for a family of related products. Example: successive entries in a game series share a PLA.

5.4 Software Architecture Issues

Key concerns at the architectural level:

  • Organisation and global control structure
  • Communication, synchronisation, and data-access protocols
  • Assignment of functionality to design elements
  • Physical distribution of components
  • Selection among design alternatives

5.5 State of Practice

The field lacks a single well-defined terminology or notation. Practitioners use:

  • Informal rules of thumb and idiomatic patterns — tacit knowledge, ad hoc conventions
  • Formal industry standards — more rigorous but less flexible

Real-world systems often mix multiple styles — the so-called “Toaster” Model: different sub-collections within the same system may follow different architectural styles.

5.6 Building Architecture as a Parallel

Three analogous aspects of building architecture clarify software architecture:

  1. Multiple Views — a building has skeleton frame views, electrical wiring views, plumbing views, etc. Software has analogous architectural views.
  2. Architectural Styles — buildings have Classical, Romanesque, Gothic, Modernist, etc. Software has analogous named styles.
  3. Materials — one does not build a skyscraper from wooden posts and beams. In software, the choice of language, framework, and platform constrains what is feasible.

5.7 What Is a Software Architectural Style?

An Architectural Style defines a family of systems in terms of a pattern of structural organisation. It determines:

  • The vocabulary of components and connectors that can be used in instances of that style
  • A set of constraints on how they can be combined

5.8 Why Use Architectural Styles?

Three reasons:

  1. Easy communication — named styles give stakeholders a shared vocabulary. Rather than describing the entire structure from scratch, saying “this is a client–server system” communicates volumes.
  2. Documentation of early design decisions — capturing the chosen style early preserves the reasoning behind major structural choices.
  3. Reuse and transfer of qualities — proven styles carry known quality attributes. Choosing a pipe-and-filter style immediately imports its known advantages (parallelisability, reusability of filters) and disadvantages (no shared state).

Roy T. Fielding (co-author of HTTP and inventor of REST) captured the right attitude toward styles:

“Architectural styles are named so we can easily discuss & contrast them, not so they can be carried on banners or elevated to purity.”

Use styles to communicate the general idea. Be ready to break them when needed — but be aware of the consequences.

5.9 Describing an Architectural Style for a System

The architecture of a specific system is a collection of:

  • Computational components (nodes in a graph): e.g., Procedures, Modules, Processes, Tools, Databases
  • Connectors (edges in a graph): e.g., procedure calls, event broadcasts, database queries, pipes

Together these form a topology.

5.10 Determining an Architectural Style

To fully understand and evaluate a given style, answer six questions:

  1. What is the structural pattern? (components, connectors, constraints)
  2. What is the underlying computational model?
  3. What are the essential invariants of the style?
  4. What are common examples of its use?
  5. What are the advantages and disadvantages of using this style?
  6. What are common specialisations of this style?

5.11 Architectural Styles Covered in This Course

CategoryStyles
Pipe-and-filterPipelines, Batch Sequential
Data-centredRepository, Blackboard
Implicit invocationPublish/Subscribe, Event-Based
Layered
Client–server
Process-control

Architectural Views and Decomposition

6.1 Software Architecture: A Working Definition

Software architecture = the set of principal design decisions about a system.

Every system has an architecture, whether explicitly documented or not. The architect’s job is to make those decisions deliberately and to record them.

6.2 Core Vocabulary

TermDefinition
SubsystemA meaningful grouping of components with a defined interface
ComponentA computational unit with an interface; can be a module, process, class, service
ConnectorA mechanism for communication between components (method call, pipe, event bus, HTTP request)
Configuration (Topology)The arrangement of components and connectors; the graph structure

6.3 Topological Goals

  • Minimise coupling — components should depend on as few other components as possible
  • Maximise cohesion — elements within a component should be strongly related and focused on a single concern

6.4 Abstraction

Two types of abstraction are fundamental:

  • Control abstraction — structured programming (procedures, loops, conditions) hides the details of control flow
  • Data abstraction — ADTs and OOP hide the internal representation of data behind an interface

Architectural decomposition is an application of data abstraction at the system level.

6.5 Decomposition

Decomposition is top-down abstraction: breaking a large problem into smaller, more manageable sub-problems.

Criteria for decomposing a system:

  • Team characteristics — match modules to team expertise and organisational structure
  • Application domain — decompose along natural boundaries in the problem domain
  • Parallelisation — identify independent sub-problems that can be developed concurrently

Goal: typical cases should be simple to handle; exceptional cases should at least be possible.

Conway’s Law

“The structure of a software system reflects the structure of the organisation that built it.”

Organisations communicate along certain channels. The software they produce will tend to have interfaces (or seams) in the same places. This is not a critique — it is an empirical observation with design implications: if you want a modular architecture, organise your teams modularly.

When Decomposition Doesn’t Work

Decomposition is powerful but not universal:

  • Works well for systems like a restaurant menu (each dish is independent)
  • Does not always work for creative artefacts (characters in a play cannot write in isolation — their dialogue depends on each other)
  • Sometimes impossible: complex, deeply coupled, or fundamentally atomic problems

6.6 Architectural Representations

An architecture that cannot be communicated has no value. Representations facilitate technical communication.

Three properties of representations:

PropertyDefinition
AmbiguityOpen to multiple valid interpretations
AccuracyCorrect within acceptable tolerances
PrecisionConsistent, but not necessarily accurate (systematic but may be wrong)

A good architectural representation is unambiguous, accurate, and precise.

6.7 Prescriptive vs. Descriptive Architecture

TypeAlso calledMeaning
PrescriptiveAs-conceived (a priori)The intended architecture; the blueprint
DescriptiveAs-implemented (a posteriori)The actual architecture extracted from the running system

In healthy projects these are the same. In unhealthy projects they diverge.

6.8 Architectural Degradation

Two forms of divergence between prescriptive and descriptive architecture:

FormDefinition
Architectural DriftChanges to the implementation that are not captured in the prescriptive architecture, but do not violate it
Architectural ErosionChanges that violate the prescriptive architecture

Common causes:

  • Release pressure (shipping fast, not updating docs)
  • Absence of a prescriptive architecture in the first place
  • Local optimisations by individual developers that make global sense only to them

Erosion is the more dangerous of the two: it means the system no longer behaves as designed.

6.9 Architectural Views

A single architectural model of a large system would be overwhelming. Views are projections of the architecture that focus on a subset of elements and their relationships:

  • Different views emphasise different concerns (static structure, runtime behaviour, deployment)
  • Views overlap — the same component may appear in multiple views
  • Maintaining consistency between views is a significant challenge

UML for Software Architecture

UML (Unified Modelling Language) provides five diagram types commonly used for architectural views.

Martin Fowler identified two “religions” of UML:

  • UML as Blueprint — forward engineering; the diagram drives the code
  • UML as Programming Language — the diagram is the executable specification

In practice, UML is most useful as a communication and documentation tool.

7.1 Class Diagrams (Design-Level View)

Class diagrams show the static structure of a design: classes, their attributes, methods, and relationships.

Relationships:

RelationshipUML SymbolMeaning
DependencyDashed arrowOne class uses another (weak coupling)
GeneralizationSolid arrow with hollow triangleInheritance / “is-a”
AssociationSolid line“has-a” or “uses-a” structural relationship
AggregationSolid line with hollow diamondWeak whole–part (“has-a”, part can exist independently)
CompositionSolid line with filled diamondStrong whole–part (part cannot exist without the whole)

Association details:

  • Multiplicity1, 0..1, *, 1..*, etc., placed at each end of the association line
  • Role names — labels at the ends describing the role of each class in the relationship
  • Association classes — a class attached to an association line, representing attributes of the relationship itself
  • Self-association — a class associated with itself (e.g., an Employee manages other Employees)

Other notations:

  • <<interface>> stereotype for interfaces
  • <<enumeration>> for enumerations
  • <<exception>> for exception classes
  • Packages — rectangles with a tab, used to group related classes

7.2 Component Diagrams (Physical Static Implementation View)

Component diagrams show the physical organisation of software components and their dependencies.

Notation:

  • Page-shaped blocks — shared libraries or compiled modules
  • Circles — connectors / interfaces exposed by a component
  • Arrows — dependency relationships (A depends on B)

Example: A collision detection library:

PATH.DLL → COLLISION.DLL → DRIVER.DLL
                               ↑ (conditional, via IDRIVE interface)

7.3 Deployment Diagrams (Static Deployment View)

Deployment diagrams show the run-time processing nodes and how software components are assigned to them.

Notation:

  • Block shapes (cubes) — processing nodes (physical machines or containers)
  • Circles — interfaces
  • Realization dependency — interface fulfilled by a component
  • Usage dependency — component uses an interface

Examples:

  • DICTIONARY → SPELL-CHECK / SYNONYMS (interfaces on the DICTIONARY node)
  • ATM-GUI → UPDATE → TRANSACTIONS
  • A client–server system with SERVER:HOSTMACHINE and CLIENTMACHINE:PC nodes

7.4 Use Case Diagrams

Use case diagrams capture the functional requirements from the actor’s perspective.

Notation:

  • Ellipse with name — a use case
  • Stick figure — an actor
  • Bidirectional association — actor participates in use case
  • Actor generalisation — e.g., Student → Person (Student is a Person)
  • Primary actors on the left; secondary actors (e.g., external systems) on the right

Example:

Student ——— Register For Courses ——— Billing System
                                    Registrar

7.5 State Machine Diagrams

State machine diagrams describe the dynamic behaviour of objects over time, modelling an object’s lifecycle.

Notation:

  • Filled circle — start state (exactly one per machine)
  • Filled circle with ring — final state (one or more)
  • Rounded rectangle — simple state
  • Rounded rectangle with dashed horizontal divider — concurrent composite state (regions execute in parallel)
  • Nested rounded rectangles — sequential composite state

Example (course registration flow):

● → Unscheduled → Downloading → Selecting → Verifying
  → Checking Schedule → Scheduled → ◎

7.6 Sequence Diagrams

Sequence diagrams show time-ordered interactions among objects.

Notation:

  • Objects on the x-axis; time on the y-axis (flows downward)
  • Object lifeline — dashed line descending from the object box
  • Messages — horizontal arrows (solid arrowhead = synchronous call; non-solid arrowhead = return)
  • Conditions — expressed in [guard] before an arrow
  • Return values — non-solid arrowhead, typically unlabelled
  • Iteration* marker or *[i=1..N]
  • Self-delegation — an object sending a message to itself

Example (order management):

Order Entry Window → Order → * Order Line → Stock Item → (self) → Reorder Item
                                                                  Delivery Item

7.7 Collaboration Diagrams

Collaboration diagrams (also called communication diagrams) emphasise object relationships rather than time order.

  • Objects are identified as objectname : classname
  • Messages are labelled with sequence numbers instead of positioned by time
  • Semantically equivalent to sequence diagrams (both derived from the same UML meta-model)

7.8 Activity Diagrams

Activity diagrams are essentially flowcharts for modelling workflows.

“An activity is an ongoing non-atomic execution within a state machine.”

Notation:

  • Solid horizontal bars — fork (one flow splitting into parallel flows) or join (parallel flows converging)
  • Synchronisation conditions — placed at join bars
  • Activities connected by directed arrows

Example (order management):

Receive Order → fork → Authorize Payment   → join [payment authorised
                      → Check Line Items   →      AND items available] → Dispatch Order

Object-Oriented Design Patterns — Introduction

8.1 What Are Design Patterns?

A design pattern is a reusable, named solution to a commonly occurring problem in a given context. Patterns capture the collective experience of practitioners — they are not invented but discovered by observing recurring good designs.

The canonical reference is the Gang of Four (GoF) book (Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides), which catalogues 23 patterns in three categories:

CategoryFocusExamples
CreationalObject creationSingleton, Factory Method, Abstract Factory, Builder, Prototype
StructuralObject compositionAdapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
BehaviouralCommunication/responsibilityObserver, Strategy, Command, Iterator, Template Method, etc.

8.2 Design Goals

  • High cohesion — each class has a single, focused responsibility
  • Low coupling — classes depend on as few other classes as possible
  • YAGNI principle — “You Ain’t Gonna Need It”: do not add functionality speculatively; add it when needed

8.3 Code Smells

Code smells are symptoms of poor design that may warrant refactoring:

SmellDescription
Code DuplicationThe same logic appears in multiple places
Multiple-Personality ClassA class trying to do too many unrelated things
Blob / God ObjectOne class that knows and does everything
Data ClassA class with only fields and getters/setters, no behaviour
Data ClumpGroups of data that always appear together but are not encapsulated
Feature EnvyA method that is more interested in the data of another class than its own
Tradition BreakerA subclass that overrides most parent methods, suggesting the hierarchy is wrong

Structural Design Patterns

9.1 The Adapter Pattern

Category: Structural

Intent: Convert the interface of a class into another interface that clients expect. Adapter lets classes work together that could not otherwise because of incompatible interfaces.

Motivation: You have an existing class (the Adaptee) with a useful implementation but the wrong interface. A Target interface is what clients expect. The Adapter sits between them.

Analogies:

  • A power plug adapter converts a two-pin plug to a three-pin socket — the appliance (adaptee) still works, but through a different interface.
  • Translating from British English to American English: the content is the same, but the vocabulary (interface) differs.

Two forms:

  1. Class Adapter (uses multiple inheritance):

    Adapter extends Adaptee implements Target
    
    • +request() in Adapter calls specificRequest() inherited from Adaptee
    • Works only with a single concrete Adaptee; can override Adaptee behaviour
  2. Object Adapter (uses composition):

    Adapter implements Target, holds a reference to Adaptee
    
    • +request() delegates to adaptee.specificRequest()
    • More flexible: works with Adaptee and all its subclasses

Participants:

  • Target — the interface clients use
  • Adaptee — the existing class with an incompatible interface
  • Adapter — translates Target requests into Adaptee calls
  • Client — collaborates with objects through the Target interface

When to use:

  • Reusing an existing class whose interface doesn’t match what is needed
  • Creating a reusable class that cooperates with classes that don’t share a compatible interface
  • Adapting several existing subclasses by wrapping a parent class (object adapter)

Consequences:

  • Class Adapter: more compact, but only works with concrete Adaptee
  • Object Adapter: more flexible, adds an indirection level

9.2 The Bridge Pattern

Category: Structural

Intent: Decouple an abstraction from its implementation so the two can vary independently.

Motivation: Inheritance couples an abstraction permanently to its implementation. If you want to switch implementations (e.g., render a UI on different platforms), inheritance forces a proliferation of subclasses. Bridge replaces that inheritance relationship with a composition relationship.

Key insight: Instead of extending a class to add a new implementation, Bridge holds an interface reference to an independent implementation hierarchy.

Structure:

Abstraction (holds reference to Implementor interface)
    ↓ uses
Implementor <<interface>>
    ↙                 ↘
ConcreteImplementorA    ConcreteImplementorB

Refined Abstraction is a subclass of Abstraction that adds behaviour while still delegating implementation-specific work to the Implementor.

Example (Window / UI toolkit):

  • Window (abstraction) has IconWindow and TransientWindow subclasses
  • WindowImp (implementor) has XWindowImp and PMWindowImp subclasses
  • Any combination of window type and platform can be assembled at runtime

When to use:

  • Want to avoid a permanent binding between abstraction and implementation
  • Both abstraction and implementation should be extensible through subclassing
  • Changes in implementation should have no impact on clients
  • Want to hide implementation details completely

Consequences:

  • Decouples interface and implementation
  • Improves extensibility
  • Hides implementation details from clients

9.3 The Composite Pattern

Category: Structural

Intent: Compose objects into tree structures to represent part–whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

Motivation: Drawing programs mix individual shapes (Line, Circle) with groups of shapes. Client code should not need to distinguish between “I’m operating on a single shape” vs. “I’m operating on a group of shapes.”

Structure:

Component <<interface/abstract>>: +draw(), +add(Component), +remove(Component)
     ↙                ↘
  Leaf              Composite (has children: List<Component>)
(no children)       +draw() iterates over children and calls their draw()

Key property: Both Leaf and Composite implement the same Component interface. A Composite’s operation simply delegates to each of its children — which may themselves be Composite instances. This gives recursive tree behaviour.

Example (drawing editor):

  • Picture = Composite containing Line, Circle, Rectangle (Leaves) and other Pictures (Composites)

Participants:

  • Component — declares the common interface; implements default behaviour if appropriate; declares interface for accessing/managing children
  • Leaf — has no children; defines behaviour for primitive objects
  • Composite — stores children, implements child-related operations, and implements Component operations by delegating to children
  • Client — manipulates objects through the Component interface

When to use:

  • Representing part–whole hierarchies
  • Clients should ignore differences between individual objects and compositions

Consequences:

  • Makes client code simple (treats leaves and composites uniformly)
  • Easy to add new kinds of components
  • Can make design overly general (hard to restrict what components can contain what)

9.4 The Decorator Pattern

Category: Structural

Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Motivation: You want to add features to individual objects, not to the entire class. Subclassing can lead to a combinatorial explosion of classes for every combination of features.

Key idea: A Decorator wraps the component it decorates. It has the same interface as the component (so clients are unaffected) and adds behaviour before or after delegating to the wrapped component.

Structure:

Component <<interface>>: +draw()
     ↙                ↘
ConcreteComponent    Decorator (holds Component reference)
                          ↙                ↘
                 ConcreteDecoratorA    ConcreteDecoratorB

Decorator’s draw() method:

// Before behaviour (optional)
component.draw();        // delegate to wrapped component
// After behaviour (optional)

Example (text with visual enhancements):

  • TextView (ConcreteComponent)
  • BorderDecorator adds a border around whatever it wraps
  • ScrollDecorator adds scroll bars to whatever it wraps
  • ScrollDecorator(BorderDecorator(TextView)) produces a scrollable, bordered text view — no subclass explosion needed

When to use:

  • Add responsibilities to objects without affecting other objects
  • Extend functionality when subclassing is impractical (class explosion or not desired)
  • Withdraw responsibilities dynamically

Consequences:

  • More flexible than static inheritance
  • Avoids feature-laden classes high in the hierarchy
  • Decorated component and its decorator are not identical objects (identity may matter)
  • Many small objects (each decorator is a separate object) can be confusing

9.5 The Proxy Pattern

Category: Structural

Intent: Provide a surrogate or placeholder for another object to control access to it.

Motivation: Sometimes a client should not (or cannot) directly reference an object. A Proxy acts as a stand-in, forwarding requests to the real object when appropriate.

Three types of proxy:

TypePurposeExample
Remote ProxyRepresents an object in a different address space (another machine)Java RMI stub
Virtual ProxyCreates expensive objects on demandImage placeholder that loads the full image only when displayed
Protection ProxyControls access based on permissionsA proxy that checks whether a client has the right to call a method

Structure:

Subject <<interface>>: +request()
     ↙                ↘
RealSubject          Proxy (holds reference to RealSubject)
+request()           +request() → [optionally] realSubject.request()

Participants:

  • Subject — common interface for Proxy and RealSubject, so Proxy can substitute
  • RealSubject — the real object being proxied
  • Proxy — keeps a reference to RealSubject; controls access; may defer creation

When to use:

  • Lazy initialisation (virtual proxy)
  • Remote access (remote proxy)
  • Access control (protection proxy)
  • Logging or caching wrappers

9.6 The Facade Pattern

Category: Structural

Intent: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Motivation:

  • Structuring a system into subsystems reduces complexity
  • A common design goal is to minimise communication and dependencies between subsystems
  • Use a facade object to provide a single, simplified entry point to a subsystem

Structure:

Client Classes
     ↓ all access through
  Facade
     ↓ delegates to
[Subsystem Classes — complex internal relationships hidden from clients]

Example (compiler programming environment):

  • Compiler.Compile() is the Facade (single, simple interface)
  • Subsystem classes: Scanner, Token, Parser, ProgNodeBuilder, ProgNode, CodeGenerator, RISCCG, StackMachineCG, StatementNode, ExpressionNode, VariableNode
  • Clients only need to call Compile() — they need not know which subsystem classes handle which parts

Participants:

  • Facade — knows which subsystem classes are responsible for a request; delegates client requests to the appropriate subsystem objects
  • Subsystem Classes — implement subsystem functionality; handle work assigned by the Facade; have no knowledge of the Facade (no reference back to it)

When to use:

  • To provide a simple interface to a complex subsystem
  • To decouple clients from implementation classes
  • To define a single entry point to a layered subsystem

Adapter vs. Facade:

PatternScopePurpose
AdapterSingle classAdds a “human face” to one grungy abstraction; does not change the adaptee
FacadeWhole subsystemPuts a human face on an entire system; usually requires modifying core code to route all access through the facade

Architectural Styles — Pipe-and-Filter, Data-Centred

10.1 Pipe-and-Filter Style

Overview: Components (called filters) transform data streams. Data flows through pipes connecting filters. Each filter is independent — it reads from its input pipe, processes data, and writes to its output pipe.

Pipe-and-Filter ArchitectureInputFilter ALexerFilter BParserFilter CCodeGenOutputpipepipepipepipeFilters share no state · Unix pipelines · Compiler stages · Signal processing

Components:

  • Filter — a data transformer; reads input, processes, produces output
    • Active filter: pulls data from input, pushes to output (runs in its own thread)
    • Passive filter: called by the pipeline; either push-driven or pull-driven

Connectors:

  • Pipe — a unidirectional data channel; can be synchronous (blocking) or buffered

Topology:

Input → [Filter A] → Pipe → [Filter B] → Pipe → [Filter C] → Output

Invariants:

  • Filters are independent — they share no state with other filters
  • Filters do not know the identity of their upstream or downstream neighbours
  • Data is transformed as it flows through the sequence

Examples:

  • Unix shell pipelines: cat file.txt | grep "error" | sort | uniq -c
  • Compiler: lexer → parser → semantic analyser → code generator → optimiser
  • Signal processing: microphone → noise filter → amplifier → compressor → speaker

Advantages:

  • Easy to understand as a simple sequence
  • Supports reuse — each filter can be reused in a different pipeline
  • Easy to maintain and evolve — add, remove, or replace filters
  • Supports concurrent execution — filters can run in parallel
  • Allows specialised filters to analyse data quality

Disadvantages:

  • Not suitable for interactive applications (filters process whole data streams)
  • Shared-state interactions are awkward (filters cannot share data structures)
  • Data format must be agreed on by adjacent filters
  • Overhead of parsing/unparsing if filters use different internal representations

Variants:

  • Batch Sequential — the “degenerate” form where each filter processes the entire dataset before the next begins (no true streaming); classic mainframe ETL pipelines

10.2 Data-Centred Style: Repository

Overview: A large, passive central data store (the repository) is shared by multiple independent computational components. Components read from and write to the repository.

Components:

  • Repository (Data Store) — the central passive data structure
  • Computational components — active agents that query and update the repository

Connectors:

  • Direct procedure calls or queries (SQL, API)
  • The repository schema is the shared communication medium

Topology:

[Comp A] ←→
[Comp B] ←→   [ Repository / Central Data Store ]
[Comp C] ←→

Invariants:

  • All data shared between components flows through the repository
  • Components do not communicate directly with each other

Examples:

  • Relational database systems (all components query the same DB)
  • Compilers with a symbol table as the shared repository
  • Information retrieval systems

Advantages:

  • Efficient storage of large data — each component does not need its own copy
  • Sharing is easy — the schema is published; all components know how to access data
  • Centralised management — supports backup, security, and concurrency control in one place

Disadvantages:

  • Must agree on a data model a priori — all components are coupled to the schema
  • Difficult to distribute — if multiple copies exist, they must stay in sync
  • Data evolution is expensive — changing the central schema requires updating all components
  • Single point of security failure — if the repository is compromised, the whole system is compromised

10.3 Data-Centred Style: Blackboard

Overview: A variant of the Repository style where the data store is active — it notifies computational components (called knowledge sources) when relevant data changes. Used for problems where the solution strategy is not known in advance.

Components:

  • Blackboard — the shared data structure; triggers knowledge sources
  • Knowledge Sources — specialist components that each solve a part of the problem; each monitors the blackboard for data relevant to it

Connectors:

  • Active notification (the blackboard triggers knowledge sources, unlike the passive repository)
  • A control component may manage the order in which knowledge sources are activated

Topology:

[KS1]                   [Control]
[KS2]  ←→  [Blackboard] ←→ inspects/drives
[KS3]

Examples:

  • Speech recognition systems (multiple specialist analysers — acoustic, phonetic, lexical, syntactic, semantic — each contribute partial interpretations)
  • AI planning systems
  • Complex event processing

Key difference from Repository: In a repository, components decide when to access shared data. In a blackboard, the blackboard (or a control component) decides when to activate knowledge sources based on the current state of the data.


Architectural Styles — Implicit Invocation, Layered, Client–Server, Process-Control

11.1 Implicit Invocation Style

Overview: Components do not invoke each other directly. Instead, a component announces an event or broadcasts data. Other components that have registered interest respond — the announcer has no knowledge of who will respond, or even whether anyone will.

Core idea: Suitable for applications where creators of data do not need to know who will use the data or how many consumers there are.

Components:

  • Data generators (publishers/emitters) — produce and broadcast data
  • Data consumers (subscribers/event handlers) — wait for and process data produced by generators

Connectors:

  • Procedure calls — used in publish-subscribe to connect subscriber to publisher at registration time
  • Event bus — the shared communication medium in event-based style

Two variants:

Variant 1: Publish-Subscribe

  • Subscribers register to receive specific messages from publishers
  • Publishers maintain a subscription list and broadcast messages to all registered subscribers
  • Subscribers connect to publishers directly (or through a network)

Topology:

  • One publisher (e.g., game server)
  • Multiple independent subscribers connected via remote procedure calls
  • Publisher broadcasts to all subscribers simultaneously

Examples:

  • Twitter — a user (publisher) posts a tweet; all followers (subscribers) receive it
  • Environment Canada — publishes weather data for each location; news services subscribe to that data

Variant 2: Event-Based

  • Components asynchronously emit and receive events over a shared event bus
  • Components do not directly talk to each other — all communication goes through the bus
  • Any component can both emit and consume events
  • Data on the bus is ephemeral — it expires once it has passed through the entire bus

Topology:

  • All components connected to the same event bus
  • Spacecraft, Clock, Game Logic, GUI — each connected to a centralised event bus
  • The bus routes events; emitters do not know which consumers will respond

Key distinction between variants: In event-based, each component can both emit and consume events. In publish-subscribe, a publisher may only publish data (though it may also subscribe to other publishers).

Examples:

  • Programming environments — the debugger stops at a breakpoint and emits an event; the editor (a separate component) responds by scrolling to the appropriate source line and highlighting it
  • GUI frameworks (button click → event → multiple listeners respond)

Advantages:

  • Strong support for reuse — any component can be added simply by registering for events; existing components need not change
  • Eases system evolution — components can be replaced without affecting others (as long as they produce/consume the same events)
  • Efficient dissemination of one-way information (publish-subscribe) — information is broadcast to all interested parties simultaneously

Disadvantages:

  • When a component announces an event:
    • It has no idea what other components will respond
    • It cannot rely on the order in which responses are invoked
    • It cannot know when responses are finished
  • Special protocols needed when number of subscribers is very large (publish-subscribe)

11.2 Layered Style

Overview: The system is organised into a hierarchy of layers. Each layer provides services to the layer above and uses services from the layer below. Direct access between non-adjacent layers is prohibited.

Layered Architecture (3-tier)Presentation LayerUI · User interaction · Views · FormsBusiness Logic LayerApplication rules · Computations · WorkflowsData LayerPersistence · Database · File system · APIsEach layer only communicates with adjacent layers

Components:

  • Layers — each layer is a coherent grouping of functionality with a well-defined interface

Connectors:

  • Procedure calls — each layer calls the interface of the layer immediately below

Topology:

Layer N    (highest — closest to user)
    ↕
Layer N-1
    ↕
  ...
    ↕
Layer 1    (lowest — closest to hardware/platform)

Invariants:

  • Layer i may only use services from layer i-1
  • Layer i may only provide services to layer i+1
  • No skip-layer access (unless explicitly permitted in a “relaxed layered” variant)

Examples:

  • ISO/OSI Network Model — 7 layers from Physical to Application
  • Operating System — hardware → kernel → system calls → user applications

Advantages:

  • Supports incremental development — build lower layers first, then build higher layers on top
  • Separation of concerns — each layer has a clear, bounded responsibility
  • Enhances portability — changing the lower layers (e.g., swapping OS) only affects the layer immediately above

Disadvantages:

  • Not all systems fit naturally into a hierarchy
  • Performance penalty — each layer adds overhead of translation/marshalling
  • Difficult to determine where to put a layer that spans multiple concerns

11.3 Client–Server Style

Overview: A server provides services; clients request those services. The server does not know about clients; clients know about the server.

Client-Server ArchitectureClient AClient BClient CServeralways-on · statefulrequest →← response

Components:

  • Client — initiates requests; has a user interface
  • Server — provides services; waits for and responds to requests; typically persistent and always-on

Connectors:

  • Remote Procedure Call (RPC) or message passing (HTTP, sockets)

Topology:

[Client 1]  ──┐
[Client 2]  ──┤ → [Server]
[Client 3]  ──┘

Invariants:

  • Communication is initiated by the client
  • The server does not know the identity of clients (in the general case)
  • Roles are asymmetric

Advantages:

  • Clear separation of concerns
  • Server is easily scaled or upgraded independently
  • Clients are lightweight

Disadvantages:

  • Server can be a bottleneck / single point of failure
  • Network latency adds overhead
  • Managing distributed state is complex

11.3a Model-View-Controller Style

MVC is a specialisation of the implicit-invocation/observer pattern applied to interactive systems. It separates the application’s data model from its presentation and from user-input handling.

MVC ArchitectureModeldata + business logicViewvisual presentationControllerhandles user inputnotifiesupdatesuser eventsMultiple views can observe one model simultaneously

11.4 Process-Control Style

Overview: Designed for real-time, embedded systems where software must continuously monitor and manipulate a physical process. A feedback loop maintains a controlled variable at a desired set point.

Components:

  • Process Definition — the physical or logical process being controlled; includes mechanisms for manipulating process variables
  • Control Algorithm — decides how to manipulate process variables to maintain the desired state

Connectors (data flow relations):

  • Process Variables:
    • Controlled variable — the variable whose value the system is intended to control (e.g., speed)
    • Input variable — measures an environmental input to the process (e.g., load on engine)
    • Manipulated variable — the variable the controller can change to affect the controlled variable (e.g., throttle setting)
  • Set Point — the desired value for the controlled variable
  • Sensors — obtain the current values of process variables

Topology (closed-loop / feedback control):

set point → [Controller] → changes to manipulated variables → [Process] → controlled variable
                ↑                                                               |
                └───────────────── sensor for controlled variable ─────────────┘
                                   (feedback loop)

In a closed-loop system, the sensor feeds back the actual controlled variable so the controller can compare it to the set point and make corrections.

Variant: Open-Loop Control System

In an open-loop system, the feedback path is absent. Information about the process variable is not used to adjust the system. The controller operates on a fixed input without observing the outcome.

set point → [Controller] → changes to manipulated variables → [Process] → controlled variable
            (no feedback)

Examples:

  • Automobile Anti-Lock Brakes (ABS) — sensors detect wheel lock; controller modulates brake pressure
  • Nuclear Power Plants — sensors monitor reactor temperature and neutron flux; control rods are adjusted
  • Automobile Cruise Control — desired speed (set point) + wheel rotation pulses (sensor) → controller → throttle setting (manipulated variable) → engine → wheel rotation (controlled variable) — feedback loop maintained

Cruise control diagram:

desired speed → [Controller] → throttle setting → [Engine] → wheel rotation
                    ↑                                               |
                    └─────────────── pulses from wheel ─────────────┘

Project Scheduling

12.1 Why Project Scheduling?

Software projects must be delivered on time and within budget. Project scheduling provides a structured plan to allocate work, sequence activities, assign resources, and track progress.

12.2 Work Breakdown Structure (WBS)

A Work Breakdown Structure decomposes the total project into manageable units:

  • The project is broken into deliverables
  • Each deliverable is broken into activities (or work packages)
  • Activities are the atomic units that can be estimated, assigned, and tracked

12.3 Key Scheduling Concepts

TermDefinition
MilestoneA significant point in the project; typically marks completion of a deliverable or phase
ActivityA defined unit of work with a start, end, duration, and resource requirements
DurationCalendar time required to complete an activity
EffortPerson-hours required (duration × number of people assigned)
DependencyA constraint that one activity must start/finish before another can start/finish

Dependency types:

TypeAbbreviationMeaning
Finish-to-StartFSB cannot start until A finishes (most common)
Start-to-StartSSB cannot start until A starts
Finish-to-FinishFFB cannot finish until A finishes
Start-to-FinishSFB cannot finish until A starts (rare)

12.4 Critical Path Method (CPM)

The Critical Path is the longest sequence of dependent activities through the project. It determines the minimum project duration.

Key terms:

TermDefinition
Earliest Start Time (ES)The earliest time an activity can begin, given all predecessors are complete
Earliest Finish Time (EF)ES + Duration
Latest Finish Time (LF)The latest time an activity can finish without delaying the project
Latest Start Time (LS)LF − Duration
Slack (Float)LS − ES (or LF − EF): how much an activity can be delayed without delaying the project

Activities on the critical path have zero slack — any delay in them delays the entire project.

Algorithm:

  1. Forward pass — compute ES and EF for all activities (left to right)
  2. Backward pass — compute LF and LS for all activities (right to left)
  3. Identify critical path — activities with slack = 0

12.5 Gantt Charts

A Gantt chart is a horizontal bar chart representing the project schedule:

  • Y-axis — activities (one per row)
  • X-axis — calendar time (divided into equal units: days, weeks, months)
  • Bars — the duration of each activity
  • Dependencies — arrows between bars
  • Team member names — written inside the bars to show who is working on what
  • Resource availability — Gantt charts can also show limited resources (e.g., a stove that cannot prepare two dishes simultaneously; a developer who cannot work two tasks simultaneously)

Practical advice for course projects:

  • Use a spreadsheet (Excel, Google Sheets)
  • Each column = equal unit of time; each row = one activity
  • Write team members’ names in the rectangles
  • Ensure no team member is assigned to two activities at the same time
  • Clearly indicate dependencies between tasks

12.6 Representing Resource Availability

In software projects, critical resources include:

  • Staff — developers, QA personnel, site reliability engineers (SREs)
  • Computing resources — servers, build machines
  • Financial resources — budget, cashflow constraints

Gantt charts can be extended to show resource utilisation and identify over-allocation.

12.7 Project Crashing

Project crashing describes schedule compression techniques used when a project must be shortened without changing its scope.

Important: “Project crashing” has nothing to do with software crashes or failures.

Key terms:

TermDefinition
CrashingReducing project time by expending additional resources (e.g., hiring more staff, acquiring more servers)
Crash timeThe amount of time saved by applying crash resources to an activity
Crash costThe cost of the additional resources spent to achieve the crash time
GoalMaximise crash time while minimising crash cost (a multi-objective optimisation)

Crash cost per week = (Crash cost − Normal cost) / (Normal time − Crash time)

Example calculation: For Activity 1 with Normal time 12 weeks, Crash time 7 weeks, Normal cost $3,000, Crash cost $5,000:

  • Total allowable crash time = 12 − 7 = 5 weeks
  • Crash cost per week = ($5,000 − $3,000) / 5 = $400/week

12.8 Time–Cost Tradeoff

There is a fundamental tradeoff when crashing a project:

  • Direct costs decrease as project duration increases (less crashing needed)
  • Indirect costs (overhead, penalties for late delivery) increase as project duration increases

Optimal project time = the point where total project cost (direct + indirect) is minimised. Graphically, this is the bottom of the U-shaped total cost curve.

12.9 Caveat: The Mythical Man-Month

Fred Brooks, in The Mythical Man-Month, observed that adding more human capital does not always accelerate a software project. This is especially true for projects that are already late:

Adding staff to a late software project often makes it even later.

Reasons:

  • Ramp-up costs — new developers need time to learn the codebase
  • Communication costs — each new developer added multiplies the number of communication channels

Implication: crashing may not always be possible for software projects. Prefer crashing specific, well-defined activities rather than the whole project.


Software Cost Estimation

13.1 Why Cost Estimation?

Before a project begins, stakeholders need to know: how much will this cost? How long will it take? Accurate estimation enables informed decisions about whether to proceed, how to staff the project, and how to bid on contracts.

13.2 Challenges of Software Cost Estimation

Software cost estimation is notoriously difficult because:

  • Requirements may be incomplete or volatile
  • No two projects are identical
  • The invisibility of software makes progress hard to measure
  • Individual developer productivity varies widely

13.3 Algorithmic Cost Models

Algorithmic models use a formula with project attributes as inputs to produce effort or cost estimates.

General form:

Effort = A × Size^B × M

Where:

  • Size is a measure of the software (e.g., lines of code, function points)
  • A and B are calibration constants derived from historical data
  • M is a multiplier based on project attributes (complexity, team experience, etc.)

COCOMO II

COCOMO II (Constructive Cost Model II) is one of the most widely used algorithmic models.

  • Developed at the Center for Software Engineering, USC
  • Input: estimated source lines of code (SLOC)
  • Produces: estimated person-months of effort

Key parameters include scale factors (novelty, development flexibility, architecture risk, team cohesion, process maturity) and effort multipliers (reliability requirements, database size, product complexity, execution time constraints, etc.).

Function Points

Function points measure software size based on functionality delivered to the user, independent of programming language:

  • Count inputs, outputs, queries, files, and external interfaces
  • Apply complexity weights
  • Adjust for environmental factors

Function points are language-independent and can be estimated from requirements before any code is written.

13.4 Non-Algorithmic Cost Estimation Techniques

Five alternative approaches:

1. Expert Judgement

The project is estimated by one or more experts based on their experience.

  • Advantages: Captures tacit knowledge; can consider unusual project factors
  • Disadvantages: Biased by personal experience; may be overconfident; not reproducible

2. Estimation by Analogy

The project is compared to a similar completed project in the same domain.

  • Advantages: Accurate if good comparable project data exists
  • Disadvantages: Impossible if no comparable project exists; domain knowledge required

3. Parkinson’s Law

“Work expands to fill the time available” — the project costs whatever resources are available.

  • Advantages: No overspending (you never exceed the available budget)
  • Disadvantages: The system is usually unfinished — you deliver whatever fits in the available time/budget

4. Pricing to Win

The project is priced at whatever the customer is willing to pay.

  • Advantages: You win the contract
  • Disadvantages: The probability that the customer gets the system they actually want is small; costs often do not reflect the work required; leads to scope cuts or overruns

5. Wideband Delphi

A structured expert elicitation technique:

  1. Each expert independently estimates
  2. Estimates are shared (anonymously or openly)
  3. Experts discuss, then re-estimate
  4. Repeat until convergence

Reduces individual bias; takes advantage of collective wisdom.

13.5 Top-Down vs. Bottom-Up Estimation

All of the above techniques can be applied in either direction:

ApproachHow It WorksStrengthsWeaknesses
Top-downStart at system level; estimate overall functionality and delivery through subsystemsUsable with little knowledge; factors in integration, configuration, and documentation costsCan underestimate low-level implementation problems
Bottom-upStart at component level; estimate each piece; aggregate for system totalAccurate for components whose architecture is known; captures low-level detailsMay underestimate system-level activities such as integration; requires architecture to be known

13.6 COCOMO II in More Detail

COCOMO II models three stages of a project’s development:

  1. Application Composition — early prototyping; measured in “object points”
  2. Early Design — rough design; measured in function points
  3. Post-Architecture — detailed design; measured in SLOC

The basic Post-Architecture formula:

PM = A × Size^SF × EM₁ × EM₂ × ... × EMₙ

Where:

  • PM = person-months of effort
  • SF = sum of scale factor exponents (adjusts for project characteristics)
  • EM = effort multipliers (cost drivers)

Scale factors capture economies/diseconomies of scale: larger projects are disproportionately harder to manage.


Notes compiled from CS 446 / ECE 452 / CS 646 course materials, Winter 2021. Instructor: Shane McIntosh (material adapted from Mei Nagappan, Zhen Ming Jiang, Ahmed E. Hassan, and Reid Holmes). Sources include Tailor et al., Garlan & Shaw, GoF Design Patterns, Hughes & Cotterell, Sommerville, and Russell & Taylor.

Back to top