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.
Defining the architecture-level APIs for a small Tic-Tac-Toe game:
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 if the implementation changes; JsonFileHandler becomes a lie the day you switch to a database. 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 — it looks like every other connection.
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 The n-tier architecture utilizes this principle. Each layer in the architecture has a well-defined functionality that has no functional overlap with the other layers.
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, Java has language support for substitutability. 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. A sequential project is divided by activity: a stage is 'requirements' or 'testing', and it ends when that activity is finished for the whole product. An iterative project is divided into bounded cycles instead: an iteration runs through several activities and ends in a result the team can learn from. What each iteration is for is then a choice -- most often a slice of functionality, 'the part that does X', but it can equally be a component, a risky assumption, or a level of completeness across the whole product.
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.
An iteration is only worth its overhead if it ends in evidence. 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. Without that, an iteration produces a new version and no new knowledge.
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. Ordering iterations by risk in this way is the central idea of the spiral model.
As AI coding advances, producing a candidate implementation is becoming much cheaper than it used to be; deciding what to build and confirming that the result is correct have not. Within an iteration, that shifts the effort away from writing code and toward specifying and verifying. It does not reduce the value of being precise about what is wanted: a vague requirement that once produced a question from a teammate now produces a confident implementation of the wrong thing, quickly.
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:
Many 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. Where those are missing -- a fixed-price contract with a signed-off scope, or software that must be certified before release -- an agile approach costs more than it returns.
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:
That is the same argument iterative models make about the whole lifecycle, applied to individual development practices instead: shorten the gap between doing something and finding out whether it worked.
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:
The three approaches differ in how a project is divided, and in when it finds out whether it is on track.
The example models are particular ways of doing this: XP pushes individual development practices to happen continuously, Scrum fixes a small set of accountabilities, events and artifacts around short Sprints, and the Unified Process runs four phases, each as one or more iterations.
The exercises below cover several of these topics together.
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, formats such as Markdown, AsciiDoc, and PlantUML are often used for software documentation.
Here are some tips on writing effective documentation.
The main advantage of the top-down approach is that the document is structured like an upside-down tree (root at the top) and the reader can follow the path they are interested in until they reach the component they want to learn about in depth, without having to read the entire 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 ... while 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, further drill down to the internal workings of each component. A reader who is not interested in knowing 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/APIs, etc., 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.