Software Design

Chapter 6 - Comprehensive Guide to Software Engineering Design Principles

Introduction to Software Design

Mitch Kapor, creator of Lotus 1-2-3, presented a "software design manifesto" stating that good software design should exhibit:

  • Firmness: A program should not have any bugs that inhibit its function.
  • Commodity: A program should be suitable for the purposes for which it was intended.
  • Delight: The experience of using the program should be pleasurable.

Software design encompasses the set of principles, concepts, and practices that lead to the development of a high-quality system or product. These practices change continuously as new methods, better analysis, and broader understanding evolve.

What is Software Design?

Software design is where customer requirements, business needs, and technical considerations all come together in the formulation of a product or system. The design model provides detail about software data structures, architecture, interfaces, and components.

Purpose of Design

The design model can be assessed for quality and improved before code is generated and tests are conducted. Key questions to consider:

  • Does the design contain errors, inconsistencies, or omissions?
  • Are there better design alternatives?
  • Can the design be implemented within the constraints, schedule, and cost that have been established?

From Analysis Model to Design Model

Data/Class Design

Transforms analysis classes into design classes/implementation classes along with the data structures required to implement the software.

Architectural Design

Defines the relationship between major structural elements of the software; architectural styles and design patterns help achieve the requirements.

Interface Design

Defines how software elements, hardware elements, and end-users communicate.

Component-Level Design

Transforms structural elements of the software architecture into a procedural description of software components.

Design and Quality

Quality Characteristics

A high-quality design should:

  • Implement all explicit requirements contained in the analysis model and accommodate all implicit requirements desired by the customer
  • Be a readable, understandable guide for code generation and testing
  • Provide a complete picture of the software, addressing data, functional, and behavioral domains

Quality Guidelines

  1. Architectural Excellence: Design should exhibit an architecture created using recognizable styles/patterns, composed of components with good design characteristics, implementable in an evolutionary fashion
  2. Modularity: Software should be logically partitioned into elements or subsystems
  3. Distinct Representations: Design should contain distinct representations of data, architecture, interfaces, and components
  4. Appropriate Data Structures: Design should lead to data structures appropriate for classes to be implemented and drawn from recognizable data patterns
  5. Independent Functional Characteristics: Design should lead to components that exhibit independent functional characteristics
  6. Reduced Complexity: Design should lead to interfaces that reduce complexity of connections between components and with the external environment
  7. Repeatable Method: Design should be derived using a repeatable method driven by information from requirements analysis
  8. Effective Communication: Design should be represented using notation that effectively communicates its meaning

Design Principles

Core Design Principles

1. Avoid Tunnel Vision

The design should not suffer from narrow focus. Consider all aspects and implications of design decisions.

2. Traceability

The design should be traceable to the analysis model, ensuring all requirements are addressed.

3. Reusability

The design should not reinvent the wheel. Software components should be designed for effective reuse to increase productivity.

4. Minimize Intellectual Distance

The design should minimize the gap between the software and the real-world problem it solves.

5. Uniformity and Integration

The design should exhibit uniformity and integration across all components.

6. Accommodate Change

The design should be structured to accommodate change gracefully.

7. Graceful Degradation

The design should be structured to degrade gently when aberrant data events or operating conditions are encountered.

8. Separation of Concerns

Design is not coding, coding is not design. Each phase has its distinct purpose.

9. Quality Assessment

The design should be assessed for quality as it is being created, not after the fact.

10. Address Semantic Before Syntactic

The design should be reviewed to minimize conceptual errors (ambiguousness and inconsistency) before dealing with syntactical errors.

Prototyping

Prototyping should be used when requirements are not completely defined at the beginning. The user interacts with the developer to expand and refine requirements as development proceeds. A quick 'mock-up' of the system can be developed to give users a feel of what the system will look like and demonstrate included functions. Prototyping also helps reduce risks of designing software not in accordance with customer requirements.

Important: Testing should be involved from the initial stages of design.

Fundamental Concepts

1. Abstraction

Abstraction means different levels of description of the design. It's a fundamental concept allowing designers to work at various levels of detail.

2. Architecture

The overall structure of the software and the ways in which that structure provides conceptual integrity for a system.

3. Patterns

Patterns convey the essence of proven design solutions. They represent what has already been proven to be the best design solution for a particular problem.

4. Refinement

Elaboration of detail for all abstractions. This involves breaking down high-level concepts into more detailed implementations.

5. Aspect-Oriented Programming (AOP)

An aspect of a program is a feature linked to many other parts of the program but not related to its primary functions. An aspect crosscuts the program's core concerns.

Example: Logging code can crosscut many modules, yet the aspect of logging should be separate from the functional concerns of the module it cross-cuts. Isolating aspects like logging and persistence from business logic is at the core of AOP.

Best Practices

Separation of Concerns

Any complex problem can be more easily handled if it is subdivided into pieces.

Modularity

Group highly coupled data and functions into modules (packages or libraries).

Functional Independence

Single-minded function and low coupling between components.

Information Hiding

Controlled interfaces. Don't leave anything open to the external user unnecessarily.

Refactoring

A reorganization technique that simplifies the design without changing functionality.

SOLID Principles

Single responsibility, Open/Closed, Liskov substitution, Interface segregation, Dependency inversion.

Software Architecture

"The overall structure of the software and the ways in which that structure provides conceptual integrity for a system."

Software architecture refers to the structure of the system, composed of various program/system components, the attributes (properties) of those components, and the relationships amongst them. The software architecture enables software engineers to analyze the software design efficiently.

Key Architectural Aspects

Structural Properties

This aspect defines the components of a system (e.g., modules, objects, filters) and the manner in which those components are packaged and interact with one another. For example, objects are packaged to encapsulate both data and the processing that manipulates the data and interact via the invocation of methods.

Extra-Functional (Non-Functional) Properties

The architectural design description should address how the architecture achieves requirements for:

  • Performance
  • Capacity
  • Reliability
  • Security
  • Adaptability
  • Other system characteristics

Families of Related Systems

The architectural design should draw upon repeatable patterns commonly encountered in the design of families of similar systems. The design should have the ability to reuse architectural building blocks.

Modularity

Software should be divided into separately named and addressable components, called modules, that are integrated to satisfy problem requirements.

In software engineering, modularity refers to the extent to which a software/web application is divided into small modules. Modularity is the most important attribute of software that allows a program to be intellectually manageable.

Module Structure

Main Program → Modules → Functions → Module Data

Global Data ↔ Main Program

Information Hiding

Why Information Hiding?

  • Reduces the likelihood of "side effects"
  • Limits the global impact of local design decisions
  • Emphasizes communication through controlled interfaces
  • Discourages the use of global data
  • Leads to encapsulation—an attribute of high quality design
  • Results in higher quality software

Modules hide "secrets" including:

  • Algorithms
  • Data structures
  • Details of external interfaces
  • Resource allocation policies

Stepwise Refinement

Stepwise refinement is a top-down design strategy used for decomposing a system from a high level of abstraction into a more detailed level. A hierarchy is developed by decomposing a macroscopic statement of function step by step until programming language statements are reached.

Process: You begin with a statement of function defined at a high level of abstraction. The statement describes function or information conceptually but provides no information about internal workings. Then, you provide more and more details as each successive refinement occurs.

Note: Abstraction and refinement are complementary concepts. Refinement helps manage complexity by thinking about low-level details as design progresses.

Functional Independence

Functional independence is achieved by developing modules with "single-minded" function with minimized interaction with other modules. It's a key to good design, and design is the key to software quality.

Component independence is assessed using two criteria:

Cohesion (Intra-Module)

An indication of the relative functional strength of a module. A cohesive module performs a single task, requiring little interaction with other components. A cohesive module should ideally do just one thing.

Goal: High Cohesion

Coupling (Inter-Module)

An indication of the relative interdependence among modules. Coupling depends on interface complexity between modules, entry/reference points, and data passed across interfaces.

Goal: Low Coupling

Design Goal: Achieve high cohesion (single-mindedness) with the lowest possible coupling (little interaction with other modules). Simple connectivity among modules results in software that is easier to understand and less prone to a "ripple effect" when errors occur at one location and propagate throughout a system.

Architectural Abstraction Levels

Level Description Focus
Architecture in the Small Concerned with the architecture of individual programs How individual programs are decomposed into components
Architecture in the Large Concerned with complex enterprise systems Systems including other systems, programs, and components distributed over different computers

Non-Functional Requirements Dictate Architectural Style

Requirement Architectural Approach
Performance Localize critical operations and minimize communications. Use large rather than fine-grain components.
Security Use a layered architecture with critical assets in the inner layers.
Safety Localize safety-critical features in a small number of sub-systems.
Availability Include redundant components and mechanisms for fault tolerance.
Maintainability Use fine-grain, replaceable components.

Architectural Patterns

Patterns are a means of representing, sharing, and reusing knowledge. An architectural pattern is a stylized description of good design practice that has been tried and tested in different environments.

Patterns should include information about when they are and when they are not useful. Patterns may be represented using tabular and graphical descriptions.

Model-View-Controller (MVC) Pattern

Description

Separates presentation and interaction from the system data. The system is structured into three logical components that interact with each other:

  • Model: Manages the system data and associated operations on that data
  • View: Defines and manages how the data is presented to the user
  • Controller: Manages user interaction (e.g., key presses, mouse clicks) and passes these interactions to the View and the Model

When Used

Used when there are multiple ways to view and interact with data. Also used when future requirements for interaction and presentation of data are unknown.

Advantages

  • Allows the data to change independently of its representation and vice versa
  • Supports presentation of the same data in different ways
  • Changes made in one representation shown in all of them

Disadvantages

Can involve additional code and code complexity when the data model and interactions are simple.

MVC Architecture

User → sees → View

User → uses → Controller

Controller → manipulates → Model

Model → updates → View

Database ↔ Model

Key Principle: MVC is an example of Separation of Concerns, which is fundamental to good software design.

Layered Architecture

Description

Used to model the interfacing of sub-systems. Organizes the system into a set of layers (or abstract machines), each of which provides a set of services.

Advantages

  • Supports incremental development of sub-systems in different layers
  • When a layer interface changes, only the adjacent layer is affected
  • Provides clear separation of concerns

Typical Layers (Top to Bottom)

  1. User Interface - Presentation layer (HTML5, JavaScript, CSS)
  2. User Interface Management / Authentication and Authorization
  3. Core Business Logic / Application Functionality / System Utilities - Application layer (Java, .NET, C#, Python, C++)
  4. System Support (OS, Database, etc.) - Data layer (MySQL, Oracle, PostgreSQL, SQL Server, MongoDB)

Client-Server Architecture

Two-Tier Architecture

Thin Client

Client has only the presentation layer (user interface programs), while Application (business logic) and Data layer reside in the server. Most data processing takes place in the server. Virtual Desktops are hosted in data centers.

Fat Client

Client has both the presentation layer and Application layer, while Data layer resides in the server. Most resources are locally installed.

Data-Centered/Repository Architecture

Characteristics

The data resides at a central place in the architecture and is accessed frequently by other components that update, add, delete, or modify the data within the store.

Focus

How to place data so everyone can access it frequently.

Example

An organization's data is placed on the server and all employees access it (cloud data).

Usage

  • This type of design supports many web service architectures (Microsoft .NET, Java 2 Enterprise Edition)
  • Used by Siebel and Oracle
  • When a central issue is storage, representation, management, and retrieval of large amounts of related persistent data

Key Benefits

  • Goal of integrating data
  • Clients are relatively independent of each other (can be added, removed, or changed)
  • Data store is independent of clients

Note: When large amounts of data are to be shared, the repository model is most commonly used as it is an efficient data sharing mechanism. Sub-systems must exchange data through either a shared central database or by maintaining separate databases and passing data explicitly.

Data Flow/Pipe & Filter Architecture

Focus

How data is flowing in the system. Applied when input data is converted through a series of manipulative components into output data.

Components

A pipe-and-filter pattern has a set of components, called filters, connected by pipes that transmit data from one component to the next.

Filter Characteristics

  • Each filter works independently of components upstream and downstream
  • Designed to expect data input of a certain form
  • Produces data output of a specified form
  • Does not require knowledge of neighboring filters' workings

Batch Sequential

If the data flow degenerates into a single line of transforms, it is termed batch sequential. This structure accepts a batch of data and applies a series of sequential components (filters) to transform it.

Pipe and Filter Architecture

Input → Filter → Pipe → Filter → Pipe → Filter → Output

(Multiple parallel paths possible with filters connected via pipes)

Interface Design

A user interface should be: Easy to learn, Easy to use, Easy to understand, Responsive in short time, and Attractive.

What is Interface Design?

User Interface is the front-end application with which users interact to use software functionalities. Users can manipulate and control software and hardware through the user interface. We find user interfaces in all digital technologies: computers, mobiles, cars, music players, etc.

Categories of UI

  • Command Line Interface (CLI)
  • Graphical User Interface (GUI)

Design Issues

Response Time

System should respond promptly to user actions

Help Facilities

Provide adequate help and documentation

Error Handling

Gracefully handle and communicate errors

Menu and Command Labeling

Use clear, consistent labeling

Application Accessibility

Ensure all users can access features

Internationalization

Support multiple languages and regions

Golden Rules of Interface Design

1. Place the User in Control

  • Define interaction so users are not forced into performing unnecessary actions
  • Provide friendly user interaction
  • Hide technical internals from casual users

2. Reduce the User's Memory Load

  • Reduce demands on short-term memory
  • Establish meaningful defaults
  • Define shortcuts for experienced users

3. Make the Interface Consistent

  • Maintain consistency across a family of applications
  • If past interactive models have created user expectations, do not make changes unless there is a compelling reason

Typical Design Errors to Avoid

  • Lack of consistency
  • Too much memorization required
  • No guidance or help
  • No context sensitivity
  • Poor response time
  • Unclear or unfriendly messages

Interface Analysis

A key principle of all software engineering process models is: understand the problem before you attempt to design a solution.

Interface analysis means understanding:

  1. The people (end-users) who will interact with the system through the interface
  2. The tasks that end-users must perform to do their work
  3. The content that is presented as part of the interface
  4. The environment in which these tasks will be conducted

WebApp Interface Design

A WebApp interface should answer three primary questions for the end user:

Where am I?

The interface should:

  • Provide an indication of the WebApp accessed
  • Inform the user of location in the content hierarchy

What can I do now?

The interface should help users understand:

  • What functions are available? (e.g., sub menu)
  • What links are live? (enabled or disabled)
  • What content is relevant?

Where have I been, where am I going?

The interface must facilitate navigation:

  • Provide a "map" showing where the user has been
  • Show paths to move elsewhere within the WebApp

Characteristics of Effective WebApp Interfaces

Bruce Tognozzi suggests:

  • Effective interfaces are visually apparent and forgiving, instilling in users a sense of control
  • Effective interfaces do not concern users with the inner workings of the system. Work is carefully and continuously saved, with full option for users to undo any activity at any time
  • Effective applications and services perform a maximum of work while requiring a minimum of information from users

Summary and Key Takeaways

Essential Design Principles

  • Good design requires understanding the problem before creating solutions
  • Design should be traceable to requirements and assessed for quality continuously
  • Modularity, abstraction, and information hiding are fundamental concepts
  • Aim for high cohesion and low coupling in component design
  • Use proven architectural patterns and styles appropriate to your requirements
  • Interface design should prioritize user control, reduce memory load, and maintain consistency

Design Process Step-by-Step

  1. Examine the information domain model and design appropriate data structures
  2. Select an architectural style and design patterns that are appropriate
  3. Partition the analysis model into design subsystems
  4. Design the subsystem interfaces
  5. Allocate analysis classes or functions to each subsystem
  6. Create a set of design classes or components
  7. Translate each analysis class into a design class
  8. Check each design class against design criteria
  9. Define methods associated with each design class
  10. Evaluate and select design patterns
  11. Design required interfaces with external systems
  12. Design the user interface
  13. Conduct component-level design
  14. Specify algorithms and refine component interfaces

Remember

Software design is an iterative process. The design should be continuously reviewed and refined to ensure it meets quality standards and fulfills all requirements. Testing should be involved from the initial stages, and prototyping should be used when requirements are not completely defined.