Detailed Table of Contents
The analysis process for identifying objects and object classes is recognized as one of the most difficult areas of object-oriented development. --Ian Sommerville, in the book Software Engineering
Sidebar: Domain Modeling
Domain modeling is modeling the i.e., to model how things actually work in the real world. Domain modeling is useful in understanding the problem domain, which is essential to the success of a project.
Domain modeling can be done using:
When building an OOP system, it makes sense to build OOP models of the problem domain, given OOP aspires to emulate the objects in the real world.
The UML models that capture class structures in the problem domain are called conceptual class diagrams. They are in fact a lighter version of class diagrams, and sometimes also called OO domain models (OODMs). The latter name is somewhat misleading as conceptual class diagrams (CCDs) are actually only one type of domain model that can model an OOP problem domain.
Example The CCD of a snakes and ladders game is given below.
Description: The snakes and ladders game is played by two or more players using a board and a die. The board has 100 squares marked 1 to 100. Each player owns one piece. Players take turns to throw the die and advance their piece by the number of squares they earned from the die throw. The board has a number of snakes. If a player’s piece lands on a square with a snake head, the piece is automatically moved to the square containing the snake’s tail. Similarly, a piece can automatically move from a ladder foot to the ladder top. The player whose piece is the first to reach the 100th square wins.
CCDs do not contain solution-specific classes (i.e., classes that are used in the solution domain but do not exist in the problem domain). For example, a class called DatabaseConnection could appear in a class diagram but not usually in a CCD because DatabaseConnection is something related to a software solution but not an entity in the problem domain.
CCDs represent the class structure of the problem domain and not their behavior, just like class diagrams. To show behavior, use other diagrams such as sequence diagrams.
CCD notation is a subset of the class diagram notation (omits methods and navigability).
Guidance for the item(s) below:
Activity diagrams is the last UML diagram type you'll be learning in this course, and probably the easiest and most intuitive of the lot. You've heard about 'flow charts', right? Well, this is the UML equivalent of that.
Software projects often involve workflows. Workflows define the in which a process or a set of tasks is executed.
Understanding such workflows is important for the success of the software project.
Example A software that automates the work of an insurance company needs to take into account the workflow of processing an insurance claim.
Example The algorithm of a piece of code represents the workflow (i.e., the execution flow) of the code.
UML Activity Diagrams → Introduction → What
UML Activity Diagrams → Basic Notation → Linear Paths
UML Activity Diagrams → Basic Notation → Alternate Paths
UML Activity Diagrams → Basic Notation → Parallel Paths
Follow up notes for the item(s) above:
Here are some examples showing the steps of drawing an activity diagram to match a given workflow.
Guidance for the item(s) below:
A few weeks ago, you learned how to interpret UML diagrams. More recently, you learned how to draw diagrams to match code. There's a third use of models: as an aid for coming up with a design before the code is written.
While this course doesn't ask you to come up with detailed designs before writing code (i.e., our approach leans closer to the agile design rather than the full design upfront approach), this third use of models come in handy at times. Let's learn a bit about that too.
You can use models to analyze and design software before you start coding.
Suppose you are planning to implement a simple minesweeper game that has a text-based UI and a GUI. Given below is a possible OOP design for the game.
Before jumping into coding, you may want to find out things such as,
To answer these questions, you can analyze how the objects of these classes will interact with each other to produce the behavior you want.
As mentioned in [Design → Modeling → Modeling a Solution → Introduction], this is the Minesweeper design you have come up with so far. Our objective is to analyze, evaluate, and refine that design.
Let us start by modeling a sample interaction between the person playing the game and the TextUi object.
newgame and clear x y represent commands typed by the Player on the TextUi.
How does the TextUi object carry out the requests it has received from the player? It would need to interact with other objects of the system. Because the Logic class is the one that controls the game logic, the TextUi needs to collaborate with Logic to fulfill the newgame request. Let us extend the model to capture that interaction.
W = Width of the minefield; H = Height of the minefield
The above diagram assumes that W and H are the only information TextUi requires to display the minefield to the Player. Note that there could be other ways of doing this.
The Logic methods you conceptualized in our modeling so far are:

Now, let us look at what other objects and interactions are needed to support the newGame() operation. It is likely that a new Minefield object is created when the newGame() method is called.

Note that the behavior of the Minefield constructor has been abstracted away. It can be designed at a later stage.
Given below are the interactions between the player and the TextUi for the whole game.
Note that can be used when discovering/defining the architecture-level APIs.
Guidance for the item(s) below:
You've already encountered architecture diagrams in your tP. Pretty soon, you might have to update that diagram to match your new product. Given below are just a brief note about drawing architecture diagrams.
While architecture diagrams have no standard notation, follow these guidelines when drawing them.
Storage stays accurate even if the implementation changes; JsonFileHandler becomes inaccurate if you switch to a database later. Example Consider the two architecture diagrams of the same software given below. Because Diagram 2 uses double-headed arrows everywhere, the important fact that GUI has a genuinely bidirectional dependency with the Logic component is no longer visible.
Guidance for the item(s) below:
These principles build on top of the design fundamentals you learned earlier (i.e., abstraction, coupling, cohesion).
Separation of concerns principle (SoC): To achieve better modularity, separate the code into distinct sections, such that each section addresses a separate concern. -- Proposed by Edsger W. Dijkstra
A concern in this context is a set of information that affects the code of a computer program.
Example Some concerns in a payroll application:
add employee featurepersistence or securityEmployee entityApplying reduces functional overlaps among code sections and also limits the ripple effect when changes are introduced to a specific part of the system.
Example If the code related to persistence is separated from the code related to security, a change to how the data are persisted will not need changes to how the security is implemented.
This principle can be applied at the class level, as well as at higher levels.
Example An application can be divided into layers, each with a well-defined responsibility that does not overlap with the others.
This principle should lead to higher cohesion and lower coupling.
Single responsibility principle (SRP): A class should have one, and only one, reason to change. -- Robert C. Martin
If a class has only one responsibility, it needs to change only when there is a change to that responsibility.
Example Consider a TextUi class that parses user commands as well as interacts with the user. That class needs to change when the formatting of the UI changes as well as when the syntax of the user command changes. Hence, such a class does not follow the SRP.
Gather together the things that change for the same reasons. Separate those things that change for different reasons. -- Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin
Liskov substitution principle (LSP): Derived classes must be substitutable for their base classes. -- proposed by Barbara Liskov
LSP sounds the same as substitutability but it goes beyond substitutability; LSP implies that a subclass should not be more restrictive than the behavior specified by the superclass. As you know, However, if LSP is not followed, substituting a subclass object for a superclass object can break the functionality of the code.
Example Suppose the Payroll class depends on the adjustMySalary(int percent) method of the Staff class. Furthermore, the Staff class states that the adjustMySalary method will work for all positive percent values. Both the Admin and Academic classes override the adjustMySalary method.
Now consider the following:
Admin#adjustMySalary method works for both negative and positive percent values.Academic#adjustMySalary method works for percent values 1..100 only.In the above scenario,
Admin class follows LSP because it fulfills Payroll’s expectation of Staff objects (i.e., it works for all positive values). Substituting Admin objects for Staff objects will not break the Payroll class functionality.Academic class violates LSP because it will not work for percent values over 100 as expected by the Payroll class. Substituting Academic objects for Staff objects can potentially break the Payroll class functionality.Another Example
The Open-Closed Principle aims to make a code entity easy to adapt and reuse without needing to modify the code entity itself.
Open-closed principle (OCP): A module should be open for extension but closed for modification. That is, modules should be written so that they can be extended, without requiring them to be modified. -- proposed by Bertrand Meyer
In object-oriented programming, OCP can be achieved in various ways. This often requires separating the specification (i.e., interface) of a module from its implementation.
Example In the design given below, the behavior of the CommandQueue class can be altered by adding more concrete Command subclasses. For example, by including a Delete class alongside List, Sort, and Reset, the CommandQueue can now perform delete commands without modifying its code at all. That is, its behavior was extended without having to modify its code. Hence, it is open to extensions, but closed to modification.
Example The behavior of a Java generic class can be altered by passing it a different class as a parameter. In the code below, the ArrayList class behaves as a container of Students in one instance and as a container of Admin objects in the other instance, without having to change its code. That is, the behavior of the ArrayList class is extended without modifying its code.
ArrayList students = new ArrayList<Student>();
ArrayList admins = new ArrayList<Admin>();
Law of Demeter (LoD):
Also known as
More concretely, a method m of an object O should invoke only the methods of the following kinds of objects:
O itselfmm (directly or indirectly) Example The following code fragment violates LoD because, while b is a ‘friend’ of foo (because it receives it as a parameter), g is a ‘friend of a friend’ (which should be considered a ‘stranger’), and g.doSomething() is analogous to ‘talking to a stranger’.
void foo(Bar b) {
Goo g = b.getGoo();
g.doSomething();
}
LoD aims to prevent objects from navigating the internal structures of other objects.
Example An analogy for LoD can be drawn from Facebook. If Facebook followed LoD, you would not be allowed to see posts of friends of friends, unless they are your friends as well. If Jake is your friend and Adam is Jake’s friend, you should not be allowed to see Adam’s posts unless Adam is a friend of yours as well.
Guidance for the item(s) below:
If you liked the principles covered above, given below are a few more widely used principles most of which are optional in this course (they were moved to the optional topics in order to reduce the course workload).
The only examinable thing is the term SOLID principles.
The five OOP principles given below are known as SOLID Principles (an acronym made up of the first letter of each principle):
Single Responsibility Principle (SRP)
Open-Closed Principle (OCP)
Liskov Substitution Principle (LSP)
Interface Segregation Principle (ISP)
Dependency Inversion Principle (DIP)
Guidance for the item(s) below:
Remember these three topics that we covered early in the course?
The simplest way to build software is to start coding and keep fixing what breaks, with no explicit stages at all. That approach, sometimes called code-and-fix, has no overhead and works well enough for a small program written by one person over a short period. It stops working as the software and the team grow. There is no way to tell how far along the work is, and no way to divide it among several people without them colliding. There is also no record of the decisions already made, so changes become harder and more expensive.
Software development goes through different stages such as requirements, analysis, design, implementation, and testing. These stages are collectively known as the software development lifecycle (SDLC). There are several approaches, known as software development lifecycle models (also called software process models), that describe different ways to go through the SDLC. Each process model prescribes a 'roadmap' for the development effort: the aims of the development stages, the outcome of each stage, and how the stages relate to one another.
Reaching users is not the end of the lifecycle. Deployment, operation, and maintenance are commonly counted as lifecycle activities too, and process models differ in how they partition and name them. Once the software is released, it has to be operated, kept working as its environment changes, and improved. What happens during that time feeds back into development: defects reported by users, the way the software is actually used, and requests for things it cannot yet do all become inputs to later work. Most software spends far longer in this state than it spent being built for the first time.
The sequential model, also called the waterfall model, views software development as a linear process, with the project progressing through the development stages in order. The name waterfall stems from how the model is drawn to look like a waterfall (see below).
When one stage of the process is completed, it produces some to be used in the next stage. For example, the requirements stage produces a comprehensive list of requirements to be used in the design stage.
A strict sequential model project moves only in the forward direction i.e., each stage is completed before starting the next. For example, once the requirements stage is over, there is no provision for revising the requirements later. In practice the model is often relaxed to let a stage send work back to the one before it, although doing so means redoing work that was already treated as finished.
This model can work well for a project that solves a well-understood problem, in which case the requirements can remain stable and the effort can be estimated accurately. Furthermore, as each stage has a well-defined outcome, progress is easy to track: it is visible from which stage the project is in. Progress within a long stage, which is where an overrun usually builds up, stays much harder to see.
However, real-world projects often tackle problems that are not well-understood at the beginning, which makes those projects unsuitable for this model. For example, target users of a software product may not be able to state their requirements accurately at the start of the project if they have not used a similar product before.
A second weakness is that feedback arrives late. Each stage's output is checked mainly by the stage that follows it, so integration and contact with real users come near the end. A mistake made while gathering requirements or designing therefore tends to surface at the point where going back to completed work costs the most.
The iterative model advocates producing the software by going through several iterations. Each iteration could go through all the stages of the SDLC, from requirements gathering to deployment.
Each iteration produces a new version of the product, building upon the previous version. Feedback from each iteration is factored into the subsequent iterations. For example, if an implementation task took longer than expected, the effort estimate for similar tasks in future iterations can be adjusted. Similarly, if a feature introduced in the current iteration was not well-received by target users, it can be removed or tweaked in the next iteration.
The two models divide a project along different lines.
The iterative model can use a breadth-first or depth-first approach.
Iterating and incrementing are two different things, and most projects do both. To iterate is to rework something that already exists, in the light of feedback; to increment is to add to it. That is why the two are usually named together, as an iterative and incremental approach.
What an iteration delivers is called an increment: a usable improvement or addition to the product, not merely a new version of the code.
Ideally, an iteration should produce not only progress but also new knowledge. Before starting one, decide what would show it succeeded -- a condition the result must satisfy, a test that must pass, or a demonstration to a target user -- and what decision the answer will drive.
Example An iteration can add a new feature while helping the team estimate the effort for similar future features or assess the advantages and disadvantages of the approach used to implement it.
Example Taking a Minesweeper game as an example:
A project can be done as a mixture of breadth-first and depth-first iterations i.e., an iteration can contain some breadth-first work as well as some depth-first work, or some iterations can be breadth-first while others are depth-first.
Whichever shape the iterations take, an early one is a chance to find out you were wrong while changing course is still cheap. That makes the assumptions whose failure would cost the most -- an unproven technology, an unfamiliar user need, a performance target nobody has hit yet -- worth putting into an early iteration rather than a late one.
Guidance for the item(s) below:
Let's continue that thread to learn about some SDLC process models that are commonly used in the industry.
The agile approaches grew out of lightweight methods that were already in use. In 2001, a group of prominent software engineering practitioners -- among them the authors of several such methods -- met to articulate the values their approaches had in common. They were reacting against the documentation-driven, heavyweight processes used in most large projects at the time. The result was the agile manifesto.
We are uncovering better ways of developing software by doing it and helping others do it.
Through this work we have come to value:
- Individuals and interactions over processes and tools
- Working software over comprehensive documentation
- Customer collaboration over contract negotiation
- Responding to change over following a plan
That is, while there is value in the items on the right, we value the items on the left more.
-- Extract from the Agile Manifesto
The methods represented at that meeting, and later approaches built on the same values, are collectively called agile processes. Some of the key features of agile approaches are:
Several agile processes are in use today. Extreme Programming (XP) and Scrum are two well-known ones.
Agile approaches depend on conditions that are not always present: a customer available to give feedback continuously, and the ability to ship a change cheaply. An agile approach is not a good fit when those conditions are absent—for example, in a fixed-price contract with a signed-off scope or when software must be certified before release.
Scrum is a lightweight agile framework rather than a complete process. It fixes a small set of roles, events, and artifacts, and leaves the team to fill in the rest with practices of its own choosing. The description below follows the Scrum Guide.
A Scrum team has three accountabilities:
A Scrum project is divided into short iterations called Sprints. Sprints are time-boxed (i.e., restricted to a fixed duration) at one month or less, and every Sprint in a project has the same length. One to four weeks is the common choice.
A Sprint contains all the work done in it, together with all its events. It opens with Sprint Planning, where the team selects the work and agrees on a Sprint Goal, and the Developers coordinate daily as the work proceeds. It ends with two distinct meetings: a Sprint Review, where the team and stakeholders inspect the Increment and decide what the product needs next, and a Sprint Retrospective, where the team inspects how it worked and chooses improvements. The next Sprint begins immediately after.
During each Sprint, the team creates a potentially deliverable Increment (for example, working and tested software). The work comes from the Product Backlog, a prioritized set of high-level requirements for the product as a whole. The items selected for the current Sprint form the Sprint Backlog.
Within a Sprint the Sprint Goal stays fixed, but the plan for reaching it does not. The team updates the Sprint Backlog as it learns more, and can renegotiate the scope with the Product Owner as long as the Sprint Goal survives. The Sprint must end on time; work that is not completed returns to the Product Backlog.
Scrum enables self-organizing teams, which rely on frequent and direct communication among all team members and disciplines rather than on documents handed from one to the next.
Scrum assumes that customers will change their minds about what they want (often called requirements churn) and that unforeseen problems cannot be planned for in advance. It therefore takes an empirical approach: instead of trying to define the problem fully up front, it maximizes the team's ability to deliver quickly and respond to requirements as they emerge.
The Daily Scrum is a short daily meeting in which the Developers synchronize their plans, surface whatever is blocking them, and decide what needs to be taken up separately. It is not a problem-solving meeting.
Example A common way to run it is for each member to say what they did since the previous Daily Scrum, what they plan to do next, and what is in their way.
Intro to Scrum in Under 10 Minutes
The following description was adapted from the XP home page, emphasis added:
Extreme Programming (XP) stresses customer satisfaction. Instead of delivering everything you could possibly want on some date far in the future, this process delivers the software you need as you need it.
XP aims to empower developers to confidently respond to changing customer requirements, even late in the lifecycle.
XP emphasizes teamwork. Managers, customers, and developers are all equal partners in a collaborative team. The team self-organizes around the problem to solve it as efficiently as possible.
XP aims to improve a software project in five essential ways: communication, simplicity, feedback, respect, and courage. Extreme Programmers constantly communicate with their customers and fellow programmers. They keep their design simple and clean. They get feedback by testing their software starting on day one. With this foundation, Extreme Programmers are able to courageously respond to changing requirements and technology.
What makes XP 'extreme' is not the practices it uses but how often it uses them. Each one was already considered good; XP pushes each to the point where it happens continuously rather than in a scheduled phase:
No approach is best for every project; the choice depends on the project. These questions usually decide whether a project leans sequential or iterative:
These questions guide a choice; they do not compute one. Two reasonable teams can weigh them differently and both be right, and a project can combine approaches rather than adopt one wholesale.
Example Two projects, two defensible answers:
Follow up notes for the item(s) above:
AI's impact on » SDLC process models
AI changes the cost of the work, not the need for a plan.
Guidance for the item(s) below:
As you will be updating documentation of your project soon, here are some guidelines to help you with that.
Developer-to-developer documentation can be in one of two forms:
Example API documentation: String API
Example Tutorial-style documentation: Java Internationalization Tutorial
Example API documentation: string API
Example Tutorial-style documentation: How to use Regular Expressions in Python
Another view proposed by Daniele Procida in this article is as follows:
There is a secret that needs to be understood in order to write good software documentation: there isn’t one thing called documentation, there are four. They are: tutorials, how-to guides, explanation and technical reference. They represent four different purposes or functions, and require four different approaches to their creation. Understanding the implications of this will help improve most software documentation - often immensely. ...
TUTORIALS
A tutorial:
- is learning-oriented
- allows the newcomer to get started
- is a lesson
Analogy: teaching a small child how to cook
HOW-TO GUIDES
A how-to guide:
- is goal-oriented
- shows how to solve a specific problem
- is a series of steps
Analogy: a recipe in a cookery book
EXPLANATION
An explanation:
- is understanding-oriented
- explains
- provides background and context
Analogy: an article on culinary social history
REFERENCE
A reference guide:
- is information-oriented
- describes the machinery
- is accurate and complete
Analogy: a reference encyclopedia article
Software documentation (applies to both user-facing and developer-facing) is best kept in a text format for ease of version tracking. A writer-friendly source format is also desirable because non-programmers (e.g., technical writers) may need to author/edit such documents. As a result, software documentation often uses text formats such as Markdown, together with static site generators such as Jekyll and diagramming tools such as PlantUML and Mermaid.
Technical documents exist to help others understand technical details. Therefore, it is not enough for the documentation to be accurate and comprehensive; it should also be comprehensible.
Here are some tips on writing effective documentation.
When writing project documents, a top-down breadth-first explanation is easier to understand than a bottom-up one.
A top-down document is structured like an upside-down tree, with its root at the top, so readers can follow the path that interests them to the component they want to study in depth. They need not read the whole document or understand the whole system.
Example To explain a system called SystemFoo with two sub-systems, FrontEnd and BackEnd, start by describing the system at the highest level of abstraction, and progressively drill down to lower-level details. An outline for such a description is given below.
[First, explain what the system is, in a black-box fashion (no internal details, only the external view).]
SystemFoois a ....
[Next, explain the high-level architecture of SystemFoo, referring to its major components only.]
SystemFooconsists of two major components:FrontEndandBackEnd.
The job ofFrontEndis to ...; the job ofBackEndis to ...
And this is howFrontEndandBackEndwork together ...
[Now you can drill down to FrontEnd's details.]
FrontEndconsists of three major components:A,B,C
A's job is to ...B's job is to...C's job is to...
And this is how the three components work together ...
[At this point, drill down further into the internal workings of each component. A reader who is not interested in the nitty-gritty details can skip ahead to the section on BackEnd.]
In-depth description of
A
In-depth description ofB
...
[At this point, drill down to the details of the BackEnd.]
...
Aim for 'just enough' developer documentation.
Anything that is already clear in the code need not be described in words. Instead, focus on providing higher-level information that is not readily visible in the code or comments.
Refrain from duplicating chunks of text. When describing several similar algorithms, designs, or APIs, do not simply duplicate large chunks of text. Instead, describe the similarities in one place and emphasize only the differences in other places. Readers can find it annoying to see pages and pages of similar text without any indication of how they differ.