Week 12 [Nov 5]
Todo
Admin info to read:
Tweak as per peer-testing results, draft Project Portfolio Page, practice product demo.
Project Management:
- Freeze features around this time. Ensure the current product have all the features you intend to release at v1.4. Adding major changes after this point is risky. The remaining time is better spent fixing problems discovered late or on fine-tuning the product.
Product:
- Consider increasing code coverage by adding more tests if it is lower than the level you would like it to be. Take note of
our expectation on test code . - After you have sufficient code coverage, fix remaining code quality problems and bring up the quality to your target level.
- There is no requirement for a minimum coverage level. Note that in a production environment you are often required to have at least 90% of the code covered by tests. In this project, it can be less. The less coverage you have, the higher the risk of regression bugs, which will cost marks if not fixed before the final submission.
- You must write some tests so that we can evaluate your ability to write tests.
- How much of each type of testing should you do? We expect you to decide. You learned different types of testing and what they try to achieve. Based on that, you should decide how much of each type is required. Similarly, you can decide to what extent you want to automate tests, depending on the benefits and the effort required.
- Applying
TDD is optional. If you plan to test something, it is better to apply TDD because TDD ensures that you write functional code in a testable way. If you do it the normal way, you often find that it is hard to test the functional code because the code has low testability.
Relevant: [
-
Ensure your code has at least some evidence of these (see here for more info)
- logging
- exceptions
- assertions
- defensive coding
-
Ensure there are no coding standard violations e.g. all boolean variables/methods sounds like booleans. Checkstyle can prevent only some coding standard violations; others need to be checked manually.
-
Ensure SLAP is applied at a reasonable level. Long methods or deeply-nested code are symptoms of low-SLAP may be counted against your code quality.
-
Reduce code duplications i.e. if there multiple blocks of code that vary only in minor ways, try to extract out similarities into one place, especially in test code.
-
In addition, try to apply as many of the
code quality guidelines covered in the module as much as you can.
Can explain the importance of code quality
Always code as if the person who ends up maintaining your code will be a violent psychopath who knows where you live. -- Martin Golding
Can explain the importance of readability
Programs should be written and polished until they acquire publication quality. --Niklaus Wirth
Among various dimensions of code quality, such as run-time efficiency, security, and robustness, one of the most important is understandability. This is because in any non-trivial software project, code needs to be read, understood, and modified by other developers later on. Even if we do not intend to pass the code to someone else, code quality is still important because we all become 'strangers' to our own code someday.
The two code samples given below achieve the same functionality, but one is easier to read.
Bad
|
|
Good
|
Bad
|
|
Good
|
Can improve code quality using technique: avoid long methods
Be wary when a method is longer than the computer screen, and take corrective action when it goes beyond 30 LOC (lines of code). The bigger the haystack, the harder it is to find a needle.
Can improve code quality using technique: avoid deep nesting
If you need more than 3 levels of indentation, you're screwed anyway, and should fix your program. --Linux 1.3.53 CodingStyle
In particular, avoid arrowhead style code.
Example:
Can improve code quality using technique: avoid complicated expressions
Avoid complicated expressions, especially those having many negations and nested parentheses. If you must evaluate complicated expressions, have it done in steps (i.e. calculate some intermediate values first and use them to calculate the final value).
Example:
Bad
return ((length < MAX_LENGTH) || (previousSize != length)) && (typeCode == URGENT);
Good
boolean isWithinSizeLimit = length < MAX_LENGTH;
boolean isSameSize = previousSize != length;
boolean isValidCode = isWithinSizeLimit || isSameSize;
boolean isUrgent = typeCode == URGENT;
return isValidCode && isUrgent;
Example:
Bad
return ((length < MAX_LENGTH) or (previous_size != length)) and (type_code == URGENT)
Good
is_within_size_limit = length < MAX_LENGTH
is_same_size = previous_size != length
is_valid_code = is_within_size_limit or is_same_size
is_urgent = type_code == URGENT
return is_valid_code and is_urgent
The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague. -- Edsger Dijkstra
Can improve code quality using technique: avoid magic numbers
When the code has a number that does not explain the meaning of the number, we call that a magic number (as in “the number appears as if by magic”). Using a
Example:
Bad
|
|
Good
|
Note: Python does not have a way to make a variable a constant. However, you can use a normal variable with an ALL_CAPS
name to simulate a constant.
Bad
|
|
Good
|
Similarly, we can have ‘magic’ values of other data types.
Bad
"Error 1432" // A magic string!
Can improve code quality using technique: make the code obvious
Make the code as explicit as possible, even if the language syntax allows them to be implicit. Here are some examples:
- [
Java
] Use explicit type conversion instead of implicit type conversion. - [
Java
,Python
] Use parentheses/braces to show grouping even when they can be skipped. - [
Java
,Python
] Useenumerations when a certain variable can take only a small number of finite values. For example, instead of declaring the variable 'state' as an integer and using values 0,1,2 to denote the states 'starting', 'enabled', and 'disabled' respectively, declare 'state' as typeSystemState
and define an enumerationSystemState
that has values'STARTING'
,'ENABLED'
, and'DISABLED'
.
Can improve code quality using technique: structure code logically
Lay out the code so that it adheres to the logical structure. The code should read like a story. Just like we use section breaks, chapters and paragraphs to organize a story, use classes, methods, indentation and line spacing in your code to group related segments of the code. For example, you can use blank lines to group related statements together. Sometimes, the correctness of your code does not depend on the order in which you perform certain intermediary steps. Nevertheless, this order may affect the clarity of the story you are trying to tell. Choose the order that makes the story most readable.
Can improve code quality using technique: do not 'trip up' reader
Avoid things that would make the reader go ‘huh?’, such as,
- unused parameters in the method signature
- similar things look different
- different things that look similar
- multiple statements in the same line
- data flow anomalies such as, pre-assigning values to variables and modifying it without any use of the pre-assigned value
Can improve code quality using technique: practice kissing
As the old adage goes, "keep it simple, stupid” (KISS). Do not try to write ‘clever’ code. For example, do not dismiss the brute-force yet simple solution in favor of a complicated one because of some ‘supposed benefits’ such as 'better reusability' unless you have a strong justification.
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. --Brian W. Kernighan
Programs must be written for people to read, and only incidentally for machines to execute. --Abelson and Sussman
Can improve code quality using technique: avoid premature optimizations
Optimizing code prematurely has several drawbacks:
- We may not know which parts are the real performance bottlenecks. This is especially the case when the code undergoes transformations (e.g. compiling, minifying, transpiling, etc.) before it becomes an executable. Ideally, you should use a profiler tool to identify the actual bottlenecks of the code first, and optimize only those parts.
- Optimizing can complicate the code, affecting correctness and understandability
- Hand-optimized code can be harder for the compiler to optimize (the simpler the code, the easier for the compiler to optimize it). In many cases a compiler can do a better job of optimizing the runtime code if you don't get in the way by trying to hand-optimize the source code.
A popular saying in the industry is make it work, make it right, make it fast which means in most cases getting the code to perform correctly should take priority over optimizing it. If the code doesn't work correctly, it has no value on matter how fast/efficient it it.
Premature optimization is the root of all evil in programming. --Donald Knuth
Note that there are cases where optimizing takes priority over other things e.g. when writing code for resource-constrained environments. This guideline simply a caution that you should optimize only when it is really needed.
Can improve code quality using technique: SLAP hard
Avoid varying the level of
Example:
Bad
readData();
salary = basic*rise+1000;
tax = (taxable?salary*0.07:0);
displayResult();
Good
readData();
processData();
displayResult();
Design → Design Fundamentals → Abstraction →
Abstraction is a technique for dealing with complexity. It works by establishing a level of complexity we are interested in, and suppressing the more complex details below that level.
The guiding principle of abstraction is that only details that are relevant to the current perspective or the task at hand needs to be considered. As most programs are written to solve complex problems involving large amounts of intricate details, it is impossible to deal with all these details at the same time. That is where abstraction can help.
Ignoring lower level data items and thinking in terms of bigger entities is called data abstraction.
Within a certain software component, we might deal with a user data type, while ignoring the details contained in the user data item such as name, and date of birth. These details have been ‘abstracted away’ as they do not affect the task of that software component.
Control abstraction abstracts away details of the actual control flow to focus on tasks at a simplified level.
print(“Hello”)
is an abstraction of the actual output mechanism within the computer.
Abstraction can be applied repeatedly to obtain progressively higher levels of abstractions.
An example of different levels of data abstraction: a File
is a data item that is at a higher level than an array and an array is at a higher level than a bit.
An example of different levels of control abstraction: execute(Game)
is at a higher level than print(Char)
which is at a higher than an Assembly language instruction MOV
.
Abstraction is a general concept that is not limited to just data or control abstractions.
Some more general examples of abstraction:
- An OOP class is an abstraction over related data and behaviors.
- An architecture is a higher-level abstraction of the design of a software.
- Models (e.g., UML models) are abstractions of some aspect of reality.
Can improve code quality using technique: make the happy path prominent
The happy path (i.e. the execution path taken when everything goes well) should be clear and prominent in your code. Restructure the code to make the happy path unindented as much as possible. It is the ‘unusual’ cases that should be indented. Someone reading the code should not get distracted by alternative paths taken when error conditions happen. One technique that could help in this regard is the use of guard clauses.
Example:
Bad
if (!isUnusualCase) { //detecting an unusual condition
if (!isErrorCase) {
start(); //main path
process();
cleanup();
exit();
} else {
handleError();
}
} else {
handleUnusualCase(); //handling that unusual condition
}
In the code above,
- Unusual condition detection is separated from their handling.
- Main path is nested deeply.
Good
if (isUnusualCase) { //Guard Clause
handleUnusualCase();
return;
}
if (isErrorCase) { //Guard Clause
handleError();
return;
}
start();
process();
cleanup();
exit();
In contrast, the above code
- deals with unusual conditions as soon as they are detected so that the reader doesn't have to remember them for long.
- keeps the main path un-indented.
Can explain the need for following a standard
One essential way to improve code quality is to follow a consistent style. That is why software engineers follow a strict coding standard (aka style guide).
The aim of a coding standard is to make the entire code base look like it was written by one person. A coding standard is usually specific to a programming language and specifies guidelines such as the location of opening and closing braces, indentation styles and naming styles (e.g. whether to use Hungarian style, Pascal casing, Camel casing, etc.). It is important that the whole team/company use the same coding standard and that standard is not generally inconsistent with typical industry practices. If a company's coding standards is very different from what is used typically in the industry, new recruits will take longer to get used to the company's coding style.
💡 IDEs can help to enforce some parts of a coding standard e.g. indentation rules.
What is the recommended approach regarding coding standards?
c
What is the aim of using a coding standard? How does it help?
Can follow simple mechanical style rules
Learn basic guidelines of the Java coding standard (by OSS-Generic)
Sample coding standard: PEP 8 Python Style Guide -- by Python.org
Consider the code given below:
import java.util.*;
public class Task {
public static final String descriptionPrefix = "description: ";
private String description;
private boolean important;
List<String> pastDescription = new ArrayList<>(); // a list of past descriptions
public Task(String d) {
this.description = d;
if (!d.isEmpty())
this.important = true;
}
public String getAsXML() { return "<task>"+description+"</task>"; }
/**
* Print the description as a string.
*/
public void printingDescription(){ System.out.println(this); }
@Override
public String toString() { return descriptionPrefix + description; }
}
In what ways the code violate the basic guidelines (i.e., those marked with one ⭐️) of the OSS-Generic Java Coding Standard given here?
Here are three:
descriptionPrefix
is a constant and should be namedDESCRIPTION_PREFIX
- method name
printingDescription()
should be named asprintDescription()
- boolean variable
important
should be named to sound boolean e.g.,isImportant
There are many more.
Can follow intermediate style rules
Go through the provided Java coding standard and learn the intermediate style rules.
According to the given Java coding standard, which one of these is not a good name?
b
Explanation: checkWeight
is an action. Naming variables as actions makes the code harder to follow. isWeightValid
may be a better name.
Repeat the exercise in the panel below but also find violations of intermediate level guidelines.
Consider the code given below:
import java.util.*;
public class Task {
public static final String descriptionPrefix = "description: ";
private String description;
private boolean important;
List<String> pastDescription = new ArrayList<>(); // a list of past descriptions
public Task(String d) {
this.description = d;
if (!d.isEmpty())
this.important = true;
}
public String getAsXML() { return "<task>"+description+"</task>"; }
/**
* Print the description as a string.
*/
public void printingDescription(){ System.out.println(this); }
@Override
public String toString() { return descriptionPrefix + description; }
}
In what ways the code violate the basic guidelines (i.e., those marked with one ⭐️) of the OSS-Generic Java Coding Standard given here?
Here are three:
descriptionPrefix
is a constant and should be namedDESCRIPTION_PREFIX
- method name
printingDescription()
should be named asprintDescription()
- boolean variable
important
should be named to sound boolean e.g.,isImportant
There are many more.
Here's one you are more likely to miss:
* Print the description as a string.
→* Prints the description as a string.
There are more.
Can explain the need for good names in code
Proper naming improves the readability. It also reduces bugs caused by ambiguities regarding the intent of a variable or a method.
There are only two hard things in Computer Science: cache invalidation and naming things. -- Phil Karlton
Can improve code quality using technique: use nouns for things and verbs for actions
Every system is built from a domain-specific language designed by the programmers to describe that system. Functions are the verbs of that language, and classes are the nouns. ― Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship
Use nouns for classes/variables and verbs for methods/functions.
Examples:
Name for a | Bad | Good |
---|---|---|
Class | CheckLimit |
LimitChecker |
method | result() |
calculate() |
Distinguish clearly between single-valued and multivalued variables.
Examples:
Good
Person student;
ArrayList<Person> students;
Good
student = Person('Jim')
students = [Person('Jim'), Person('Alice')]
Can improve code quality using technique: use standard words
Use correct spelling in names. Avoid 'texting-style' spelling. Avoid foreign language words, slang, and names that are only meaningful within specific contexts/times e.g. terms from private jokes, a TV show currently popular in your country
Can improve code quality using technique: use name to explain
A name is not just for differentiation; it should explain the named entity to the reader accurately and at a sufficient level of detail.
Examples:
Bad | Good |
---|---|
processInput() (what 'process'?) |
removeWhiteSpaceFromInput() |
flag |
isValidInput |
temp |
If the name has multiple words, they should be in a sensible order.
Examples:
Bad | Good |
---|---|
bySizeOrder() |
orderBySize() |
Imagine going to the doctor's and saying "My eye1 is swollen"! Don’t use numbers or case to distinguish names.
Examples:
Bad | Bad | Good |
---|---|---|
value1 , value2 |
value , Value |
originalValue , finalValue |
Can improve code quality using technique: not too long, not too short
While it is preferable not to have lengthy names, names that are 'too short' are even worse. If you must abbreviate or use acronyms, do it consistently. Explain their full meaning at an obvious location.
Can improve code quality using technique: avoid misleading names
Related things should be named similarly, while unrelated things should NOT.
Example: Consider these variables
colorBlack
: hex value for color blackcolorWhite
: hex value for color whitecolorBlue
: number of times blue is usedhexForRed
: : hex value for color red
This is misleading because colorBlue
is named similar to colorWhite
and colorBlack
but has a different purpose while hexForRed
is named differently but has very similar purpose to the first two variables. The following is better:
hexForBlack
hexForWhite
hexForRed
blueColorCount
Avoid misleading or ambiguous names (e.g. those with multiple meanings), similar sounding names, hard-to-pronounce ones (e.g. avoid ambiguities like "is that a lowercase L, capital I or number 1?", or "is that number 0 or letter O?"), almost similar names.
Examples:
Bad | Good | Reason |
---|---|---|
phase0 |
phaseZero |
Is that zero or letter O? |
rwrLgtDirn |
rowerLegitDirection |
Hard to pronounce |
right left wrong |
rightDirection leftDirection wrongResponse |
right is for 'correct' or 'opposite of 'left'? |
redBooks readBooks |
redColorBooks booksRead |
red and read (past tense) sounds the same |
FiletMignon |
egg |
If the requirement is just a name of a food, egg is a much easier to type/say choice than FiletMignon |
Can explain the need for avoiding error-prone shortcuts
It is safer to use language constructs in the way they are meant to be used, even if the language allows shortcuts. Some such coding practices are common sources of bugs. Know them and avoid them.
Can improve code quality using technique: use the default branch
Always include a default branch in case
statements.
Furthermore, use it for the intended default action and not just to execute the last option. If there is no default action, you can use the 'default' branch to detect errors (i.e. if execution reached the default
branch, throw an exception). This also applies to the final else
of an if-else
construct. That is, the final else
should mean 'everything else', and not the final option. Do not use else
when an if
condition can be explicitly specified, unless there is absolutely no other possibility.
Bad
if (red) print "red";
else print "blue";
Good
if (red) print "red";
else if (blue) print "blue";
else error("incorrect input");
Can improve code quality using technique: don't recycle variables or parameters
- Use one variable for one purpose. Do not reuse a variable for a different purpose other than its intended one, just because the data type is the same.
- Do not reuse formal parameters as local variables inside the method.
Bad
double computeRectangleArea(double length, double width) {
length = length * width;
return length;
}
Good
double computeRectangleArea(double length, double width) {
double area;
area = length * width;
return area;
}
Can improve code quality using technique: avoid empty catch blocks
Never write an empty catch
statement. At least give a comment to explain why the catch
block is left empty.
Can improve code quality using technique: delete dead code
We all feel reluctant to delete code we have painstakingly written, even if we have no use for that code any more ("I spent a lot of time writing that code; what if we need it again?"). Consider all code as baggage you have to carry; get rid of unused code the moment it becomes redundant. If you need that code again, simply recover it from the revision control tool you are using. Deleting code you wrote previously is a sign that you are improving.
Can improve code quality using technique: minimise scope of variables
Minimize global variables. Global variables may be the most convenient way to pass information around, but they do create implicit links between code segments that use the global variable. Avoid them as much as possible.
Define variables in the least possible scope. For example, if the variable is used only within the if
block of the conditional statement, it should be declared inside that if
block.
The most powerful technique for minimizing the scope of a local variable is to declare it where it is first used. -- Effective Java, by Joshua Bloch
Resources:
Can improve code quality using technique: minimise code duplication
Code duplication, especially when you copy-paste-modify code, often indicates a poor quality implementation. While it may not be possible to have zero duplication, always think twice before duplicating code; most often there is a better alternative.
This guideline is closely related to the
Supplmentary → Principles →
DRY (Don't Repeat Yourself) Principle: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system The Pragmatic Programmer, by Andy Hunt and Dave Thomas
This principle guards against duplication of information.
The functionality implemented twice is a violation of the DRY principle even if the two implementations are different.
The value a system-wide timeout being defined in multiple places is a violation of DRY.
Can explain the need for commenting minimally but sufficiently
Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer. --Steve McConnell, Author of Clean Code
Some think commenting heavily increases the 'code quality'. This is not so. Avoid writing comments to explain bad code. Improve the code to make it self-explanatory.
Can improve code quality using technique: do not repeat the obvious
If the code is self-explanatory, refrain from repeating the description in a comment just for the sake of 'good documentation'.
Bad
// increment x
x++;
//trim the input
trimInput();
Can improve code quality using technique: write to the reader
Do not write comments as if they are private notes to self. Instead, write them well enough to be understood by another programmer. One type of comments that is almost always useful is the header comment that you write for a class or an operation to explain its purpose.
Examples:
Bad Reason: this comment will only make sense to the person who wrote it
// a quick trim function used to fix bug I detected overnight
void trimInput(){
....
}
Good
/** Trims the input of leading and trailing spaces */
void trimInput(){
....
}
Bad Reason: this comment will only make sense to the person who wrote it
# a quick trim function used to fix bug I detected overnight
def trim_input():
...
Good
def trim_input():
"""Trim the input of leading and trailing spaces"""
...
Can improve code quality using technique: explain what and why, not how
Comments should explain what and why aspect of the code, rather than the how aspect.
What : The specification of what the code supposed to do. The reader can compare such comments to the implementation to verify if the implementation is correct
Example: This method is possibly buggy because the implementation does not seem to match the comment. In this case the comment could help the reader to detect the bug.
/** Removes all spaces from the {@code input} */
void compact(String input){
input.trim();
}
Why : The rationale for the current implementation.
Example: Without this comment, the reader will not know the reason for calling this method.
// Remove spaces to comply with IE23.5 formatting rules
compact(input);
How : The explanation for how the code works. This should already be apparent from the code, if the code is self-explanatory. Adding comments to explain the same thing is redundant.
Example:
Bad Reason: Comment explains how the code works.
// return true if both left end and right end are correct or the size has not incremented
return (left && right) || (input.size() == size);
Good Reason: Code refactored to be self-explanatory. Comment no longer needed.
boolean isSameSize = (input.size() == size) ;
return (isLeftEndCorrect && isRightEndCorrect) || isSameSize;
null
Documentation:
-
Update documentation to match the product.
-
Create the first version of your Project Portfolio Page (PPP). Reason: Each member needs to create a PPP to describe your contribution to the project. Creating a PPP takes a significant effort; it is too risky to leave it to the last week of the project.
Relevant: [
At the end of the project each student is required to submit a Project Portfolio Page.
-
Objective:
- For you to use (e.g. in your resume) as a well-documented data point of your SE experience
- For us to use as a data point to evaluate your,
- contributions to the project
- your documentation skills
-
What to include:
- Main things to include:
- links to collated code
- features you implemented (include relevant extracts from the user guide and the developer guide)
- features you propose to implement in future (include relevant extracts from the user guide and the developer guide if applicable)
Note: the purpose of allowing you to include proposed features is to provide you more flexibility to show your documentation skills. e.g. you can bring in a proposed feature just to give you an opportunity to use a UML diagram type not used by the actual features. - other significant contributions to the project e.g. tweaks to existing features, setting up project tools
- Other things you can include:
- Evidence of helping others e.g. responses you posted in our forum, bugs you reported in other team's products,
- Evidence of technical leadership e.g. offering features for others to reuse, evidence of those features being reused by others
- If you plan to use the PPP in your Resume, you can also include your SE work outside of the module (will not be graded)
- Main things to include:
-
Format:
- Page limit: If you have more content than the limit given below, shorten (or omit some content) so that you do not exceed the page limit. Having too much content in the PPP will be viewed unfavorably during grading. Note: the page limits given below are after converting to PDF format. The actual amount of content you require is actually less than what these numbers suggest because the HTML → PDF conversion adds a lot of spacing around content.
Content Limit Description in point form 0.5-1 Extracts from the User Guide 1-3 Extracts from the Developer Guide 3-6 Total 5-10 - Follow the example in the AddressBook-Level4.
- 💡 You can use the Asciidoc's
include
feature to include sections from the developer guide or the user guide in your PPP. Follow the example in the sample. - ❗️ It is assumed that all contents in the PPP were written primarily by you. If any section is written by someone else e.g. someone else wrote described the feature in the User Guide but you implemented the feature, clearly state that the section was written by someone else (e.g.
Start of Extract [from: User Guide] written by Jane Doe
). Reason: Your writing skills will be evaluated based on the PPP
- Page limit: If you have more content than the limit given below, shorten (or omit some content) so that you do not exceed the page limit. Having too much content in the PPP will be viewed unfavorably during grading. Note: the page limits given below are after converting to PDF format. The actual amount of content you require is actually less than what these numbers suggest because the HTML → PDF conversion adds a lot of spacing around content.
Demo:
- Do a product demo to serve as a rehearsal for the final project demo at v1.4
- Follow
final demo instructions as much as possible. - Cover all features, not just the ones added in the recent iteration.
- Try to make it a 'well prepared' demo i.e., know in advance exactly what you'll do in the demo.
- Follow
-
Duration: Strictly
(teamSize x 3) + 1
minutes e.g. 17 minutes for a 5-person team. Exceeding this limit will be penalized. The extra minute is for the first speaker to give an overview of the product. -
Target audience: Assume you are giving a demo to a higher-level manager of your company, to brief him/her on the current capabilities of the product. This is the first time they are seeing the new product you developed but they are familiar with the AddressBook-level4 (AB4) product.
-
Scope:
- Each person should demo the enhancements they added. However, it's ok for one member to do all the typing.
- Subjected to the constraint mentioned in the previous point, as far as possible, organize the demo to present a cohesive picture of the product as a whole, presented in a logical order. Remember to explain the profile of the target user profile and value proposition early in the demo.
- It is recommended you showcase how the feature improves the user’s life rather than simply describe each feature.
- No need to cover design/implementation details as the manager is not interested in those details.
- Mention features you inherited from AB4 only if they are needed to explain your new features. Reason: existing features will not earn you marks, and the audience is already familiar with AB4 features.
-
Structure:
- Demo the product using the same executable you submitted, on your own laptop, using the TV.
- It can be a sitting down demo: You'll be demonstrating the features using the TV while sitting down. But you may stand around the TV if you prefer that way.
- It will be uninterrupted demo: The audience members will not interrupt you during the demo. That means you should finish within the given time.
- The app should be populated with a significant amount of realistic data at the start. e.g at least 20 contacts.
- Dress code : The level of formality is up to you, but it is recommended that the whole team dress at the same level.
-
Optimizing the time:
- Spend as much time as possible on demonstrating the actual product. Not recommended to use slides (if you do, use them sparingly) or videos or lengthy narrations.
Avoid skits, re-enactments, dramatizations etc. This is not a sales pitch or an informercial. While you need to show how a user use the product to get value, but you don’t need to act like an imaginary user. For example, [Instead of this]Jim get’s a call from boss. "Ring ring", "hello", "oh hi Jim, can we postpone the meeting?" "Sure". Jim hang up and curses the boss under his breath. Now he starts typing ..etc.
[do this]If Jim needs to postpone the meeting, he can type …
It’s not that dramatization is bad or we don’t like it. We simply don’t have enough time for it.
Note that CS2101 demo requirements may differ. Different context → Different requirements. - Rehearse the steps well and ensure you can do a smooth demo. Poor quality demos can affect your grade.
- Don’t waste time repeating things the target audience already knows. e.g. no need to say things like "We are students from NUS, SoC".
- Bring sufficient amount of sample data and know how to load them to the system. You should not plan to type all the sample data during the demo itself. On the other hand, trying to demo a product using just 1-2 sample data creates a bad impression too.
- Plan the demo to be in sync with the impression you want to create. For example, if you are trying to convince that the product is easy to use, show the easiest way to perform a task before you show the full command with all the bells and whistles.
- Limit the demo to CLI inputs only. There is no point explaining GUI inputs because they don't earn marks.
- Spend as much time as possible on demonstrating the actual product. Not recommended to use slides (if you do, use them sparingly) or videos or lengthy narrations.
Outcomes
🅿️ Project
W12.1
Can build a 1-2 KLoC program
Complete the project as specified.
Tutorial 12
W11.3b
Can explain the costs and benefits of reuse
Implementation → Reuse → Introduction →
While you may be tempted to use many libraries/frameworks/platform that seem to crop up on a regular basis and promise to bring great benefits, note that there are costs associated with reuse. Here are some:
- The reused code may be an overkill (think using a sledgehammer to crack a nut) increasing the size of, or/and degrading the performance of, your software.
- The reused software may not be mature/stable enough to be used in an important product. That means the software can change drastically and rapidly, possibly in ways that break your software.
- Non-mature software has the risk of dying off as fast as they emerged, leaving you with a dependency that is no longer maintained.
- The license of the reused software (or its dependencies) restrict how you can use/develop your software.
- The reused software might have bugs, missing features, or security vulnerabilities that are important to your product but not so important to the maintainers of that software, which means those flaws will not get fixed as fast as you need them to.
- Malicious code can sneak into your product via compromised dependencies.
One of your teammates is proposing to use a recently-released “cool” UI framework for your class project. List the pros and cons of this idea.
Pros
- The potential to create a much better product by reusing the framework.
- Learning a new framework is good for the future job prospects.
Cons
- Learning curve may be steep.
- May not be stable (it was recently released).
- May not allow us to do exactly what we want. While frameworks allow customization, such customization can be limited.
- Performance penalties.
- Might interfere with learning objectives of the module.
Note that having more cons does not mean we should not use this framework. Further investigation is required before we can make a final decision.
Evidence:
Have reused software. Can explain general costs and benefits of the code you have reused.
W11.3c
Can explain APIs
Implementation → Reuse → APIs →
An Application Programming Interface (API) specifies the interface through which other programs can interact with a software component. It is a contract between the component and its clients.
A class has an API (e.g., API of the Java String
class, API of the Python str
class) which is a collection of public methods that you can invoke to make use of the class.
The GitHub API is a collection of Web request formats GitHub server accepts and the corresponding responses. We can write a program that interacts with GitHub through that API.
When developing large systems, if you define the API of each components early, the development team can develop the components in parallel because the future behavior of the other components are now more predictable.
Choose the correct statements
- a. A software component can have an API.
- b. Any method of a class is part of its API.
- c. Private methods of a class are not part of its API.
- d. The API forms the contract between the component developer and the component user.
- e. Sequence diagrams can be used to show how components interact with each other via APIs.
(a) (c) (d) (e)
Explanation: (b) is incorrect because private methods cannot be a part of the API
Defining component APIs early is useful for developing components in parallel.
True
Explanation: Yes, once we know the precise behavior expected of each component, we can start developing them in parallel.
Evidence:
Know the API of the AddressBook component you are in charge of and the APIs of the other components your component depends on.
W11.3d
Can explain libraries
Implementation → Reuse → Libraries →
A library is a collection of modular code that is general and can be used by other programs.
Java classes you get with the JDK (such as String
, ArrayList
, HashMap
, etc.) are library classes that are provided in the default Java distribution.
Natty is a Java library that can be used for parsing strings that represent dates e.g. The 31st of April in the year 2008
built-in modules you get with Python (such as csv
, random
, sys
, etc.) are libraries that are provided in the default Python distribution. Classes such as list
, str
, dict
are built-in library classes that you get with Python.
Colorama is a Python library that can be used for colorizing text in a CLI.
Evidence:
Identify libraries used (or potentially usable) in your project.
W11.3e
Can make use of a library
Implementation → Reuse → Libraries →
These are the typical steps required to use a library.
- Read the documentation to confirm that its functionality fits your needs
- Check the license to confirm that it allows reuse in the way you plan to reuse it. For example, some libraries might allow non-commercial use only.
- Download the library and make it accessible to your project. Alternatively, you can configure your
dependency management tool to do it for you. - Call the library API from your code where you need to use the library functionality.
Evidence:
Have used libraries (or reused code from elsewhere) in your project.
W11.3f
Can explain frameworks
Implementation → Reuse → Frameworks →
The overall structure and execution flow of a specific category of software systems can be very similar. The similarity is an opportunity to reuse at a high scale.
Running example:
IDEs for different programming languages are similar in how they support editing code, organizing project files, debugging, etc.
A software framework is a reusable implementation of a software (or part thereof) providing generic functionality that can be selectively customized to produce a specific application.
Running example:
Eclipse is an IDE framework that can be used to create IDEs for different programming languages.
Some frameworks provide a complete implementation of a default behavior which makes them immediately usable.
Running example:
Eclipse is a fully functional Java IDE out-of-the-box.
A framework facilitates the adaptation and customization of some desired functionality.
Running example:
Eclipse plugin system can be used to create an IDE for different programming languages while reusing most of the existing IDE features of Eclipse. E.g. https://marketplace.eclipse.org/content/pydev-python-ide-eclipse
Some frameworks cover only a specific components or an aspect.
JavaFx a framework for creating Java GUIs. TkInter is a GUI framework for Python.
More examples of frameworks
- Frameworks for Web-based applications: Drupal(PHP), Django(Python), Ruby on Rails (Ruby), Spring (Java)
- Frameworks for testing: JUnit (Java), unittest (Python), Jest (Java Script)
Evidence:
Identify frameworks used (or potentially usable) in your project.
W11.3g
Can differentiate between frameworks and libraries
Implementation → Reuse → Frameworks →
Although both frameworks and libraries are reuse mechanisms, there are notable differences:
-
Libraries are meant to be used ‘as is’ while frameworks are meant to be customized/extended. e.g., writing plugins for Eclipse so that it can be used as an IDE for different languages (C++, PHP, etc.), adding modules and themes to Drupal, and adding test cases to JUnit.
-
Your code calls the library code while the framework code calls your code. Frameworks use a technique called inversion of control, aka the “Hollywood principle” (i.e. don’t call us, we’ll call you!). That is, you write code that will be called by the framework, e.g. writing test methods that will be called by the JUnit framework. In the case of libraries, your code calls libraries.
Choose correct statements about software frameworks.
- a. They follow the hollywood principle, otherwise known as ‘inversion of control’
- b. They come with full or partial implementation.
- c. They are more concrete than patterns or principles.
- d. They are often configurable.
- e. They are reuse mechanisms.
- f. They are similar to reusable libraries but bigger.
(a)(b)(c)(d)(e)(f)
Explanation: While both libraries and frameworks are reuse mechanisms, and both more concrete than principles and patterns, libraries differ from frameworks in some key ways. One of them is the ‘inversion of control’ used by frameworks but not libraries. Furthermore, frameworks do not have to be bigger than libraries all the time.
Which one of these are frameworks ?
(a)(b)(c)(d)
Explanation: These are frameworks.
Evidence:
Explain the difference between libraries and frameworks using frameworks and libraries in your project as examples.
W11.3h
Can explain platforms
Implementation → Reuse → Platforms →
A platform provides a runtime environment for applications. A platform is often bundled with various libraries, tools, frameworks, and technologies in addition to a runtime environment but the defining characteristic of a software platform is the presence of a runtime environment.
Technically, an operating system can be called a platform. For example, Windows PC is a platform for desktop applications while iOS is a platform for mobile apps.
Two well-known examples of platforms are JavaEE and .NET, both of which sit above Operating systems layer, and are used to develop
- JavaEE (Java Enterprise Edition) is both a framework and a platform for writing enterprise applications. The runtime used by the JavaEE applications is the JVM (Java Virtual Machine) that can run on different Operating Systems.
- .NET is a similar platform and a framework. Its runtime is called CLR (Common Language Runtime) and usually used on Windows machines.
Enterprise Application: ‘enterprise applications’ means software applications used at organizations level and therefore has to meet much higher demands (such as in scalability, security, performance, and robustness) than software meant for individual use.
Evidence:
Identify platforms used (or potentially usable) in your project.
W11.5a
Can explain unit testing
Quality Assurance → Testing → Unit Testing →
Unit testing : testing individual units (methods, classes, subsystems, ...) to ensure each piece works correctly.
In OOP code, it is common to write one or more unit tests for each public method of a class.
Here are the code skeletons for a Foo
class containing two methods and a FooTest
class that contains unit tests for those two methods.
class Foo{
String read(){
//...
}
void write(String input){
//...
}
}
class FooTest{
@Test
void read(){
//a unit test for Foo#read() method
}
@Test
void write_emptyInput_exceptionThrown(){
//a unit tests for Foo#write(String) method
}
@Test
void write_normalInput_writtenCorrectly(){
//another unit tests for Foo#write(String) method
}
}
import unittest
class Foo:
def read(self):
# ...
def write(self, input):
# ...
class FooTest(unittest.TestCase):
def test_read(sefl):
# a unit test for read() method
def test_write_emptyIntput_ignored(self):
# a unit tests for write(string) method
def test_write_normalInput_writtenCorrectly(self):
# another unit tests for write(string) method
Side readings:
- [Web article] The three pillars of unit testing - A short article about what makes a good unit test.
- Learning from Apple’s #gotofail Security Bug - How unit testing (and other good coding practices) could have prevented a major security bug.
Evidence:
Identify some unit tests in AddressBook-Level4 (or your own project).
W11.5b
Can use stubs to isolate an SUT from its dependencies
Quality Assurance → Testing → Unit Testing →
A proper unit test requires the unit to be tested in isolation so that bugs in the
If a Logic
class depends on a Storage
class, unit testing the Logic
class requires isolating the Logic
class from the Storage
class.
Stubs can isolate the
Stub: A stub has the same interface as the component it replaces, but its implementation is so simple that it is unlikely to have any bugs. It mimics the responses of the component, but only for the a limited set of predetermined inputs. That is, it does not know how to respond to any other inputs. Typically, these mimicked responses are hard-coded in the stub rather than computed or retrieved from elsewhere, e.g. from a database.
Consider the code below:
class Logic {
Storage s;
Logic(Storage s) {
this.s = s;
}
String getName(int index) {
return "Name: " + s.getName(index);
}
}
interface Storage {
String getName(int index);
}
class DatabaseStorage implements Storage {
@Override
public String getName(int index) {
return readValueFromDatabase(index);
}
private String readValueFromDatabase(int index) {
// retrieve name from the database
}
}
Normally, you would use the Logic
class as follows (not how the Logic
object depends on a DatabaseStorage
object to perform the getName()
operation):
Logic logic = new Logic(new DatabaseStorage());
String name = logic.getName(23);
You can test it like this:
@Test
void getName() {
Logic logic = new Logic(new DatabaseStorage());
assertEquals("Name: John", logic.getName(5));
}
However, this logic
object being tested is making use of a DataBaseStorage
object which means a bug in the DatabaseStorage
class can affect the test. Therefore, this test is not testing Logic
in isolation from its dependencies and hence it is not a pure unit test.
Here is a stub class you can use in place of DatabaseStorage
:
class StorageStub implements Storage {
@Override
public String getName(int index) {
if(index == 5) {
return "Adam";
} else {
throw new UnsupportedOperationException();
}
}
}
Note how the stub has the same interface as the real dependency, is so simple that it is unlikely to contain bugs, and is pre-configured to respond with a hard-coded response, presumably, the correct response DatabaseStorage
is expected to return for the given test input.
Here is how you can use the stub to write a unit test. This test is not affected by any bugs in the DatabaseStorage
class and hence is a pure unit test.
@Test
void getName() {
Logic logic = new Logic(new StorageStub());
assertEquals("Name: Adam", logic.getName(5));
}
In addition to Stubs, there are other type of replacements you can use during testing. E.g. Mocks, Fakes, Dummies, Spies.
- Mocks Aren't Stubs by Martin Fowler -- An in-depth article about how Stubs differ from other types of test helpers.
Stubs help us to test a component in isolation from its dependencies.
True
Evidence:
Identify some tests in AddressBook-Level4,
- that can be made pure unit tests by introducing stubs
- that are using stubs to isolate the SUT from its dependencies
W11.5c
Can explain integration testing
Quality Assurance → Testing → Integration Testing →
Integration testing : testing whether different parts of the software work together (i.e. integrates) as expected. Integration tests aim to discover bugs in the 'glue code' related to how components interact with each other. These bugs are often the result of misunderstanding of what the parts are supposed to do vs what the parts are actually doing.
Suppose a class Car
users classes Engine
and Wheel
. If the Car
class assumed a Wheel
can support 200 mph speed but the actual Wheel
can only support 150 mph, it is the integration test that is supposed to uncover this discrepancy.
Evidence:
Explain the difference between unit tests and integration tests.
W11.5d
Can use integration testing
Quality Assurance → Testing → Integration Testing →
Integration testing is not simply a repetition of the unit test cases but run using the actual dependencies (instead of the stubs used in unit testing). Instead, integration tests are additional test cases that focus on the interactions between the parts.
Suppose a class Car
uses classes Engine
and Wheel
. Here is how you would go about doing pure integration tests:
a) First, unit test Engine
and Wheel
.
b) Next, unit test Car
in isolation of Engine
and Wheel
, using stubs for Engine
and Wheel
.
c) After that, do an integration test for Car
using it together with the Engine
and Wheel
classes to ensure the Car
integrates properly with the Engine
and the Wheel
.
In practice, developers often use a hybrid of unit+integration tests to minimize the need for stubs.
Here's how a hybrid unit+integration approach could be applied to the same example used above:
(a) First, unit test Engine
and Wheel
.
(b) Next, unit test Car
in isolation of Engine
and Wheel
, using stubs for Engine
and Wheel
.
(c) After that, do an integration test for Car
using it together with the Engine
and Wheel
classes to ensure the Car
integrates properly with the Engine
and the Wheel
. This step should include test cases that are meant to test the unit Car
(i.e. test cases used in the step (b) of the example above) as well as test cases that are meant to test the integration of Car
with Wheel
and Engine
(i.e. pure integration test cases used of the step (c) in the example above).
💡 Note that you no longer need stubs for Engine
and Wheel
. The downside is that Car
is never tested in isolation of its dependencies. Given that its dependencies are already unit tested, the risk of bugs in Engine
and Wheel
affecting the testing of Car
can be considered minimal.
Evidence:
Use tests from AddressBook-Level4 to illustrate the difference between unit testings and integration testing. Hint: good examples seedu.address.storage.StorageManagerTest
,seedu.address.logic.commands.AddCommandTest
,seedu.address.logic.commands.AddCommandIntegrationTest
W11.5e
Can explain system testing
Quality Assurance → Testing → System Testing →
System testing: take the whole system and test it against the system specification.
System testing is typically done by a testing team (also called a QA team).
System test cases are based on the specified external behavior of the system. Sometimes, system tests go beyond the bounds defined in the specification. This is useful when testing that the system fails 'gracefully' having pushed beyond its limits.
Suppose the SUT is a browser supposedly capable of handling web pages containing up to 5000 characters. Given below is a test case to test if the SUT fails gracefully if pushed beyond its limits.
Test case: load a web page that is too big
* Input: load a web page containing more than 5000 characters.
* Expected behavior: abort the loading of the page and show a meaningful error message.
This test case would fail if the browser attempted to load the large file anyway and crashed.
System testing includes testing against non-functional requirements too. Here are some examples.
- Performance testing – to ensure the system responds quickly.
- Load testing (also called stress testing or scalability testing) – to ensure the system can work under heavy load.
- Security testing – to test how secure the system is.
- Compatibility testing, interoperability testing – to check whether the system can work with other systems.
- Usability testing – to test how easy it is to use the system.
- Portability testing – to test whether the system works on different platforms.
Evidence:
Explain what system tests are. Give examples from your own project.
W11.5f
Can explain acceptance testing
Quality Assurance → Testing → Acceptance Testing →
Acceptance testing (aka User Acceptance Testing (UAT)): test the delivered system to ensure it meets the user requirements.
Acceptance tests give an assurance to the customer that the system does what it is intended to do. Acceptance test cases are often defined at the beginning of the project, usually based on the use case specification. Successful completion of UAT is often a prerequisite to the project sign-off.
Evidence:
Explain what acceptance tests are. Explain how acceptance testing of your project will be done.
W11.5g
Can explain the differences between system testing and acceptance testing
Quality Assurance → Testing → Acceptance Testing →
Acceptance testing comes after system testing. Similar to system testing, acceptance testing involves testing the whole system.
Some differences between system testing and acceptance testing:
System Testing | Acceptance Testing |
---|---|
Done against the system specification | Done against the requirements specification |
Done by testers of the project team | Done by a team that represents the customer |
Done on the development environment or a test bed | Done on the deployment site or on a close simulation of the deployment site |
Both negative and positive test cases | More focus on positive test cases |
Note: negative test cases: cases where the SUT is not expected to work normally e.g. incorrect inputs; positive test cases: cases where the SUT is expected to work normally
Requirement Specification vs System Specification
The requirement specification need not be the same as the system specification. Some example differences:
Requirements Specification | System Specification |
---|---|
limited to how the system behaves in normal working conditions | can also include details on how it will fail gracefully when pushed beyond limits, how to recover, etc. specification |
written in terms of problems that need to be solved (e.g. provide a method to locate an email quickly) | written in terms of how the system solve those problems (e.g. explain the email search feature) |
specifies the interface available for intended end-users | could contain additional APIs not available for end-users (for the use of developers/testers) |
However, in many cases one document serves as both a requirement specification and a system specification.
Passing system tests does not necessarily mean passing acceptance testing. Some examples:
- The system might work on the testbed environments but might not work the same way in the deployment environment, due to subtle differences between the two environments.
- The system might conform to the system specification but could fail to solve the problem it was supposed to solve for the user, due to flaws in the system design.
Choose the correct statements about system testing and acceptance testing.
- a. Both system testing and acceptance testing typically involve the whole system.
- b. System testing is typically more extensive than acceptance testing.
- c. System testing can include testing for non-functional qualities.
- d. Acceptance testing typically has more user involvement than system testing.
- e. In smaller projects, the developers may do system testing as well, in addition to developer testing.
- f. If system testing is adequately done, we need not do acceptance testing.
(a)(b)(c)(d)(e)(f)
Explanation:
(b) is correct because system testing can aim to cover all specified behaviors and can even go beyond the system specification. Therefore, system testing is typically more extensive than acceptance testing.
(f) is incorrect because it is possible for a system to pass system tests but fail acceptance tests.
Evidence:
Explain the difference between system tests and acceptance tests. Explain why both are needed.
W11.5h
Can explain alpha and beta testing
Quality Assurance → Testing → Alpha/Beta Testing →
Alpha testing is performed by the users, under controlled conditions set by the software development team.
Beta testing is performed by a selected subset of target users of the system in their natural work setting.
An open beta release is the release of not-yet-production-quality-but-almost-there software to the general population. For example, Google’s Gmail was in 'beta' for many years before the label was finally removed.
Evidence:
Explain alpha and beta testing. How can you do alpha and beta testing in your project?
W11.6b
Can use intermediate features of JUnit
Tools → JUnit →
Some intermediate JUnit techniques that may be useful:
- It is possible for a JUnit test case to verify if the SUT throws the right exception.
- JUnit Rules are a way to add additional behavior to a test. e.g. to make a test case use a temporary folder for storing files needed for (or generated by) the test.
- It is possible to write methods thar are automatically run before/after a test method/class. These are useful to do pre/post cleanups for example.
- Testing private methods is possible, although not always necessray
Evidence:
Use the mentioned features in the project.
W11.7b
Can explain validation and verification
Quality Assurance → Quality Assurance → Introduction →
Quality Assurance = Validation + Verification
QA involves checking two aspects:
- Validation: are we building the right system i.e., are the requirements correct?
- Verification: are we building the system right i.e., are the requirements implemented correctly?
Whether something belongs under validation or verification is not that important. What is more important is both are done, instead of limiting to verification (i.e., remember that the requirements can be wrong too).
Choose the correct statements about validation and verification.
- a. Validation: Are we building the right product?, Verification: Are we building the product right?
- b. It is very important to clearly distinguish between validation and verification.
- c. The important thing about validation and verification is to remember to pay adequate attention to both.
- d. Developer-testing is more about verification than validation.
- e. QA covers both validation and verification.
- f. A system crash is more likely to be a verification failure than a validation failure.
(a)(b)(c)(d)(e)(f)
Explanation:
Whether something belongs under validation or verification is not that important. What is more important is that we do both.
Developer testing is more about bugs in code, rather than bugs in the requirements.
In QA, system testing is more about verification (does the system follow the specification?) and acceptance testings is more about validation (does the system solve the user’s problem?).
A system crash is more likely to be a bug in the code, not in the requirements.
Evidence:
Explain validations and verification with concrete examples from the project.
W11.7c
Can explain code reviews
Quality Assurance → Quality Assurance → Code Reviews →
Code review is the systematic examination code with the intention of finding where the code can be improved.
Reviews can be done in various forms. Some examples below:
-
In
pair programming - As pair programming involves two programmers working on the same code at the same time, there is an implicit review of the code by the other member of the pair.
Pair Programming:
Pair programming is an agile software development technique in which two programmers work together at one workstation. One, the driver, writes code while the other, the observer or navigator, reviews each line of code as it is typed in. The two programmers switch roles frequently. [source: Wikipedia]
A good introduction to pair programming:
-
Pull Request reviews
- Project Management Platforms such as GitHub and BitBucket allows the new code to be proposed as Pull Requests and provides the ability for others to review the code in the PR.
-
Formal inspections
-
Inspections involve a group of people systematically examining a project artifacts to discover defects. Members of the inspection team play various roles during the process, such as:
- the author - the creator of the artifact
- the moderator - the planner and executor of the inspection meeting
- the secretary - the recorder of the findings of the inspection
- the inspector/reviewer - the one who inspects/reviews the artifact.
-
Advantages of code reviews over testing:
- It can detect functionality defects as well as other problems such as coding standard violations.
- Can verify non-code artifacts and incomplete code
- Do not require test drivers or stubs.
Disadvantages:
- It is a manual process and therefore, error prone.
Evidence:
Review PRs in the project.
W11.7d
Can explain static analysis
Quality Assurance → Quality Assurance → Static Analysis →
Static analysis: Static analysis is the analysis of code without actually executing the code.
Static analysis of code can find useful information such unused variables, unhandled exceptions, style errors, and statistics. Most modern IDEs come with some inbuilt static analysis capabilities. For example, an IDE can highlight unused variables as you type the code into the editor.
Higher-end static analyzer tools can perform for more complex analysis such as locating potential bugs, memory leaks, inefficient code structures etc.
Some example static analyzer for Java:
Linters are a subset of static analyzers that specifically aim to locate areas where the code can be made 'cleaner'.
Evidence:
Explain how static analysis is used in the project.
W11.8
Package the app as a JAR file
Extract from the Admin Info page:
Lecture 12
no webcast
Practice exam
-
Seating row assignment is here
-
Answers are given below. If you have any doubts about any of the question, please post in our forum to discuss.
Q:A
A1:A
A2:A
A3:A
A4:E
A5:E
A6:A
A7:E
A8:A
A9:E
A10:A
A11:A
A12:E
A13:E
A14:E
A15:A
A16:A
A17:E
A18:E
A19:E
A20:E
A21:A
A22:E
A23:A
A24:A
A25:E
A26:A
A27:E
A28:A
A29:E
A30:A
A31:A
A32:A
A33:E
A34:E
A35:A
A36:A
A37:A
A38:E
A39:A
A40:C
(This is a nonsense question, put there to give you a chance to use theC
option. No such deliberately incorrect questions in the actual exam paper though)
A41:E
A42:A
A43:A
A44:A
A45:A
A46:A
A47:A
A48:A
A49:E
A50:E
Talk on getting ready for internships
- [slides]