Project 1 notes

Variables

Make member variables private

Reduce “surface area” of your code.

Allows changes later without affecting other code.

No hard coded values!

No magic values.

Generalize your code with variables.

You should almost never use a numeric literal other than 0 or 1 except to assign it to a variable.

Use static final variables to make “symbolic constants”.

Define each magic numbers only once.

Variable naming guidelines

There should be an inverse relation between scope of variables and verbosity of name.

Loop variables are often one letter (e.g. i, and j).

And sometimes parameters of very short methods whose arguments are pretty abstract. (E.g. s for a String parameter that can be any String.)

But otherwise use at least one-word, meaningful names.

Parameter names

It’s especially important that method and constructor parameters be meaningful because the first thing you look at when trying to understand a method is its signature.

Variable hiccups

Immediate gargbage creation

List<String> lines = new ArrayList<String>();
lines = Files.readAllLines(getFilePath());

The ArrayList created in the first line is immediately replaced with the List returned by readAllLines, becoming gargbage immediately.

List<String> lines = Files.readAllLines(getFilePath());

Mayfly variables

public String firstHalf(String s) {
  String half = s.substring(0, s.length() / 2);
  return half;
}

No need for a variable here.

public String firstHalf(String s) {
  return s.substring(0, s.length() / 2);
}

Helper methods

Many of you really need to get on board with this.

Some tips

Methods should be about five lines of code.

It’s fine to write a method that is only used in one place.

A method call to a method with a meaningful name carries much more information about intent than the codeof the method, which has to be decoded.

Names as double-entry bookkeeping

When you name a variable or method with a meaningful name you now have two statements about what it is: the name and it’s actual definition.

That enables you (or anyone else reading the code) to check that they match.

Example

Is this code right?

int foo = (a - b) / 2;

How about this code?

int average = (a - b) / 2;

Polymorphism

This is the main power move of object oriented programming.

So use it.

Switch vs polymorphism

switch (object.kind()) {
  case SQUARE -> drawSquare(object);
  case TRIANGLE -> drawTriangle(object);
  case CIRCLE -> drawCircle(object);
}

vs.

object.draw();

Checking class of objects vs polymorphism

if (maybeFoo.getClass() == new Foo().getClass()) ...

if (maybeFoo.getClass() == Foo.class) ...

if (maybeFoo instanceof Foo) ...

vs

if (maybeFoo.isFoo()) ...

or even:

maybeFoo.doFooishThing();

Law of Demeter

Do not access the inner workings of other objects.

A.K.A. only one dot.

Bad

foo.entries.find(something)

Still not great

foo.entries().find(something)

Good

foo.findEntry(something)

Value chaining is okay

It is okay to have multiple dots when each method is returning a value, not exposing the inner structure of an object.

Arrays.stream(ss)
      .map(s -> s.substring(0, 1))
      .filter(this::isVowel)
      .count();

That’s fine.