owned this note
owned this note
Published
Linked with GitHub
# Week 4
## OOP + Java: Inheritance
* The OOP concept **Inheritance** allows you to **define a new class based on an existing class.**
```
Other names for Base class: Parent class, Super class
Other names for Derived class: Child class, Sub class, Extended class
```
* Applying inheritance on a group of similar classes can result in the **common parts among classes being extracted into more general classes.**
* Inheritance relationships through a chain of classes can result in **inheritance hierarchies** (aka inheritance trees).
* **Multiple Inheritance** is when a class **inherits directly from multiple classes**. Multiple inheritance among classes is **allowed** in some languages (e.g., Python, C++) but **not** in other languages (e.g., **Java**, C#).

* ==**Method overloading**== is when there are **multiple methods with the same name but different type signatures.** Overloading is used to indicate that multiple operations do similar things but take different parameters.
```
Type Signature: The type signature of an operation is the type sequence of the
parameters. The return type and parameter names are not part of the type
signature. However, the parameter order is significant.
```

* ==**Method overriding**== is when a **sub-class changes the behavior inherited from the parent class** by re-implementing the method. Overridden methods have the **same name, same type signature, and same return type**.

* A **subclass inherits all the members** (fields, methods, and nested classes) **from its superclass**. *Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.*
* **Every class has one and only one direct superclass** (single inheritance), **except the `Object` class**, which has no superclass. In the absence of any other explicit superclass, every class is implicitly a subclass of Object. Classes can be derived from classes that are derived from classes that are derived from classes, and so on, and ultimately derived from the topmost class, Object. Such a class is said to be descended from all the classes in the inheritance chain stretching back to Object. Java does not support multiple inheritance among classes.
* The **`java.lang.Object` class defines and implements behavior common to all classes—including the ones that you write**. In the Java platform, many classes derive directly from Object, other classes derive from some of those classes, and so on, forming a single hierarchy of classes.
* The keyword **`extends` indicates one class inheriting from another.**
```java=
public class Bicycle {
public int gear;
public int speed;
public Bicycle(int startSpeed, int startGear) {
gear = startGear;
speed = startSpeed;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
```
```java=
public class MountainBike extends Bicycle {
// the MountainBike subclass adds one field
public int seatHeight;
// the MountainBike subclass has one constructor
public MountainBike(int startHeight, int startSpeed, int startGear) {
super(startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass adds one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
```
* If your method overrides one of its superclass's methods, you can **invoke the overridden method through the use of the keyword `super`**. You can also use super to refer to a hidden field (although hiding fields is discouraged).
```java=
public class Superclass {
public void printMethod() {
System.out.println("Printed in Superclass.");
}
}
```
```java=
public class Subclass extends Superclass {
// overrides printMethod in Superclass
public void printMethod() {
super.printMethod();
System.out.println("Printed in Subclass");
}
public static void main(String[] args) {
Subclass s = new Subclass();
s.printMethod();
}
}
```
->
```
Printed in Superclass.
Printed in Subclass
```
* **Access level modifiers** determine **whether other classes can use a particular field or invoke a particular method**. Given below is a simplified version of Java access modifiers, **assuming you have not yet started placing your classes in different packages** i.e., all classes are placed in the root level.
* There are two levels of access control:
(At the **==class==** level:)
**public**: the class is visible to all other classes
**no modifier**: same as public
(At the **==member==** level:)
**public** : the member is visible to all other classes
**no modifier**: same as public
**protected**: the member is visible to sub classes only
**private**: the member is not visible to other classes (but can be accessed in its own class)
### The **`toString`** method
* **Every class inherits a toString method from the Object class that is used by Java to get a string representation of the object** e.g., for printing. By default, it simply returns the *type of the object and its address* (in hexadecimal).
* You can **override the toString method in your classes to provide a more meaningful string representation of the objects of that class.**

### The **`equals`** method
* The **`==`** operator checks whether objects are **identical**; that is, whether they are the same object.
The **`equals`** method checks whether they are **equivalent**; that is, whether they have the same value.

## OOP + Java: Polymorphism ==(shorter / simpler / more flexible)==
* ==**Polymorphism**== allows you to **write code targeting superclass objects, use that code on subclass objects, and achieve possibly different results based on the actual class of the object.** (為不同資料類型的實體提供統一的介面)
* **Every instance of a subclass is an instance of the superclass, but not vice-versa.** As a result, inheritance allows **substitutability** : the ability to substitute a child class object where a parent class object is expected.
* **Dynamic Binding** (aka late binding) : a mechanism where method calls in code are resolved at **runtime**, rather than at compile time.
* ==**Overridden**== methods are resolved using **dynamic binding**, and therefore resolves to the implementation in the actual type of the object.

* In contrast, ==**overloaded**== methods are resolved using **static binding**.

* Three concepts combine to *achieve polymorphism*:
==**Substitutability**==: Because of substitutability, you can write code that expects object of a parent class and yet use that code with objects of child classes. That is how polymorphism is able to ***treat objects of different types as one type.***
==**Overriding**==: To get polymorphic behavior from an operation, the operation in the superclass needs to be overridden in each of the subclasses. That is how overriding ***allows objects of different subclasses to display different behaviors in response to the same method call.***
==**Dynamic binding**==: Calls to overridden methods are bound to the implementation of the actual object's class dynamically during the runtime. That is how the polymorphic code can ***call the method of the parent class and yet execute the implementation of the child class.***
* The **strong-typing** can lead to unnecessary verbosity caused by repetitive similar code that do similar things with different object types.
* A better way is to take advantage of **polymorphism** to write code that **targets a superclass so that it works with any subclass objects.**
## Java: Constants
* **Java does not directly support constants**. The convention is to use a **`static` `final`** variable where a constant is needed. The **`static`** modifier **causes the variable to be available without instantiating an object.** The **`final`** modifier causes the variable to be **unchangeable**. Java constants are normally declared in ***ALL CAPS separated by underscores***.
```java=
public class Account{
public static final double MAX_BALANCE = 1000000.0;
}
```
## OOP + Java: Enumerations
* **An Enumeration is a fixed set of values that can be considered as a data type.** An enumeration is often useful when using a regular data type such as int or String would allow invalid values to be assigned to a variable.
* You can define an enum type by using the **`enum`** keyword. Because they are constants, the names of an enum type's fields are in **uppercase letters** e.g., FLAG_SUCCESS by convention.

* Note that while enumerations are usually a simple set of fixed values, **Java enumerations can have behaviors too**
## Code Quality: Readability
* **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.
* **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.
* **Avoid Deep Nesting**:
avoid arrowhead style code.

* **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).
* **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 named constant makes the code easier to understand because the name tells us more about the meaning of the number.
* **Make the Code Obvious**
* **Structure Code Logically**:
Choose the order that makes the story most readable.
* **Do Not 'Trip Up' Reader**:
such as:
unused parameters in the method signature
similar things that 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
* **Practice KISSing**:
"keep it simple, stupid” (KISS). Do not try to write ‘clever’ code.
* **Avoid Premature Optimizations**:
1. We may not know which parts are the real performance bottlenecks.
2. Optimizing can complicate the code
3. Hand-optimized code can be harder for the compiler to optimize
* **SLAP Hard**:
**Avoid varying the level of abstraction within a code fragment.** Note: The Productive Programmer (by Neal Ford) calls this the SLAP principle i.e. Single Level of Abstraction Per method.
* **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.
## Code Quality: Refactoring
* **improving a program's internal structure in small steps without modifying its external behavior is called ==refactoring==.**
* Refactoring is **not rewriting**: refactoring needs to be done in small steps.
* Refactoring is **not bug fixing**: By definition, refactoring is different from bug fixing or any other modifications that alter the external behavior (e.g. adding a feature) of the component in concern.
* benefits:
1. **hidden bugs become easier to spot**
2. **improve performance** (sometimes, simpler code runs faster than complex code because simpler code is easier for the compiler to optimize).
* Refactoring Name: ==**Consolidate Duplicate Conditional Fragments**==
Situation: The **same fragment** of code is **in all branches of a conditional** expression.
Method: **Move it outside** of the expression.

* Refactoring Name: ==**Extract Method**==
Situation: You have a code fragment that **can be grouped together**.
Method: Turn the fragment **into a method** whose name explains the purpose of the method.

* Statement: Whenever we refactor code to fix bugs, we need not do regression testing if the bug fix was minor.
```
DISAGREE.
Even a minor change can have major repercussions on the system.
We MUST do regression testing after each change, no matter how minor it is.
Fixing bugs is technically not refactoring.
```
## Automated Testing of Text UIs
* **Testing**: Operating a system or component under specified conditions, observing or recording the results, and making an evaluation of some aspect of the system or component.

* **When testing, we execute a set of test cases.** A test case specifies how to perform a test. At a minimum, it specifies the input to the software under test (SUT) and the expected behavior.
* Test cases can be determined based on the specification, reviewing similar existing systems, or comparing to the past behavior of the SUT.
* For each test case we do the following:
1. Feed the input to the SUT
2. Observe the actual output
3. Compare actual output with the expected output
* A **test case failure** is a **mismatch between the expected behavior and the actual behavior**. **A failure indicates a potential defect** (or a bug), unless the error is in the test case itself.
* When we modify a system, the modification may result in some unintended and undesirable effects on the system. Such an effect is called a **regression**.
* **==Regression testing== is the re-testing of the software to detect regressions.** Note that to detect regressions, we need to retest all related components, even if they had been tested before.
* regression testing is more practical when it is **automated**.
* An automated test case can be run programmatically and the result of the test case (pass or fail) is determined programmatically.
* A simple way to **semi-automate testing of a CLI**(Command Line Interface) app is by **using input/output re-direction**.
1. feed the app with a sequence of test inputs that is stored in a file while redirecting the output to another file.
2. **compare** the actual output file with another file containing the expected output.