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
- Architectural Excellence: Design should exhibit an architecture created using recognizable styles/patterns, composed of components with good design characteristics, implementable in an evolutionary fashion
- Modularity: Software should be logically partitioned into elements or subsystems
- Distinct Representations: Design should contain distinct representations of data, architecture, interfaces, and components
- Appropriate Data Structures: Design should lead to data structures appropriate for classes to be implemented and drawn from recognizable data patterns
- Independent Functional Characteristics: Design should lead to components that exhibit independent functional characteristics
- Reduced Complexity: Design should lead to interfaces that reduce complexity of connections between components and with the external environment
- Repeatable Method: Design should be derived using a repeatable method driven by information from requirements analysis
- 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)
- User Interface - Presentation layer (HTML5, JavaScript, CSS)
- User Interface Management / Authentication and Authorization
- Core Business Logic / Application Functionality / System Utilities - Application layer (Java, .NET, C#, Python, C++)
- 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:
- The people (end-users) who will interact with the system through the interface
- The tasks that end-users must perform to do their work
- The content that is presented as part of the interface
- 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
- Examine the information domain model and design appropriate data structures
- Select an architectural style and design patterns that are appropriate
- Partition the analysis model into design subsystems
- Design the subsystem interfaces
- Allocate analysis classes or functions to each subsystem
- Create a set of design classes or components
- Translate each analysis class into a design class
- Check each design class against design criteria
- Define methods associated with each design class
- Evaluate and select design patterns
- Design required interfaces with external systems
- Design the user interface
- Conduct component-level design
- 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.