Vous êtes sur la page 1sur 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 classs 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 prepost-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.
Cant 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. statically 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 supers clone instead. Child classes will call the
super.clone() of their
parent, and any fields that arent 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 supers 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. dont 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 / suppliers obligations and benefits.
Stronger precondition = more burden on client; and vice-versa with postcondition.
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.
Dont 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. Dont 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: dont 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 cant test everything.
Dont 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 its meant to say is you shouldnt have direct access to the internal
implementation,
EVER. So basically, dont ever make your aggregated classes public?
(Possibly OK for primitives and structs - these only reveal some
statistic data, they
dont 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 objects existence dependent on
composed objects
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 its within the
Semester object = it doesnt 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 Javas 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 didnt 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 mallocd, 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 hasnt 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 functions 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, dont 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 doesnt 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 classs 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:
Variables 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 objects
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, cant add it as an Apple.
I.e. covariance not preserved (its 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).
Whats the point of this though? Its like arguing: you cant go up, you can
only go down
(which is how polymorphism works to begin with, so thats 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());
// wont compile
// fruits.add(new Fruit());
// wont compile because cant input
using extends
// why? cant 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 dont 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 (cant 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 dont 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 wouldnt
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 - its 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
(Cant 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
Dont need edge relaxation if isolating storage of the path inside the state
(inefficient).
Why? Because theres 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. doesnt over-estimate), otherwise: cant
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)
Dont over-engineer: make a solution more general only if it doesnt
increase complexity.
Dont over-produce: e.g. oversupply in goods, unnecessary software
content.
Dont 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. Dont 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, doesnt 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 its 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 users 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 itll position them (and scale them)
automatically for you.

= Very powerful.
Dont 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 its 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-swingrevalidate-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 (dont 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; dont
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 theres 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 doesnt 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: dont 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. Im 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 doesnt
happen.
Solution: can read in parallel, cant 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, its 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 cant 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 dont 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(): cant do required condition yet. Atomically release the lock here
and wait until
// myCondition.signal(thisMethod) or myCondition.signalAll() is called.
//
whileawait() 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/Lock.html
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.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), doesnt 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, dont: 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 doesnt store the contents of your collection to iterate

over.
Its defined within (i.e. nested) within your Collection class itself, hence it
has
encapsulation-breaking (doesnt 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 youre 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-thescene operations going on that Im 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 doesnt 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: doesnt 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.
Cant 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/1C6ioRYU0VXpl1XmgKNm5BO6tP4GieNN0qyIwpKUiJs/edit

Vous aimerez peut-être aussi