Midterm Flashcards
Why do we write software?
We write software to solve problems
What is essential complexity?
Essential difficulties are that are intrinsic to developing software.
These difficulties cannot be separated from the software development process.
What is accidental complexity?
Accidental difficulties emerge because of circumstance
What’s the “hard part” of writing software
Learning a new skill is hard.
The only way to get better, faster, and more efficient, is practice.
External Software Quality - what is it and what are the measures?
Refers to the quality of the software from the perspective of the stakeholders
Functionality, Reliability, Usability, Efficiency, Portability
Internal Software Quality - what is it and what are the measures?
Concerned with the software quality from the perspective of the developers
Primary - Maintainability
Within that: Analyzability, Changeability, Stability, Reusability, and Testability
what is the most important skill to achieving high internal software quality?
design
What is Compiling?
Compiling is turning human-readable source code into computer readable instructions
What is Interpreting?
Fundamentally the same idea as compiling, only rather than turning the entire program into machine code first, and then running second, when code is interpreted, we do both at the same time with the help of another program
How does Java work?
A programmer edits the .java file, then the .java files goes to the JDK to get compiled, which creates a .class file, which the JRE runs, and the JVM and JIT within the JRE interact with the actual computer to run the .class file
What is JDK?
Java Development Kit
Compiles code and produces a .class file, which is the bytecode that specifies the machine instructions
What is JRE?
Java Runtime Environment
Used to run Java programs
What is JVM?
Java Virtual Machine
JVM is actually an interpreter, that interprets the code it is given at runtime, which is the .class bytecode compiled by the JDK, and passed to the JVM by the JRE; JVM handles the direct interactions with the actual underlying hardware
What is JIT?
Just In Time Compiler
JIT is part of the JVM, specifically the part that can compile JVM bytecode instructions into machine code instructions for the underlying hardware
What is a distributed repository?
There is a remote repository, and you work on your working copy, commit to your local repository, and then push to the remote repository
What are the steps for merging?
- Commit your changes in your feature branch
- Checkout main
- Pull main
- Checkout feature branch
- git merge main to merge main into your feature branch
- Resolve any conflicts, commit and push
- Checkout main
- git merge featureBranch
How do you use branches effectively and safely?
One branch per feature
What are the advantages of Gradle?
- Greatly simplifies the process of downloading and using external libraries
- Greatly simplifies the process of updating to new libraries
- Greatly simplifies the process of building a Java project into a distributable Jar file
What does the dependencies section in Gradle do?
Dependencies section lists all the external libraries the program is using
What does the repositories section do in Gradle?
Repositories section tells Gradle that you want to download all references from the repository within the brackets
What does the Gradle build command do? “gradle build”
The gradle build command is used to compile, test, and package your project; uses the globally installed version of Gradle
What does Gradle’s test command do?
This tells gradle how to execute our tests
What is the meaning of a fat-jar
It is a jar that contains not only my own code, but also all of the dependencies of my code
Gradle wrapper (gradlew) - what does it do? Why is it useful?
./gradlew build: Uses the Gradle Wrapper, which ensures consistency by downloading and using the project-specific Gradle version
What question does verification ask?
Does our code meet our specification?
What question does validation ask?
Is the customer satisfied with our product?
Which V is internal and which is external?
Verification - Internal
Validation - External
Defect
an existing problem according to the software specification in the product that has not been discovered yet
Failure
the inability of the software system to perform its function according to the specification
Mistake
a human error that produces something incorrect, such as a bug in the code or a misunderstood customer need
Error
the difference between the current state and the correct state
What is the primary purpose of testing?
To find defects
True or False. Testing code can prove your code is bug free.
False
@Test
Used to identify a test-method
Methods must meet the following conditions:
- Must be public and NOT static
- Must return void
- Must take in no arguments
@BeforeEach
Tag tells JUnit to run the function before each test
Ensures that the state of the instance variable is always the same at the start of every test, no matter what tests we’ve run, or how many of them we have run
assertEquals
Testing scenario passes if the expected output and the actual output match
Always put the expected value first and the actual value second
How do you use assertEquals with doubles?
When comparing double variables, we want to use: assertEquals(double expected, double actual, double tolerance)
How do you use assertEquals with Objects?
Uses the .equals() method of the class of expected to compare to actual
assertThrows
Used when we expect the code we are using to throw an exception
Ex: assertThrows(IndexOutOfBoundsException.class, () -> myList.get(0));
Should you test the interface or the implementation?
Interface
What is a sound test?
A sound test is one that correctly tests against the specification
What is a false positive?
a test fails, indicating a defect, but there is no defect with the code
What is a false negative?
a test passes, but it shouldn’t, as there is an underlying defect that the test fails to find
Equivalence Tests
“Typical” test cases - test cases that show how the software behaves under normal, predictable conditions
Boundary Tests
Typically cases where there is something interesting or unique about the test case to separate it from an equivalence case
Exception Tests
Test cases that cannot be meaningfully executed correctly and should throw exceptions
Robustness Tests
Test cases that are syntactically valid but semantically meaningless; Often times in these cases, it may be unclear how they are intended to behave, and it may be worth checking the specification itself
Black-Box Testing
The tester focuses on the functionality of the software without any knowledge of its internal workings or code
What is equivalence partioning?
Breaking up our equivalence cases into groups that largely behave the same
White-Box Testing
The tester has full knowledge of the internal workings, structure, and code of the system
The tester exampines the code and creates test cases based on its logic, internal paths, and flows
Aims to achieve high code coverage
Statement Coverage
Measure of what percentage of statements have been covered by our tests
100% statement coverage is a good minimum target for testing, as we don’t want to rely on any lines of code we haven’t tested at least once
Branch Coverage
We want to write tests such that we test the outcome of all conditional logic
Conditonal Coverage
For every boolean value, we want to evaluate the code if it’s true or false
Path Coverage
We are interested in what percentage of possible paths through our code we have taken
Test-Driven Development
Write tests first that describe the specification first, and only implement the method after the code is written
TDD is inherently Black Box Testing
When should we use exceptions when designing?
to enforce pre-conditions and post-conditions of our function
When should you never throw and exception?
You should never throw exceptions along with try-catch as a way of handling control-flow
When to use try-catch
Should only be used when you can meaningfully handle an exception or when dealing with checked exceptions, turning a checked exception into an unchecked exception
Never catch an exception you can’t handle
Unchecked Exceptions
Exceptions that occur at runtime and are not checked by the compiler; handling is optional
Checked Exceptions
Exceptions that are checked at compile time; handling is not optional
assert [boolean_statement]
If the asserted statement is false, then this results in an AssertionError, which is not the same as an exception, and cannot be caught with a try-catch or Exception or any sub-types; Replace assert statements with Exceptions when deploying
Using defensive programming to ensure class is used correctly
- Want to make sure that if an exception was thrown, we don’t change the state of the object in question
- After testing assertThrows, test assertEquals to ensure that the state did not change
Analyzability
Core part of software internal quality – maintainability
Readability - To what extent can we read and understand the code’s syntax
Understandability - To what extent can we read and understand the code’s semantics, or meaning and intent, of the code can be understood
What is the relationship between readability and understandability?
Readability is a necessary but insufficient condition for Understandability
Our code cannot be understandable if it isn’t readable; however, just because our code is readable does not mean it is understandable
What are some problems with internal quality of software to avoid
Long functions, large classes, long parameter list, boolean parameters, primitive obsessions, and message chains
Refactoring
the process of making changes to the code to improve the internal software quality, without making any changes to the functionality of the software
What are refactoring techniques?
Renaming identifiers, extract constants, abstract conditional logic, invert boolean, replace ErrorCode with Exception, replace Null with Optional, extract method, extract class, preserve whole object
What is DRY?
Don’t Repeat Yourself: avoiding duplication of code, logic, or data
What is WET?
Write Everything Twice: Suggests code duplication is sometimes acceptable, often when reuse would overcomplicate the code or reduce clarity
Lambda functions
A lambda function is an unnamed function; rather than naming a function, we create a function when it’s needed on the fly at runtime
What is the structure of a lambda function?
either (parameters) -> {code block} or (parameters) -> expression
What is Comparator used for?
sorted() and sort()
used in sorting lists, specifically Collections.sort(List<E> list, Comparator<E> comparator) and listInstance.sort(Comparator<E> comparator); also used in defining TreeSets</E></E></E>
What is Comparator’s Method?
public int compare(E e1, E e2)
What is Consumer used for?
forEach()
take in some value and doing something with it, but don’t return anything; used specifically in Java’s foreach function on Collections, as well as foreach and peek in Streams
What is Consumer’s Method?
public void accept(E e)
What is Predicate used for?
filter()
used for checking if a value meets some condition; specifically, it is used in the filter method for Streams
What is Predicate’s method?
public boolean test(E e)
What is Function used for?
map()
pass some instance of T as an argument, return an instance of R; used in the map function in Java Streams
What is Function’s method?
public R apply(T)
What is Executable used for?
assertThrows()
used in assertThrows, dynamicTest, and assumingThat
What is executable’s method?
public void execute()
What are Java Streams?
A stream is like an assembly line – we take in some collection of information on one side, pass it through a number of steps, and then at the end we have some useful information
What can you make streams from?
List, Set, Map (via entrySet())
Intermediate Operations: map
Method: map(Function<E,R> f)
Output: Stream<R>
Explanation: used to convert our data from one thing to another</R>
stream() vs parallelStream()
stream(): This gives us a Stream that we can now perform zero or more intermediate operations one (which will be the bulk of our logic), and one terminal option on, which converts the stream back into something useful like a List, or gives us summary information (such as an integer sum)
parallelStream(): We can replace stream() with parallelStream() to take advantage of automated multithreading
What is the cost of multithreading?
May do operations in an unpredictable order
Intermediate Operations: filter
Method: filter(Predicate p)
Output: Stream<E> with only elements that return true for our Predicate function; elements that return false are removed from the Stream
Explanation: we may want to “filter out” certain values from our stream, or only keep certain other values</E>
Intermediate Operations: sorted example
.sorted((a, b) -> a.size() - b.size())
Intermediate Operations: sorted
Method: sorted(Comparator<E> comparator)
Output: Stream<E> sorted by the comparator
Explanation: we may want to sort during our intermediate steps (such as when we get the five smallest states); this method lets us define a sorting methodology functionally</E></E>
Intermediate Operations: peek
Method: peek(Consumer<E> e)
Output: Stream<E> with no elements removed or altered (unless altered as a side effect of the Consumer function in peek)
Explanation: similar to forEach, this method performs some functions on every element in the Stream; however, forEach is a terminal operation, while peek is an intermediate operation</E></E>
Intermediate Operations: map example
.map(x -> x.size())
Intermediate Operations: peek example
.peek(System.out::println)
Intermediate Operations: limit example
.limit(10)
Intermediate Operations: filter example
.filter(x -> x.getPopulation > 1000000)
Intermediate Operations: limit
Method: limit(long n)
Output: Stream<E>, but only the first n elements
Explanation: in general, if we want to use limit, we only use it after sorted</E>
Intermediate Operations: distinct
Method: distinct()
Output: Stream<E> with all duplicate elements removed
Explanation: uses E’s .equals() method to remove any duplicate elements from our Stream</E>
Terminal Operations: forEach
Method: forEach(Consumer<E> e)
Output: void – forEach cannot be used to return anything directly
Explanation: used for performing some action with items left in the Stream at the end of the intermediate operations</E>
Intermediate Operations: distinct example
.distinct()
Terminal Operations: forEach example
names.stream().forEach(name -> System.out.println(name));
Terminal Operations: count
Method: count()
Output: long – the number of items left in the Stream
Terminal Operations: count example
.count()
Terminal Operations: toList example
List<Integer> evenNumbers = numbers.stream().filter(n -> n % 2 == 0).collect(Collectors.toList());</Integer>
Terminal Operations: toList
Collectors.toList() returns a List<E> which matches the state of the Stream at the end of the Stream chain of operations</E>
Terminal Operations: reduce
reduce allows you to define a way to reduce a stream of multiple objects to one value
Terminal Operations: reduce example
int totalPopulation = stateList.stream().map(State::getPopulation).reduce(0, (subtaotal, statePopulation) -> subtotal + statepopulation);
How do we tell the difference between a good design and a bad design?
A good software design is modular, maintainable, scalable, flexible, and simple, with a clear structure that facilitates testing and future changes
A bad software design is often rigid, difficult to maintain, and prone to errors, with tightly coupled components, poor scalability, and convoluted logic
Modularity
Break up our big problem into little problems that are easier to solve, and each unit is potentially reusable
Functional Independence
Each module can perform its own purpose with minimal interactions with the rest of the system
Gold standard – when two modules only interact through passing arguments and receiving return values via public functions
What do we want modules to be?
highly cohesive and loosely coupled
Abstraction
the process of hiding all unnecessary details and exposing only required details
What does it mean to design for the interface, not the implementation?
So long as the interface is maintained, changes to the implementation will not force changes to other modules
Information Hiding
Mechanism by which we create a limited interface to achieve abstraction
What does “highly cohesive” mean, and why is it desirable?
Highly cohesive refers to the degree to which the elements within a module, class, or component of a software system are closely related and focused on a single, well-defined task
Means that the methods, functions, and responsibilities inside a class or module all contribute to performing a unified and specific function
Why is abstraction good?
It encourages focusing on the abstractions (interfaces) that define the behavior of a system rather than the specific implementation details
This leads to more flexible, maintainable, and scalable systems
What does “loosely coupled” mean, and why is it desirable?
Loosely coupled refers to the degree of dependency between components, classes, or modules in a software system
In a loosely coupled system, components have minimal dependencies on each other, meaning changes in one component are less likely to affect other components
Each module or class interacts with others through well-defined interfaces, rather than relying on direct knowledge of their inner workings or implementation details
Coincidental Cohesion
Worst – very loose cohesion, avoid at all costs
Elements are combined into a module by effectively coincidence
Logical Cohesion
Worst – very loose cohesion, avoid at all costs
Elements are grouped into a module because they described the same general activity, or have similar interfaces, or are otherwise categorized the same
Temporal Cohesion
Worst – very loose cohesion, avoid at all costs
Where items are grouped because they occur at or around the same time (but not in any particular sequence), but are otherwise unrelated
Procedural Cohesion
Decent – acceptable, but often could be better
Supporting unrelated activities in which control passes from one activity to the next, ensuring they execute in a specific order
Communicational Cohesion
Decent – acceptable, but often could be better
Elements are grouped together in a module simply because they perform actions on the same data for either input or output
Sequential Cohesion
Best
Generally considered very cohesive, second only to functional
We can group modules into individual “procedures”
Functional Cohesion
Best
Describes a single well-supported function, i.e., a something that only does one thing
Module only has one behavior
This is very maintainable, because so long as the interface remains the same, the implementation can change without propagating change to any other module
Content Coupling
Occurs when one class modifies the inner state of a class it depends on directly instead of using the public interface
Common Coupling
When they have read and write access to the same global data
Control Coupling
When one module passes some kind of flag (typically a boolean) to another module which is used in control flow
Stamp Coupling
Exists when a module passes a data structure to another module, when the entire data structure is not needed
Data Coupling
The gold standard: all communication between modules is done via passing the minimum amount of data as arguments and returning exactly the data needed
This insures that any action taken by the “called” method, with the exception the data it returns, will not affect the execution of the calling method
Dependency
A class depends on another class when it uses methods or functions from that class
Aggregation/Composition
Aggregation “A aggregates B” - refers to an instance of class, A, “has” one or more instances of Class B
Composition - “A is composed of B” refers to a relationship where one or more instances of B are intrinsically part of another class, A. In general, B does not such that they often cannot be meaningfully seperated. The composing class brings together these items, and the items by themselves don’t have a meaning outside of their attachment to the composing class. Composition relationships are a subset of Aggregation relationships
Bidirectional Association, specifically in the form of mutual Aggregation
Two classes are connected in such a way that interaction may be needed both ways
In general, with bidirectional associations, the semantics generally refer to a relatively symmetrical relationship in the sense that neither class possesses the other, but they do reference each other
Realization (implementing interfaces)
A class realizes an interface or abstract by implementing/extending it
“is-a” relationship
Generalization (extending classes) aka Inheritance
a parent describes a general behavior, and a child describes a specific behavior, or specialization of the parent
Generalization encapsulates all “is-a” relationships
Why do we often say “Prefer Composition/Aggregation over Inheritance”?
Specifically, what is the flaw in Inheritance?
Introducing abstraction to support polymorphism is valuable when it reduces usage complexity by more than it increases coupling and implementation complexity
Creates a tight coupling:
1) The caller is coupled to the abstraction as either an aggregation or a dependency
2) Any implementators are coupled to the abstraction in a realization way
Single Responsibility Principles
Each software should have one, and only one, reason to change
Different between implementing interface and extending classes
Inheritance in Java is when a class (child) inherits the properties and methods of another class (parent)
A class can implement multiple interfaces, but can extend only one parent class
Inheritance means it must implement all methods declared in the interface, but extends means it inherits methods from the parent class (can override or add new ones)
Benefit of Polymorphism
Polymorphism enables the ability to “call the same method” on different types of objects and have each object respond in its own way
Dependency Inversion
Entities should only depend on abstractions, not on concretions
It suggests that high-level modules should not depend on low-level modules; both should depend on abstractions (such as interfaces or abstract classes)
Abstractions should not depend on details, but details should depend on abstractions