Download as rtf, pdf, or txt
Download as rtf, pdf, or txt
You are on page 1of 33

COMP2911 Summary Notes (of concepts)

WEEK 1:
Agile Development:
Working software more essential than documentation.
Weekly development cycles with small groups of people.
Continuous development until development budget runs out.

WEEK 2:
ADTs: type / data abstraction - separation of the data (struct) and
the operations (functions)
OOP: procedural abstraction - both data and functionality is hidden /
associated within the object.
I.e. an object not only has its own instance of its class’s
variables but also of its functions.
Advantages: easier to divide up the program.
Reuse (e.g. strategy patterns) + reliability (e.g. program is
divided up (identify where the
error exists by evaluating where invalid pre-, post-
conditions are), polymorphism)
See the ’swirling’ example - the benefit is no need for
brute force -> polymorphism
Disadvantages: harder to prove correctness of program.

Read Week 5 for more specifics on polymorphism.

Philosophy of autognosis: an object only has knowledge of itself;


must make request to
other objects to access their information.

Liskov Substitution Principle (LSP): philosophy: an object (e.g. in


polymorphism) can be replaced by another object (of any related but
differing class) without any effect on its extrinsic behaviour.
Related: more specifically, of any subtype (subclass)
e.g. in a Rectangle class, setHeight()’s external behaviour
changes the height without affecting
the width. As a result, Square should not be a child class of
Rectangle or it will break LSP.
Also: can inherit redundant fields (height, width), redundant
functions (getHeight()).

I.e. objects are treated completely abstractly - no knowledge is


required of its
implementation, except that extrinsic behaviour is preserved (in
terms of pre-post-conditions,
return types).

Basis for strategy patterns: can completely change the strategy


without changing anything else.

Well designed code can be extended without modification“… “new


features are added by adding new code, rather than by changing old,
already working, code” ~ Open-Closed Principle
Pre-existing code closed off to editing (legacy reasons), but
open to extending.
Can’t change existing behaviour, can only add to it.

When overriding functions in a child class: (polymorphism):


Needs TO CONTAIN the base class input and TO BE CONTAINED
by the base
class output.
Preconditions must be the same or weaker
Postconditions must be the same or stronger

JAVA CONCEPTS: Introduction to Java, and essential functions:


equals(), toString(), clone().
equals():
Remember: ‘==‘ looks at reference in memory, not
equivalence of info stored.
If you use hashing, essential to also override the
hashCode() function.
If you use hashing, ensure you only compare equality based
on fields that are immutable
(i.e. “static”ally defined on initialisation - UPDATE: um,
more like, guaranteed by the
invariant to not change (or final operator?)?).
Ensure you also check they are the same class type.
E.g. use instanceof or .getClass() or for full control,
write your own function:
canEquals(), which returns (obj instanceof SomeClass).
BASIC IMPLEMENTATION:
Compare special cases: null / identical object / same level
of inheritance or class type, and
then basically compare fields like a struct (need to
typecast the generalised Object first). If
you have child classes, make use of the instanceof trick.
Should you treat the child of a class as equal to its parent?
Answer: depends on what the child and the parent
represent. Always ask, does it make
sense for the given data?
clone(): just do copy constructors.
Basically, call the super’s clone instead. Child classes will
call the super.clone() of their
parent, and any fields that aren’t covered by this ~ call the
super.clone() of the Java API.
clone() is associated with a
CloneNotSupportedException, need to throws or try-catch
Then typecast the super’s clone into the current class type.
Add in the remaining new
variables.
Shallow vs deep copy:
Shallow: copies all the pointers / references for your
fields (cheap, but referenced).
Except for strings: all strings are immutable.
Deep: makes a new copy for all your fields (not cheap,
but data isolation).
toString():
Pretty basic - basically the return value is a string of
whatever you want.

JAVA APIs: java.lang.String, java.util.Calendar,


java.util.GregorianCalendar

WEEK 3:
Defensive Programming: blind checking. Redundant = more
complicated code + inefficient.
Design by Contract:
CONTRACT: If the preconditions are true, then the
postconditions are guaranteed true.
Methods: preconditions (input), postconditions (output)
Classes: invariants (conditions to fields that must always
be true / preserved)
I.e. don’t need to test preconditions within the function - note it
in the documentation instead,
and assume the user / caller will take care of it.
Contract document: client / supplier’s obligations and benefits.
Stronger precondition = more burden on client; and vice-
versa with post-condition.

Inheritance: new class built upon pre-existing ones [parent -> child
(+freedom to override)]
Redeclaration: override functionality but keep external behaviour
constant - LSP.
Polymorphism: type adaption caused by inheritance. (see Week 5)
Dynamic binding: naturally tied to polymorphism: function calls
depend on the dynamic context of what type of class (parent or
child class) that function belongs to (could differ due to overriding).

Covariant: if s is a subtype of t, post(s) is a subtype of post(t).


[ pre cur -> post ]
Contra-variant: if s is a subtype of t, pre(t) is a subtype of pre(s). [
pre <- cur post ]
Bi-variant: both apply. (s is both pre and post?).
Methods are bivariant: weaker preconditions (contra-) and
stronger postconditions (co-).
Hence parameters (contra-variant) and return types
(covariant).
Invariant: neither apply.
Don’t get this confused with:
Class invariant: a condition that applies all the time (same rules
as with postconditions:
same or stronger). Condition only need not apply for constructor
and destructor, should always
apply for the class instance.

Debugging tests simulate real life operation, hence your tests need
to obey the preconditions as well.

JAVA CONCEPTS:
Javadoc: automatically HTML formatted documentation
embedded within your code.
Try-catch blocks: for handling unexpected inputs and logic
errors. Don’t use them to catch
stack overflows or out-of-memory issues.
API spec bugs (incorrect commenting) vs code bugs (actual
implementation issue).
Asserts in Java: don’t use them for actual processing - use them
only for debugging.
JUnit: use of asserts in Eclipse for debugging purposes.
Useful for test driven development - but TTD can’t test
everything.
Don’t put function calls and assignments in an assert
statement.
@Override, @Decrepit, etc. are metadata tags read at compile
time ~ checks for correct
implementation of the methods you have tagged.

WEEK 4:
Law of Demeter: a class can only look one parent class up or one
child class down at a time - chaining together a series of class fields
reveals internal implementation.
Advantage: “hidden internal implementation = maintainable +
adaptable”.
Disadvantage: multiple function headers per class = space, time
inefficiencies.
E.g. dog.legs.walk() breaks LoD; instead call dog.walk() which
contains legs.walk() inside.
I.e. always use one dot, even if it means more function headers
as a result.

All it’s meant to say is you shouldn’t have direct access to the
internal implementation,
EVER. So basically, don’t ever make your aggregated classes
public?
(Possibly OK for primitives and “structs” - these only reveal
some statistic data, they
don’t reveal your procedural abstraction).

DOCUMENTATION CONCEPTS: refer to website :P


Use case: sequence of steps of how the user interacts with the
system in a specific context
I.e. plan all possible combinations of user actions (as if a
chess game)

CRC cards: class name, responsibilities, collaborators (i.e. how


they interact with other
classes hierarchically). Then, rearrange the cards on a table to
identify this hierarchy (top to
bottom), relationships, etc. Pile of cards = incremental
refinement of that class.
Responsibilities: i.e. fields and functions - what information
am I in charge of? More
importantly, what problems do I need to solve?
Collaborators: communications with other classes.
I.e. a vague UML, done on cards because: fast + not that much
notation + throwaway.

Walkthrough: go through the use case and relate each CRC card
(i.e. each class) to each step.
I.e. a vague sequence diagram.

UML:
Class diagrams:
Name, fields (+,-,#,underlined), methods (incl. <<‘’>>
construct. interface).
Relationships:
Dependence (- ->), association (—>), aggregation (<>—),
composition (<#>—).
Multiplicity (1, * (0+), 1..* (range)): i.e. how many relate
to how many.
Generalisation (parent <|— child), realisation (interface
<|- - class).
More info below:
http://creately.com/blog/diagrams/class-diagram-
relationships/ - easier to just read this.

Dependence: one thing depends upon another ~ e.g. maybe


for component objects
of a composed object? I.e. component object’s existence
dependent on composed object’s
existence.
Association: one thing calls another ~ generally for
methods, can be for classes
Aggregation: one thing is made up of several component
things
E.g. a Doctor object, with a field: an array of Patients.
Patients are objects that
exist externally, and the Doctor class takes a reference
of them.

IN JAVA: the component objects are referenced


multiple times in code, the
aggregated object simply makes up one of those
references.
Composition:
E.g. a Semester object composed of various Lesson
objects (times for different
lectures, labs). There is only one reference to that
Lesson object - it’s within the
Semester object = it doesn’t exist “externally” to the
Semester object (in its
functional, how-a-programmer-would-use-it sense).

IN JAVA: the distinction does not exist syntactically!!


Exists as part of
engineering design - designed in such a way that the
ONLY reference of that object
is within that composed class. When the composed
class is deleted, the only
reference is also deleted. Due to how Java’s garbage
collection works, any
floating memory (i.e. the component object know with
no references) is automatically
deleted.

Sequence diagrams:
`[frames (alt, opt, loop), arrows (left, right, down, (asynchron,
synchron)), guards].
Frame elements (w. top left name box):
Can chain frame elements for a block diagram =
abstraction.
If-else ‘frame elements’, loop ‘frame elements’.
Also frame elements for: parallel processing
(associated with hardware).

If-else (“alt”, “opt”) and “loop” exist as their own blocks


/ frames within your current
frame”: might help to read below before reading
frames.
Before any method calls are made, a guard is first
specified to illustrate the
conditional expression required to be fulfilled.
Blocks marked “alt” show all alternate cases you
can take; each case is
sectioned off by a horizontal dotted line (still
within that “alt” block).
Blocks marked “opt” are optional (basically like
“alt”, but only one case exists).
Blocks marked “loop”: pretty obvious, the block
denotes one loop cycle.

Sectioned columns (a dotted line) for lifelines (instances of


objects).
Arrows denoting:
Left-to-right for method calls (input and output) =
entering / exiting lifelines.
Method calls (with required parameters inserted),
return values
Arrow looping back to itself: made a function call but
didn’t need to enter a new class
in order to do so.
Top-to-bottom for logical processing (method calls, if-
else, loops).

Synchronous message: —|#> // closed arrowhead


and coloured in
Asynchronous message: —> // open arrowhead
[Synchronous: occurring the same time;
Asynchronous: not at the same time]

(In-line) Guards: i.e. condition checks (to meet pre-


conditions)
On top of your arrow (message), write as: [Boolean
expression here] functionCall()

Lifeline notation: variableInstance : ClassName


Arrow notation: functionName ( actualVariablePassedIn )
NOTE: if return type is void, not necessary to draw the
output arrow.
Only care about what function calls are made and their return
type.
Leftmost input and output to something external: the external
thing is what your entire frame
element is enclosing (title indicates what your sequence
diagram is representing).

JAVA CONCEPTS: Eclipse debugging (breakpoints, watchpoints,


altering of variable contents).
JAVA APIs:
ArrayList<ElementType> :
Partially dynamic ~ i.e. statically malloc’d, will realloc
automatically if out of space = slow
Indexed elements - definitely use arrays for searching.
LinkedList<E> :
Completely dynamic = use linked lists for adding, removing
elements from a list.
Iterator<E> :
Specifically for bidirectional movement (left, right): i.e.
bidirectional linked list.

WEEK 5:
is-a relation: a child class is not only itself, but also its parent
(inheritance), grandparent classes, etc.
Hierarchy tree: top down relation, can contain both classes and
interfaces.
Every Java type is a subtype of Object.

Polymorphism: extendible, generalisable code, reduced code


repetition (if-else statements for different object types (handled
automatically by polymorphism).
Type abstraction: can treat the child class as if it were an
object of the base class.
Type abstraction = ignore the typing of related objects
(related by inheritance).
A variable with a base type can hold references to objects
of its derived type.

JVM (Java Virtual Machine) determines what the class type


the variable holds at runtime;
its type is unknown when you are writing the code.

[VERIFIED]: polymorphism = encapsulation = abstraction.


(explained in below EXAMPLE)
Parent class display() prints “Parent”. Child class overriden
display() prints “Child”.
Parent c = new Child(); c.display() // displays “Child”
Same is true for differing implementations of an
interface.
However, if the display() function is made static in Parent,
then c.display(): // “Parent”.
I.e. variable is able to contain Child classes without
converting them into
Parents. HENCE, a Child class can be stored in a Parent
variable yet still retain all
the Child functionality of any overrided methods also
existing in Parent.
See static binding (below).
This is why polymorphism = breaking up the flow of the logic.
Some of the decision making is
made in the polymorphism itself on runtime, and this hasn’t
been directly specified by the
programmer. See EXAMPLE below - more of the same.
Instance method: non-static methods. Class method: static
methods.

EXAMPLE: (more info on classes vs interfaces below this section)


Class objects as parameters into functions: the processing can
differ each time, even though
the function’s code is not any different, due to method
overriding within the child classes.
E.g. main() has the call obj.wash(); can define obj to be a
cat, cup, car, clothing, etc.
With polymorphism, simply need to change what the obj is,
don’t need to change the
function call as well to be a different name (like in ADTs).

Useful if passing in various classes that share the same


interface:
Avoids brute force approach ( if-else statements,
checking type using instanceof ).
If it can be done using polymorphism, use it. Do not
abuse the use of instanceof.

HOWEVER: if you want to call an extended method of a


class that other classes do not
share, you must use instanceof then downcast (i.e.
typecast) the variable to the
correct reference type. ~ i.e. polymorphism doesn’t help for
classes w. extra functionality.
Downcast: typecast down the inheritance hierarchy.
ClassCastException occurs if you downcast
incorrectly down the hierarchy.

Allows for greater abstraction in programming: can


generalise and ask the object to
perform a method without needing to specify the correct
method corresponding to
that object (unlike in an ADT).

Polymorphism + ‘private’ modifier = delegates complete


responsibility to the object.
‘Private’: variable completely hidden by public view -
responsibility is completely isolated.

Dynamic binding: invoked at runtime


Instance method call dependent on what class type the variable
is currently holding.
(e.g. the example above) - i.e. polymorphism.

Static binding: method call dependent on reference type of variable.


Determined in compile-time.
I.e. variable type determines what the method is, NOT the class type
of the object held in that variable
JAVA: everything is dynamically bounded except:
Private methods. (no issues here)
Independent the actual class object at runtime.
Instance method invocations with super keyword. (no
issues here)
I.e. child class uses super to call ‘hidden’ private
methods in the parent class.
Invocation of instance initialisation methods. (no issues
here)
I.e. constructors (only called once per object).
Class methods defined with static. (issues - as mentioned
in above example).
Methods defined with static can only be called under a
variable with the same
reference type (i.e. typecast to the correct type).
E.g. parent and child have statically defined method, same
name. Variable holding
child type instance, but of parent reference type.
child.staticMethod() calls the parent
method, not the overridden child method.
To call the child class’s static method without changing the
variable type:
Do: ClassName.methodName() instead of
variableName.methodName().

Classes vs Interfaces (for Polymorphism): [JAVA]


Interfaces: a restricted form of multiple inheritance ~ it has
more restrictions than in
class extension + class objects can inherit multiple interfaces
at once (a class
can only inherit one superclass).
Problem: use of extends on classes without adding
additional methods, just to implement
polymorphism. E.g. WashableObj not really an appropriate
base class for cat / car / clothes
Solution: use of interfaces + benefit of multiple inheritance.

Interface variables and method headers are declared public and


abstract by default.
I.e. treat an interface as an abstract base class.
Only interfaces can extend other interfaces ~ parent interface,
child interface.
Two interfaces with the same function name: function
overloading ok; same function name,
same input parameters but different output type = impossible to
implement = compiler errors.

USAGE:
Variable’s type can be a reference to an interface.
Any class that can implements that interface can be stored
in that variable.
Note no constructor exists for an interface - call the
class object’s constructor.
Can use instanceof InterfaceName to check if an object
implements that interface.

Generics: [JAVA]
Use generics if you have some field / function (parameter or
return value) that needs to be
generalised. All the code writing is the same except your type is
now a variable name as well.
Generics are statically typed, hence they behave differently to
sub typing in arrays.
I.e. compiler automates typecasting for you instead of the
programmer doing it manually.
E.g. List<E> type for lists, element type E. Get an item from list,
auto-typecasts to E for you.
Type variable: unqualified identifier, of which can be a generic
class / interface / method /
constructor declaration.

USAGE: [definition]
Generic identifier: define a class / interface, then put angle
brackets afterwards, and a
generic type (represented as a variable) afterwards: e.g.
ClassName<T>.
Method declarations identical: public T getFirst() etc.

ALSO: foreach is also a generic version of for loops: e.g.


for (ClassType varName : varList) { … }

Generic definition T, usage may define T as String ~ then type


safety occurs.
Polymorphism with subtypes: e.g. apples = List<Apple> not
equal to fruit = List<Fruit>. Makes
sense to do this: can add Strawberry as a Fruit, can’t add it as
an Apple.
I.e. covariance not preserved (it’s usually preserved for
most other things).
Wildcards: ? extends ClassName or ? super ClassName
narrows / widens a reference
(i.e. covariance / contra variance relation used to generalise
acceptable types).
e.g. List<? extends Fruit> fruit = apples; // for retrieving
objects from a data structure
i.e. ArrayList can get its child items using extends.
I.e. any Fruit or anything that extends Fruit.
e.g. List<? super Apple> apples = fruit; // for putting objects into
a data structure
i.e. ArrayList can add new items of itself and its children
into it using super.
I.e. any Apple or anything WHERE the super() of that class
IS Apple.
I.e. can add Apple or any class where super() refers to
Apple as the base class.
NOTE: inconsistency with arrays: Fruits[] equal to Apples[]; can
add Strawberry to Fruits[].
I.e. obeys variable type but no type safety to actual class
type.
At runtime, throws ArrayStoreException error if Apple and
Strawberry conflict in Apples[].
Solution: use the generic class ArrayList instead of using
primitive arrays.
Generics perform type checks at compile-time, no runtime
errors then.

BASICALLY: polymorphism regarding generics - an issue of


TYPECASTING.
EXTENDS: typecasts your list up the hierarchy, but hence
can only be read-only.
Why read-only? Because you need to preserve it as
both a List of Fruit and as a List
of Apple. Clearly the more restrictive argument is “List
of Apple”.
Specifying contravariance only works if read-only.
(Think of “extends” like an iterator to a list). You
lose functionality, but this is
desired because this simplifies what you have to
work with.
IMPLEMENTS: typecasts your list down the hierarchy, adds
further restrictions.
I.e. specifies covariance (generics are not covariant by
default, need to specify this by
the ‘super’ keyword).

What’s the point of this though? It’s like arguing: you can’t
go up, you can only go down
(which is how polymorphism works to begin with, so that’s
a GOOD thing).
If you want to go up and add, just do: List<Fruits> =
new List<Fruits>(); instead??

Example:
ConcreteList used can be any Fruit definition.
List<? extends Fruit> fruits = apples; // fruits = new
List<Apple>();
// variable type does
automatic typecasting, read-only
// fruits.add(new Strawberry()); // won’t compile
// fruits.add(new Fruit()); // won’t compile because
can’t input using extends
// why? can’t guarantee type safety since unknown
child type (on conversion of
// the List to a different generic type?)
// extends is too loose a definition, super is much
more restrictive.
ConcreteList used has to be same as base class expressed
within “< >”.
List<? super Apple> apples = fruits; // apples = new
List<Fruit>();
// variable type typecasts all
Fruit as Apple
// (and hence, or as child of
Apple)
fruits.add(new Apple()); fruits.add(new GreenApple()); //
valid
// fruits.add(new Fruit()); // invalid - can only add
objects with Apple as super,
// (including Apple itself), not Fruit,
etc. because of type safety

[JAVA]: Other Wildcard usages: [not that conceptually important]


Wildcards can also be applied to parameters for methods -
syntactically identical.
Again: extends to get values + assign a different reference;
super to store values.
Wildcards can also be applied on variables types themselves ->
bounded ‘type variables’.
In this instance, can only use extends keyword.
e.g. <T extends Juicy<? super T>> List<Juice<? super T>>
squeeze(List<? extends T> fruits);
^ output restriction; ^ input restriction (put-only) ^
^ read only input
^ How do you be a child class of the ArrayList Juicy< >?
To relax the bounds a bit, use recursive bounds.
e.g. <C extends D<T>> // recursive: bound type T
satisfies depends on type T
Generics can consider multiple variables using wildcards: e.g.
<T extends A & B>.
Erasure:
Does a type-check to see if your generics are compilable, then:
Information of that class BEING generic is erased on compile
time to bytecode.
I.e. the generic type is substituted out for the actual used
type?

No difference in runtime performance.


Consequences: construction issues that I don’t understand…
E.g. hacks?? Like, the most generalised you can go is
‘Object’, as opposed to going ‘?’.
But look at the lab?? Set<?> is valid notation o.o, so …?

Lab:
Generics equals() function: do by calling .equals() of your
parameterised type E (which must
be an object (can’t be primitive), so it must have an equals()
method), or special functionality like
the generalised .contains( object of type E ) for ArrayList.

WEEK 6:
BFS:
Queue of nodes / states
While (queue not empty)
Pop node off queue, = current node
If goal node reached, break (could be placed elsewhere,
depends on required task)

Add current node to visited list (or hash table)


(If constructing a path, add current node to your path list)
Get neighbours (ignore if already visited), add to queue
DFS: replace a queue for a stack

Design patterns: (see bottom of document for clarifications on


design patterns???)
[hiding implementation, abstraction; generalised, recursive
solutions]
Standardised approach to solving some problem
More abstraction: isolate the strategy as its own object, can
just swap out the object for a
different one if required (think LSP).
Iterator pattern: some common Iterator interface
Want to iterate through a COLLECTION, but don’t want to
reveal implementation of
the collection. Hence, iterate through an “Iterator” type.
“Iterator” better than “List” because of restricted
functionality. Think about this: if
the supplied iterator is the key values off a hash map,
‘adding’ to the list wouldn’t
make sense.
http://www.oodesign.com/iterator-pattern.html
Some interface ‘Iterator’ and some implementation
‘ConcreteIterator’ (strategy
pattern). You can choose to call the strategy pattern
wherever you want - it’s just
that fewer concrete specifications = easier to adapt for
entire system.

Strategy pattern: some common Strategy pattern interface


For classes that differ only in behaviour, isolate some
logical processing into its
own class. Call on the class to get the strategy (the logic).
Because of LSP, can easily substitute out the logic for
another object.
http://www.oodesign.com/strategy-pattern.html
Class is associated with some concrete strategy
implementation
(either stored or passed in as a parameter, whatever is
most convenient).

Anti-pattern: things not to do in your UML.

WEEK 7:
Dijkstra: like a BFS, but considers weighting, and uses a PQ (priority
queue).
Priority queue: based off weighting, not based off time
spent in queue.

While PQ not empty:


Pop current state off PQ (State implements Comparable
(interface))
If current node at goal node, break [PQ pops off next
node of lowest g cost]
Add current node to visited list (to prevent backtracking)
Get neighbours
(If already visited, do edge relaxation if current g +
path_length < prev g cost)
Needs to update pred[] to new predecessor node
(current node)
(‘Technically’ need to remove old paths
extending from this node)
If not visited or edge relaxation occurred, insert in PQ
(Can’t break here if at goal node: might not be the
cheapest solution)

Edge relaxation required for 1927 path implementation: remember,


PQ determines
highest priority by lowest g cost, NOT lowest cost to next node (that
would be a min
span tree).
(Edge relaxation not needed for 2911 assignment 2 because of
triangle inequality rule).
https://en.wikipedia.org/wiki/Dijkstra
%27s_algorithm#Pseudocode

Don’t need edge relaxation if isolating storage of the path inside


the state (inefficient).
Why? Because there’s nothing to “correct”: info for each path is
isolated.

Differences to BFS: PQ, g cost weighting (from source node), edge


relaxation.

A* search: Djkstra + heuristic calculation.



If current node at goal node, break. (f = g + h = g + 0) (same
break condition as with Dijkstra)

Get neighbours
(Can ignore edge relaxation if you isolate storage of the
path inside the state)
Calculate new f cost: f = g + h. (if h = 0 always, then it is
Dijkstra)
Insert in PQ (in order of f value)

Uses a heuristic to improve comparison of which neighbour in PQ


best to go to next.
Larger heuristic values = better efficiency.
Ensure heuristic is admissible (i.e. doesn’t over-estimate),
otherwise: can’t guarantee an
optimal solution.

Coming up with heuristic ideas:


Relax the rules of your game = game is made easier (i.e.
underestimates).
Easier game = easier to determine next best move => an
estimate.

WEEK 8: (Agile)
Performance reviews: “rank and yank”
Makes things too aggressive, competitive (survival of fittest
mentality)
Discourages teamwork, encourages greed.
Arguably biased, subjective, unfair, easily exploitable
Can “game” the system if performance measured by some
metric

Agile / Scrum:
Eliminate waste: (muda)
Don’t over-engineer: make a solution more general only if it
doesn’t increase complexity.
Don’t over-produce: e.g. oversupply in goods, unnecessary
software content.
Don’t do excessive work: e.g. unnecessary transportation
Stop overburdening and unevenness: (muri, mura)
E.g. time management
Kanban system: a ‘pull system’.
Do things one at a time, only as you need to. Don’t do
anything in advance.
Software: just-in-time development. No Gantt charts. Highly
flexible to spec changes.
Basically: supply and demand, and responsiveness to
it.
Kaizan: continuous improvement.

Manifesto:
Individuals and interactions over processes and tools: (e.g.
scrum, meetings, sprints)
Working software over documentation: (e.g. Javadoc, + supports
TTD)
Customer collaboration over contract negotiation: (e.g. user
stories, product owner)
Responding to change over following a plan: (e.g. flexible to
spec changes)
MANIFESTO SUMMARY:
Meet customer needs no matter what.
Measure progress by working software (hence always have
working software!).
Maximise efficiency - maximise teamwork.

Scrum process:
[Step 1]: User Stories: user specifies what they want to be able
to do (both functional and
technical).
Stakeholders write user stories, not developers.
Use the simplest tools: an index card suffices.
[Step 2]: Planning Poker:
Rate difficulty and priority of each user story task (done by
a group of developers).
Use of relative estimation: e.g. small, medium, large. No
need for absolute estimates.
[Step 3]: Burndown Chart: scheduling and estimating (time and
priority)
Estimate how long the task will take, then keep track of it.
Measured in ideal days / ideal hours (work without
interruptions).
Prefer problems to turn up early rather than late.
Do not integrate late: agile = integrate as early as possible.
[Step 4]: User Acceptance Tests:
Does your developed product meet all the user stories’
requests?
Minimum Viable Product: what the user will accept

Model in greater detail what is immediately needed on your priority


queue. Low priority items might
even just be thrown away at any time.
Work items may be reprioritised at any time.

Scrum teams: self-organised teams


Product owner: represents the user, manages user acceptance
tests.
Scrum master: not a project manager, doesn’t allocate work. Is
a coordinator, coordinates the
team (to maintain teamwork).

Product backlog: (15min max per meeting)


Team allocates what each member wants to do (based on their
skills). (Job title unimportant).
Regular meet-ups (physical presence important: most effective
for communicating info).
“What did I do yesterday? Today? Any issues?”
Sprint (sprint review): limited amount of time to get to some MVP
goal.
Colleagues evaluate your current product.
“Did you meet your goals / deadlines? Can you improve the
process?”
I.e. encourages team reflection to improve performance.

“Scope, time, cost (and implicitly: quality)”: assume one of these is


prioritised (i.e. fixed).
If going over allocated resources, one of the other three factors will
suffer.

Refactoring:
Change the internal structure of code without changing its
external behaviour
“Refactor your code to use the pattern once you realise it’s
needed”
Refactoring databases (a bit harder): need to migrate all the
data.
Restructuring code: to keep it clean.

WEEK 9:
User-centred design: optimise product to how user wants to use the
product rather than forcing users to adapt to the product.
1. Persona: get different audiences to analyse your system
2. Scenario: get different situations for when your app is being
used
3. Use case: precise modelling of steps 1 and 2

Good user interface design:


Responsive feedback = awareness of system status
Match the user’s intuition + be consistent with platform
conventions
Be user friendly: limit what the user has to remember
Give the user freedom, control + customisation (e.g. view,
scripts for efficient use)
Minimalistic design
Reliability (prevent errors) + help fix any errors that occur +
documentation (if necessary)

GUI design: (Java Swing)


http://zetcode.com/tutorials/javaswingtutorial/
Break everything up into layouts (start generally (frame), then
be more specific (panels, then
panels within panels, then buttons, etc.).
Slap layouts everywhere and it’ll position them (and scale
them) automatically for you.
= Very powerful.
Don’t need to call any rendering functionality: passive rendering
- handles it for you.
All you need to do is specify what the layout should look like.

Painting: for drawing stuff on top.


Need to specify coordinate values in this case (maybe it’s
relative to your layout?? E.g.
Panel in centre of screen, so 0,0 starts at this centre?) .
Still remains as passive rendering:
paintComponent(Graphics g) is where you put all your
rendering functions in, however paintComponent() does
passive rendering.

Panel swapping:
http://stackoverflow.com/questions/1097366/java-swing-revalidate-
vs-repaint
Remove ‘master’ panel from frame, attach required panel on
(store all your panels in a list
somewhere). Call functions to suggest to Swing to repaint
the screen.

Event handlers
Timers (don’t really like using Timers for creating a game loop:
events can pile up).

Observer pattern: basically, waits until it is told that some event has
occurred, then passes it on to
all the dependencies that need it. Hence: Observable, aggregating
Observer.
(Association only rather than aggregation, according to
website).

PROBLEM: multiple objects all depend on this one event


occurring; don’t want to make
associations between all the different objects dependent on
this one event.

Observable provides ‘global’ access (or at least, ‘breaks’


type encapsulation??).
Without Observable, when something changes, your class
would need access every
dependent object needing to be changed (would rather
delegate this work externally).

SOLUTION:
‘Strategy pattern’ out the event-watching task: Observable
becomes the master control.
When Observable is told there’s been a change, it passes the
message on to all its
dependencies. Observable ‘unifies’ the entire system to change
at once.

http://www.oodesign.com/observer-pattern.html
Observable: interface / abstract class
Stores a list of objects to observe (Observer). Hence:
attach() and detach().
ConcreteObservable: a full implementation of Observable
Additional field storing a state, with getter, setter for the
state.
Externally: something “sets” the state to a new value, then
calls “notify()” to get all
objects attached to this observer to update themselves.
“Something updates ConcreteObservable”: done by the
main framework. The
main framework also initialises the object.
Controls a bunch of Listeners.

Observer: interface (e.g. a Listener interface)


Your desired class implements Observer (Listener), hence
needs to implement
missing functionality for update() (how you respond to the
new state is up to you).
(This is the same as onKeyPress(), etc.)

E.g. the pattern generalises a keypress. It doesn’t matter what


keypress it is, they all have the
same structural behaviour: hence, can reuse this generalised
pattern over and over again.

SUMMARY: (this is correct - see the news publisher example)


1. Main framework initialises the Listener.
2. Attach whatever objects you want to Listener.
(These objects are now dependent on the event that
Listener is monitoring).
3. Main framework updates Listener when some event has
occurred.
4. Then the Listener tells all its dependencies to update()
for this event having occurred
(event occurred = to new state).
OBSERVER LOGIC:
List of objects to update
When state changes, tell each object to update
Observer: a polymorphism argument: don’t care what
they are, so long as they are updatable.

Classical examples:
Model View Controller Pattern: sounds like some standard
implementation for a program.
E.g. GUI: controller (input, update the state), model
(processes logic),
view (GUI output)
Event handling: used extensively here. I’m guessing that
Observer is controlled by the
master system that controls event handling.

WEEK 10:
Decorator, Composite Pattern: read bottom of text file
Anti-patterns: i.e. bad software practises (not necessarily just bad
software design implementations)

WEEK 11: (Multithreading, Concurrency)


https://howtoprogramwithjava.com/java-multithreading/
Thread = an Object representing a processor. Allows your program
to process stuff.
1 CPU = 1 Thread (unless hyper-threading, then 1 CPU = 2
Threads).

How to use Threads:


Each thread your program is using has its own ID:
Thread.currentThread().getID()
If the code is linear, one thread suffices.
To make it non-linear:
Introduce multiple classes (e.g. Worker) that implements
Runnable:
An instance of this class starts running automatically
when the thread it is in
starts processing (called by varThreadName.start();)
E.g. main(): started running when the initial Thread
for your program started
processing.
Can call to initialise a new Thread in the class
implementing Runnable,
in its constructor.
Introduce new Threads that take your Runnable class; tell
them to start running:
SomeClass c = new SomeClass(); Thread newT = new
Thread(c);
Thread.start(); // c is Runnable, it calls run().

Synchronising the main thread with all the Worker threads:


// in main:
while (any worker still running (worker.running)) // would
actually require two loops
Thread.sleep(100); // keep sleeping until all workers stop
running.
Thread.sleep() simulates ‘doing’ work.
BASICALLY: main() waits for a bunch of Workers (working in
parallel to one another) to
complete their work.

Pitfalls of Java Multithreading: SYNCHRONISATION issues.


Read-write issue when two Threads access and modify the
same Object in memory at the
same time. Need to synchronise all parallel processes so that
this doesn’t happen.
Solution: can read in parallel, can’t write in parallel (use
Locks).
Another problem: race conditions: if output depends on a
specific order of events, then
if the Threads are racing one another, the sequence of events
may differ = output differs
randomly.
[End of article]
How multithreading actually works:
Can think of these issues as different tasks in parallel.
In reality, it’s one-task-at-a-time, and you quickly alternate
between each of the different Threads
(maybe pausing a Thread mid-process in order to interweave to
the next): illusion of parallelism.
But, end result is the same - same issues, same theory as to
why these issues exist.

Solution: use Reentrant Locks. (Lab 12)


Usage of a Lock is kind of like an Observer pattern / like a
‘gatekeeper’.
1. Method acquires the lock before executing. myLock.lock()
Only one thread at a time can acquire the lock (done
using .lock()).
Put a lock field for each class, therefore each instance of
the class can only be accessed
by one thread at a time.
2. Method checks if it can execute. while(if can’t execute)
myCondition.await()
.await() causes the Thread to wait (pauses the loop) until
some other Thread calls
.signal() or .signalAll().
Then you re-loop, check to see if you meet the condition:
If you meet the condition, do the operation.
If you don’t meet the condition, some other Thread is
already doing the operation?
Hence need to await() for next signalAll() that
occurs (when the other Thread
has completed the operation).
3. Method releases the lock when finished. myLock.unlock()

Lock myLock = new ReentrantLock();


// number of locks you need = number of data structures you
have. [?]
// No need for lock protection if doing a read-only function.
// Need lock protection for ALL writing functions.
Condition myCondition = myLock.newCondition(); // generates a new
state (like an enum)
Condition someOtherCondition = myLock.newCondition();
… // within some function // …
myLock.lock(); // get the lock required for writing to that data
structure
// if lock not currently available, Thread waits until
it is available.
try {
while (some condition) myCondition.await();
// await(): can’t do required condition yet. ‘Atomically’
release the lock here and wait until
// myCondition.signal(thisMethod) or myCondition.signalAll()
is called.
// while…await() is an infinite loop. You can also allow for
timeout after certain amount
// of time. If timeout, then throws an
InterruptedException.
//
// await() must keep waiting even after signalAll() until the
current thread reacquires the
// lock associated to your Condition (NOTE not necessarily
to what lock you decided to
// call .lock() on at the beginning of the method (dev
responsible for consistency)).
/* do stuff to the resource (e.g. the class the lock is a field of)
protected by the lock */
someOtherCondition.signalAll(); // or myCondition.signal(), etc.
} finally {
myLock.unlock();
}
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/L
ock.html
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/C
ondition.html

Design Pattern Summary:


Strategy, Composite: 1 interface only (strategy) / (component).
Decorator: 1 interface (base) + 1 abstract class (decorator, because
it stores a variable).
In Decorator, the DecoratorInterface is made into an abstract
class instead since the
addComponent() method is always the same, and we need to
keep track of a variable.
Hence use abstract class instead of interface.
Iterator, Observer: 2 interfaces: (Collection, Iterator) / (Observable,
Observer).
For Iterator: note that Collection, in general, can be ANY class
that contains a Collection as its
field.

Interface = basic, common, shared functionality.


Some concrete class implementation = makes this functionality
more specific (e.g. think about the
various cases for that interface - how can it be implemented
different (strategy-wise) or how can
it represent different information differently? (storage-type-wise).
Rewrite the interface functions in your concrete class blocks.
Then add whatever extra functions occur on top. Think logically
about what it will need.

Strategy pattern: [SEPARATE LOGIC = EASY REFACTORING]


Extrinsic behaviour the same, only internal implementation
differs, so better to detach
that logic segment from the object such that different logic can
easily be swapped in and
out as required.
1. someStrategy implements StrategyInterface
2. someClass makes use of someStrategy
Polymorphism argument: needs to only know what
functionality exists in order to use it
(i.e. through the interface), doesn’t need to know the type
of your strategy.
3. someStrategy can easily be swapped out for
someOtherStrategy: just pass in a different obj.

someStrategy implements StrategyInterface.


someStrategy (implementation) is associated to wherever it is
first initialised (e.g. main()).
StrategyInterface is associated to both main() and someClass.
(polymorphism association)
[main() is associated to someClass] (naturally).
Aggregate StrategyInterface if you re-use the method in
different locations to where you
first set the strategy. Otherwise, don’t: just pass it in as a
parameter.

Iterator pattern: [HIDE IMPLEMENTATION]


Want to iterate through a collection (but keep implementation
hidden), so create an iterator
(with restricted functionality).
Iterator is an interface with specified restricted
functionality.
The collection implements this Iterator interface.
1. someCollection implements IteratorInterface
2. Now, want to iterate through someCollector without knowing
its type.
E.g. foreach loop: calls someCollection.getIterator()

someCollectionIteratorGenerator implements IteratorInterface.


someCollectionIteratorGenerator is associated to its respective
someCollection.
[someCollection implements CollectionInterface].
E.g. from website: Aggregate is List, then
ConcreteAggregate is LinkedList; has its own
LinkedListIteratorImplementation.

Iterable (Java interface): generalisation - I have some class (not


necessarily a Collection).
I know it contains some Collection of items, but I have no
access to the Collection.
How do I get a Collection of items (formatted into an Iterator)
from this class? mClass.iterable()
Basically ends up being a wrapper function.

CODE IMPLEMENTATION: either:


A) Shallow clone the contents into a separate list (hidden
implementation for your iterator).

B) This version doesn’t store the contents of your collection to


iterate over.
It’s defined within (i.e. nested) within your Collection class
itself, hence it has
“encapsulation-breaking” (doesn’t actually break
encapsulation) ways of accessing your
stored Collection directly. Hence, need only store a cursor
for the current position.

For both implementations: store a cursor. Can get value, or


move cursor to next position (e.g.
increment the index for your array (internal to iterator)).

Observer pattern: [CLEANER DESIGN by restructuring your


associations]
When some event occurs, get every dependency to update
simultaneously.
Benefits: all dependencies associate to the Observable (the
controller for the Listener)
rather than associating to one another (too messy).
E.g. makes sense if you’re observing a change to some
variable.
On variable change, inform Observable; Observable
informs all dependencies.
(Event handling is a bit too abstract to understand - too
many hidden behind-the-
scene operations going on that I’m unaware of).
1. A bunch of objects are dependent on some event, so make
them all Listeners.
2. Each Listener (Observer) is controlled by a ListenerController
(Observable).
3. ListenerController is called and updated to a new state right
after the event occurs (controlled
by the other functionality in the controller).
4. ListenerController passes on this new state to all its
Listeners ‘at the same time’.

someClass implements Listener (Observer)


someClassListCont (implementation) implements
ListenerController (Observable)
someClassListCont associates (aggregates?) to a list of
Listener (or its implementation:
depends on what information is sent to the Listeners).
[Observable -> Observer association]
If ListenerController is an abstract class, then suffices to
say ListenerController associates,
rather than someClassListCont.
Observer-Observable pairing, so you have two interfaces, and
implementations for each
interface.
Input wise (when attaching observers): Observer is watching
over Observable, waiting for
something to happen.

Output wise: Observable is aware of all Observers watching


him. When he does something
they are looking for, he notifies everyone observing him.

Observable aggregates Observer (NOT composition).

Decorator pattern:
[DYNAMICALLY ADD FUNCTIONALITY through an intermediate
component]
[intermediate component = ‘OVERLAYS’ FUNCTIONALITY]
Add additional responsibilities dynamically (i.e. on runtime) to
an object.
Static extension of functionality can be done through
inheritance / adding features and
toggling them on/off with flags.
Decorator Pattern: unlike composite pattern, only composes
one Component inside of it.
1. Have a Component base class and ConcreteComponent
extending the base class.
2. Would like to add additional functionality to
ConcreteComponent dynamically.
3. Hence, use instead a Decorator object that has this
additional functionality + can attach
to any Component implementation you want.
Your additional functionality holds your base
functionality.

ConcreteComponent and Decorator both implement Component.


Decorator (abstract class) composes one Component
(ConcreteComponent, or Decorator).
Decorator is just the base class providing the “composition
functionality”.
It doesn’t provide any of the actual functionality you want =
need to extends Decorator.
(According to the website anyway; you could just
remove the interface
if you only have one Decorator, for instance).
ConcreteDecoratorExtension(s) implements Decorator, provides
the required functionality
(overlays this functionality on top of any attached component).

Example: have a SimpleWindow. A ScrollableWindow is a


Decorator with scrolling functionality,
that holds another Window inside it (overlays on this
functionality), e.g. a SimpleWindow.
Window window = new ConcreteWindow();
… now need scrolling functionality …
window = new ScrollableWindow(window); //
ConcreteWindow as parameter
// overlays scrolling functionality over an existing
Component.

Composite pattern: [implementation of TREE-LIKE STRUCTURES]


[Allows for grouping items together whilst retaining same type:
doesn’t add new functionality]
Tree structure with Nodes and CompositeNodes treated equally.
Easiest way to do this: they both share the same interface
= same basic functionalities.
Composite pattern: not about adding functionality. Can
compose many Components inside it.

Leaf and Composite implement Component.


Composite also composes 0-many Component (i.e. stores more
Leaf or Composite).

Decorator abstract class = nearly the same as Composite.


BUT: Decorator is abstract: we extend Decorator, add the
functionality we want.
NO NEED TO EXTEND Composite: all the functionality we
want is the same as what
the interface already provides. (Composite = Leaf +
composition extras).

GENERICS:
?: shorthand for Object
? extends: going to typecast everything to (up to at best the
base class)
? super Apple: (… = fruits) [example]
Can only write Apple or lower in
Reads out as super of Apple (probably Fruit), or higher (up
to Object).
Then need to typecast back down using:
Class class = fruit.getClass()
class.cast(fruit)
Why do we need it?
Extends [lower bound] is for fruits = apples. (read only,
all elements are Fruit).
Read only because can only store Apple, but read
as Fruit.
Can’t expand the precondition going up the
hierarchy.
Super [upper bound] is for apples = fruits. (read-write
possible, all elements are
Object).

EXTENDS: parent accepts child (i.e. used for typecasting all


elements to a base)
SUPER: child = parent, but can only write child to parent
(goes up (rather than down only)
For write safety.

I.e. implements covariance AND contravariance for a generic.

.getClass() is dynamic, better than instanceof hardcoding.

From sub type to base type (≤): BASICALLY LSP


Covariant: gets more general
Contravariant: gets more specific

As you go down the hierarchy,


Weaker preconditions = contra variant (less specific)
Stronger postconditions = covariant (more specific)
(alphabetical order)

Variables:
Lists: invariant (no sub typing at all for the list itself (sub typing
exists for element)), hence wildcards

https://docs.google.com/document/d/1C6-
ioRYU0VXpl1XmgKNm5BO6tP4GieNN0qyIwpKUiJs/edit

You might also like