Written by Luka Kerr on November 13, 2018
Inheritance
public abstract class Parent {
// Can't create instance of Parent
}
public class Child extends Parent {
// Can create instance of Child
// Child inherits any methods and properties in Parent
}
public interface xyz {
void abc() throws IOException;
}
public interface pqr {
void abc() throws FileNotFoundException;
}
public class Main implements xyz, pqr {
// The last interface takes a higher precedence
public void abc() throws FileNotFoundException;
}
Polymorphism
- The ability of an object to take on many forms
- Any object that can pass more than one IS-A test is considered to be polymorphic
public interface Vegetarian {}
public class Animal {}
// Polymorphic since a Deer IS-A Animal, IS-A Vegetarian, IS-A Deer, IS-A Object
public class Deer extends Animal implements Vegetarian {}
Access Modifiers
Modifier | Same Class | Same Package | Subclass | Everyone |
---|---|---|---|---|
public | X | X | X | X |
protected | X | X | X | |
default | X | X | ||
private | X |
Law Of Demeter
- Also known as The Principle of Least Knowledge
- Rules:
- A method
M
in an objectO
can call on any other method withinO
itself - A method
M
in an objectO
can call on any methods of parameters passed to the methodM
- A method
M
can call a methodN
of another object, if that object is instantiated within the methodM
- Any method
M
in an objectO
can call on any methods of any type of object that is a direct component ofO
- A method
public class O {
private E myE = new E();
public void method M(E paramE) {
// Rule 1
this.W();
// Rule 2
paramE.N();
// Rule 3
E localE = new E();
localE.N();
// Rule 4
this.myE.N();
}
private void method W() {}
}
public class E {
public void method N() {}
}
Liskov Substitution Principle
- Subtypes must be substitutable for their base types
Patterns
Strategy Pattern
- Selects and adapts an algorithm at runtime
- Advantages
- Uses composition over inheritance allowing for better decoupling between the strategy and class
- Disadvantages
- Increases the number of classes in the system
- Client must be aware of different strategies
State Pattern
- Allows an object to change its behaviour when its internal state changes
- State of the object is encapsulated in different state objects
Iterator Pattern
- Allows for traversal of items in a collection without revealing any internal data structures
Template Method Pattern
- Defines the skeleton of an algorithm in a method, deferring some steps to subclasses
- Lets subclasses redefine certain steps of an algorithm, without changing the algorithms structure
Composite Pattern
- Allows for composition of objects into tree structures to represent part-whole hierarchies
Decorator Pattern
- Attaches additional responsibilities to an object dynamically
public interface Component {
void doOperationA();
void doOperationB();
}
public class ConcreteComponent implements Component {
@Override
void doOperationA();
@Override
void doOperationB();
}
public abstract class Decorator implements Component {
private ConcreteComponent cc;
}
public class ConcreteDecoratorX extends Decorator {}
Observer Pattern
- An object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods
public interface Observer {
void update();
}
public class Observable {
private ArrayList<Observer> observers;
void addObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers():
}
public class Display implements Observer {
@Override
void update();
}
Factory Method Pattern
- Allows creation of objects without specifying the exact class of the object that will be created
public interface Product {}
public abstract class Creator {
abstract public Product makeProduct();
}
public class ConcreteCreator extends Creator {
@Override
public Product makeProduct();
}
Builder Pattern
- Enables encapsulation of the construction of a product and allows the object to be constructed in steps
public class User {
private final String name;
private final String phone;
// ...
private User(UserBuilder ub) {
this.name = ub.name;
this.phone = ub.phone;
// ...
}
public static class UserBuilder {
private final String name;
private final String phone;
// ...
public UserBuilder(String name) {
this.name = name;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
public User build() {
return new User(this);
}
}
// In use
User user = new User.UserBuilder("Bob")
.phone("1234")
.build();
Singleton Pattern
- Ensuring a class only has one instance throughout the lifetime of the program
public final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
Command Pattern
- An object is used to encapsulate all information needed to perform an action or trigger an event at a later time
- Has 4 key components: the command object, the receiver, the invoker and a client
public interface Command {
void execute();
}
// Invoker
public class Switch {
private ArrayList<Command> history;
public void storeAndExecute(Command cmd);
}
// Receiver
public class Light {
public void turnOn();
public void turnOff();
}
// Command object
public class FlipUpCommand implements Command {
@Override
public void execute();
}
Visitor Pattern
- Need a pattern where one class/interface (Visitor) defines a computation/operation and another (Visitable) is responsible for providing data access
- Need a way to add new behaviour to a family of classes without modifying the existing classes
Principles
Open Closed Principle
- Classes should be open for extension but closed for modification
Single Responsibility Principle
- A class should only have one reason to change
- High cohesion
Dependency Injection Principle
- High-level modules should not depend on low-level modules. Both should depend on abstractions
- Abstractions should not depend on details. Details should depend on abstractions
Design By Contract
- A class that provides a service and its caller should have a formal contract
- The contract is enforced between the service- provider and caller through pre-conditions, post- conditions and class invariants
/**
* @invariant balance >= 0
*/
public class Account {
/**
* Deposit into an account
* @pre amount > 0
* @post balance = balance + amount
*/
public void deposit(float amount);
}
Test Driven Development
- Write a test that fails
- Implement the code to pass the test
- Refactor and eliminate redundancy, goto 1
Generics
- Compile time checking
public class Bag<T> {
private T t;
public T get();
public void set(T t);
}
Threads
- A thread is a single sequence of execution within a process
- Multi-threading refers to multiple threads of control within a single process
public class Counter implements Runnable {
private int count = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++)
System.out.println(Thread.currentThread().getName() + " : " + counter++);
}
}
// Usage
Runnable c = new Counter();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
Concurrency
- A race condition may occur inside a critical section
- A critical section is a section of code that is executed by multiple threads, and where the sequence of execution of the threads makes a difference in the result of the concurrent execution of the critical section
- Deadlock arises when locking a thread results in a situation where other threads cannot proceed and thus wait forever for other thread(s) to terminate
- Starvation arises when threads have different priorities, and the thread scheduler gives a lock to the highest priority threads such that the lower priority threads never get access to the shared resource
public class Counter implements Runnable {
private int count = 0;
// Using synchronized to lock critical sections
public void add(int value) {
synchronized(this) {
count += value;
}
}
@Override
public void run() {}
}
Code Smells
- Bloaters
- Long method
- Large class
- Long parameter list
- Data clumps
- OO Abusers
- Switch statements
- Change Preventers
- Divergent change
- Shotgun surgery
- Dispensables
- Comments
- Data class
- Lazy class
- Duplicate code
- Couplers
- Feature envy
- Message chains
Refactoring Techniques
- Move field/method
- Extract class/inline class
- Extract method
- Introduce parameter object
- Replace local variable with method
- Replace with polymorphism