COMP2511 Review (Part 1)

Haven’t write any thing since last year, thinking might be better to write something than just leave this blog as it is. This article is a brief review of UNSW’s COMP2511 course, which is about object oriented design. Just to prepare for my final exam.

Week 1 (Java Basics)

1. Object: Objects have state (attributes) and behaviour (methods – what the object can do).

2. Class: An object is instantiated from a class and the object is said to be an instance of the class.

  • Two object instances from the same class share the same attributes and methods, but have their own object identities and are independent of each other.

3. Constructor: A special method that creates an object instance and assigns values to their attributes.

4. Abstraction: Helps you to focus on the common properties and behaviours of objects.

5. Encapsulation: An OO design concept that emphasises hiding the implementation.

  • Encapsulation lead to abstraction.
  • Encapsulation of object state implies hiding the object’s attributes.
  • Method provides explicit access to the object (getter and setter).

6. Benefits of Encapsulation:

  • Encapsulation ensures that an object’s state is in a consistent state
  • Encapsulation increases usability
  • Encapsulation abstracts the implementation, reduces the dependencies so that a change to a class does not cause a rippling effect on the system

7. Inheritance: models a relationship between classes in which one class represents a more general concept (parent or base class) and another a more specialised class. (is-a type)

  • sub-class can extend parent class by defining additional properties
  • sub-class can override methods in the parent class
  • use “super” in subclass, to invoke a parent’s method.
  • Constructors are not inherited. To create an instance of a subclass, there are two ways: 1. Use the default no-arg constructor. (This default constructor will make a default call to super() ). 2. Define a constructor in subclass.
  • The Java language allows a class to extend only one other class – single inheritance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
super.account; // invoke field
super.getBalance(); // invoke method
super(color); // invoke constructor
// parent class has a getArea() method
public class Shape{
...
public float getArea(){
return 0;
}
...
}
// getArea() method is override in the sub-class
// method signature must be the same in parent class
// Override methods cannot be less accessible
public class Rectangle extends Shape{
...
@Override
public float getArea(){
return this.height * this.width;
}
}

Week 2 (Inheritance, Polymorphism, Domain modelling)

1. Object types: All object references have a type.

2. Polymorphism: means “many forms”, an important OO principle to support software reuse and maintenance.

1
2
3
// these assignments are legal as Rectangle and Circle are both types of Shape
Shape s1 = new Rectangle();
s1 = new Circle();

3. Dynamic Binding: ensures that when a method is invoked, you get the behaviour associated with the object to which the variable refers to at runtime. (not determined at compile time)

4. instanceof operator: this method allows us to know what is the actual type of object at run time. if (s instanceof Rectangle) { // do something }

5. Access Modifiers:

  • Access Modifiers

6. Association: Association is a special relationship between two classes, that shows two classes are linked to each other. Or combined into some kind of has-a relationship, where one class “contains” another.

  • Aggregation: The contained item is an element of a collection but can also exist on its own. (Lab contains Computers)
  • Composition: The contained item is an integral part of the containing item. Cannot exist without another. (Order has Line Items)

  • UML

7. Requirements Analysis: determines “external behaviour”. what are the features of the system and who requires these features (actors).

8. Domain Modelling: determines “internal behaviour”. how elements of system-to-be interact to produce the external behaviour.

  • Also referred to as conceptual model or domain object model
  • Provides a visual representation of the problem.
  • Techniques to build a domain model: None/Verb Analysis, CRC Cards.

9. CRC Cards: Class, Responsibility, Collaborator.


Week 3 (Interfaces, Design Principles, LSP)

1. Abstract Class: A class which cannot be instantiated, cannot create object instances of an abstract class.

  • Serves as a convenient place-holder for factoring out common behaviour in sub-classes
  • An abstract class may define abstract methods.
  • A class with one or more abstract methods must be declared as abstract

2. Interface: defines collection of abstract methods. (defining methods without a body)

  • Specifies “what” needs to be done, but not “how” to do it
  • Classes can implement multiple interfaces, but extend only one

3. Design Smell: is a symptom of poor design, often caused by violation of key design principles.

  • Rigidity: Tendency of the software being too difficult to change even in simple ways.
  • Fragility: Tendency of the software to break in many places when a small change is made.
  • Immobility: Design is hard to reuse.
  • Viscosity: changes are easy to implement through “hacks” over “design preserving methods”.
  • Opacity: Tendency of a module to be difficult to understand.
  • Needless complexity: Contains constructs that are not currently useful.
  • Needless repetition: Design contains repeated structures that could potentially be unified under a single abstraction.

4. Coupling: Is defined as the degree of interdependence between components or classes.

  • High coupling occurs when one component A depends on the internal workings of B.
  • High coupling leads to complex system, with difficulty in maintenance.

5. Cohesion: The degree to which all elements of a component or class or module work together as a functional unit.

  • Highly cohesive modules are much easier to maintain and less frequently changed and more reusable.

6. Good software aims for building a system with loose coupling and high cohesion.

7. Design principle: A basic tool or technique that can be applied to designing or writing code to make software more maintainable, flexible and extensible.

  • Design principles help eliminate design smells
  • don’t apply principles when there no design smells

Design principle 1 (Least Knowledge or Law of Demeter)

Talk only to your friends!

  • classes should know about and interact with as few classes as possible
  • reduce the interaction between objects to just a few close “friends”
  • helps us to design “loosely coupled” system

Rule 1: A method M in an object O call on any other method within O itself

1
2
3
4
5
6
7
8
public class M{
public void methodM(){
this.methodN();
}
public void methodN(){
// do something
}
}

Rule 2: A method M in an object O can call on any methods of parameters passed to the method M

1
2
3
4
5
6
7
8
9
10
11
public class O{
public void M(Friend f){
f.N();
}
}
public class Friend{
public void N(){
// do something
}
}

Rule 3: A method M can call a method N of another object, if that object is instantiated within the method M

1
2
3
4
5
6
7
8
9
10
11
12
public class O{
public void M(){
Friend f = new Friend();
f.N();
}
}
public class Friend{
public void N(){
// do something
}
}

Rule 4: Any method M in an object O can call on any methods of any type of object that is a direct component of O

1
2
3
4
5
6
7
8
9
10
11
12
13
public class O{
public Friend var = new Friend();
public void M(){
var.N();
}
}
public class Friend{
public void N(){
// do something
}
}

Design principle 2 (Liskov Substitution Principle) (LSP)

LSP is about well-designed inheritance

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

In mathematics, a Square is a Rectangle. Indeed it is a specialization of a rectangle. The “is a” makes you want to model this with inheritance. However if in code you made Square derive from Rectangle, then a Square should be usable anywhere you expect a Rectangle. This makes for some strange behavior.

Imagine you had SetWidth and SetHeight methods on your Rectangle base class; this seems perfectly logical. However if your Rectangle reference pointed to a Square, then SetWidth and SetHeight doesn’t make sense because setting one would change the other to match it. In this case Square fails the Liskov Substitution Test with Rectangle and the abstraction of having Square inherit from Rectangle is a bad one.

Reference

8. Refactoring: the process of restructuring software to make it easier to understand and cheaper to modify withour changing it’s external, observable behaviour.

  • refactoring improves design of software
  • refactoring makes software easier to understand
  • refactoring helps you find bugs
  • refactoring helps you program faster
  • refactoring helps you to conform to design principle and avoid design smells
  • refactor when you add a function
  • refactor when you need to fix a bug
  • refactor as you do a code review

9. Common Bad Code Smells:

  • Duplicated Code
  • Long Method
  • Large Class
  • Long Paramenter List
  • Divergent Change: when one class is commonly changed in different ways for different reasons
  • Shotgun Suergery: the opposite of divergent change, when you have to make a lot of little changes to a lot of different classes

10. delegation: The act of one object forwarding an operation to another object to be performed on behalf of the first object

Refactoring Techniques 1: Extract Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Before extract method:
public void method(){
int a = 1;
int b = 2;
int c = a + b;
int d = a + c;
}
// after extract method:
public void method(){
int a = 1;
int b = 2;
int c = add(a, b);
int d = add(a, c);
}
private int add(int a, int b){
return a + b;
}

Refactoring Techniques 2: Rename Variables

Good code should communicate what it is doing clearly, and variable names are a key to clear code. Never be afraid to change the names of things to improve clarity.

Refactoring Techniques 3: Move Method

Generally, a method be on the object whose data uses it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Project {
Person[] participants;
}
class Person {
int id;
boolean participate(Project p) {
for(int i=0; i<p.participants.length; i++) {
if (p.participants[i].id == id) return(true);
}
return(false);
}
}
... if (x.participate(p)) ...
// After applying the move you end up with
class Project {
Person[] participants;
boolean participate(Person x) {
for(int i=0; i<participants.length; i++) {
if (participants[i].id == x.id) return(true);
}
return(false);
}
}
class Person {
int id;
}
... if (p.participate(x)) ...

Refactoring Techniques 4: Replace Temp with Query

A technique to remove unnecessary local and temporary variables.

Refactoring Techniques 5: Replace conditional logic with polymorphism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Bird {
//...
double getSpeed() {
switch (type) {
case EUROPEAN:
return getBaseSpeed();
case AFRICAN:
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
case NORWEGIAN_BLUE:
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
throw new RuntimeException("Should be unreachable");
}
}
// After applying refactoring technique 5:
abstract class Bird {
//...
abstract double getSpeed();
}
class European extends Bird {
double getSpeed() {
return getBaseSpeed();
}
}
class African extends Bird {
double getSpeed() {
return getBaseSpeed() - getLoadFactor() * numberOfCoconuts;
}
}
class NorwegianBlue extends Bird {
double getSpeed() {
return (isNailed) ? 0 : getBaseSpeed(voltage);
}
}
// Somewhere in client code
speed = bird.getSpeed();

equals() method

Difference between a == b and a.equals(b)

a == b checks memory references only
a.equals(b) calls the eqauls() method that associated with object a

Bad equals():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Point{
int x, int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public boolean equals(Point obj){
return this.x == obj.x && this.y == obj.y;
}
public static void main(String[] args){
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.out.println(p1.equals(p2)); // true
Object p3 = p2;
System.out.println(p3.equals(p1)); // false
}
}

Write a good equals() method

1
2
3
4
5
6
7
8
9
10
11
12
public boolean equals(Object obj){
// Step 1: compare object references similar to Object's equals()
if (this == obj) return true;
// Step 2: Check if paramter is null
if (obj == null) return false;
// Step 3: compare each field
if (getClass() != obj.getClass()) return false;
Point other = (Point) obj;
if (x != other.x) return false;
if (y != other.y) return false;
return true;
}
谢谢你请我吃糖果:)