Slide 1

Slide 1 text

Code Whispering Can your code tell you what’s wrong?

Slide 2

Slide 2 text

What makes good code? • Small, single responsibility classes with well-defined contracts • Hide implementation details from outsiders • Predictable state • SOLID principles

Slide 3

Slide 3 text

SOLID Principles Single responsibility Open/closed principle Liskov’s substitution principle Interface segration Dependency inversion

Slide 4

Slide 4 text

What’s a code smell? "a code smell is a surface indication that usually corresponds to a deeper problem in the system" Martin Fowler • Long methods • God objects (cough, Context) • Subclassing

Slide 5

Slide 5 text

Check your imports! • Lots of imports can imply lots of responsibilities • Perhaps some obvious delegation or leaking of implementation detail // Animations
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.app.Activity;
 // Widget?
 import android.appwidget.AppWidgetManager;
 // Copy/pasting
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 // Data
 import android.database.Cursor;
 // UI
 import android.graphics.Typeface;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 // More data
 import android.support.v4.app.LoaderManager.LoaderCallbacks;
 import android.support.v4.content.CursorLoader;
 import android.support.v4.content.Loader;
 // More UI
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
 import android.widget.Toast;
 
 import com.espian.formulae.data.ProProvider;
 import com.espian.formulae.data.ProviderHelper;
 import com.espian.formulae.data.UserDatabaseHelper;
 import com.espian.formulae.lib.BaseContentFragment;
 import com.espian.formulae.lib.R;
 // Rendering
 import com.espian.formulae.utils.AssetImage;
 import com.espian.formulae.utils.EquationParser;
 import com.espian.formulae.utils.Helper;
 // More animations
 import com.espian.formulae.utils.InLeftHideHelper;
 import com.espian.formulae.utils.InRightHideHelper;
 // More rendering
 import com.espian.formulae.utils.SuperAdapter;

Slide 6

Slide 6 text

Fields and parameters • Make as many fields final as possible • Temporary fields often mean an obvious delegate (e.g. lastX and lastY in an activity) • Lots of parameters/constructor args imply lot of responsibility • Many parameters passed around together - is this a thing?

Slide 7

Slide 7 text

Branching • Avoid an “if” statement if you can • Imply two different behaviours • Makes it more difficult to test all the routes • Strategy pattern is a really good idea public void doSomething() {
 downloadLargeFile(preferences.wifiDownloadsOnly());
 }
 
 public void downloadLargeFile(boolean wifiOnly) {
 if (wifiOnly) {
 if (connectivity.isOnWifi()) {
 download();
 }
 } else {
 download();
 }
 } public void doSomething() {
 if (preferences.wifiDownloadsOnly()) {
 downloadOnWifiOnly();
 } else {
 download();
 }
 } public void doSomething() {
 DownloadStrategy ds = preferences.downloadStrategy(connectivity);
 ds.attemptDownload();
 }
 
 public class WifiOnlyStrategy implements DownloadStrategy {
 
 public void attemptDownload() {
 if (connectivity.isOnWifi()) {
 download();
 }
 }
 
 }

Slide 8

Slide 8 text

Null checks • Try to avoid setters - required dependencies in the constructor • If an interface is optional, define a null implementation - no null checks! • Make use of the @Nonnull and @Nullable annotations public interface MessagesCache {
 
 Bitmap getContactPhoto(Contact contact);
 
 void storeContactPhoto(Contact contact, Bitmap bitmap);
 
 void invalidate();
 
 MessagesCache NO_CACHE = new MessagesCache() {
 
 @Override
 public Bitmap getContactPhoto(Contact contact) {
 return null;
 }
 
 @Override
 public void storeContactPhoto(Contact contact, Bitmap bitmap) {
 
 }
 
 @Override
 public void invalidate() {
 
 }
 };
 }

Slide 9

Slide 9 text

AsyncTask • Defines how work is done, and the work that is done • Not exactly single responsibility • Base class inheritance • Contrast with Executor

Slide 10

Slide 10 text

Flaky tests • Avoid network as much as you can • @SmallTest? All tests should be small! • Fewer integration tests, the better • Threading - mixing how to execute with what to execute • Emulators and UI

Slide 11

Slide 11 text

Large tests • Large set-up implies complex, badly-defined responsibilities • But not always true! • JVM tests are much faster than Robolectric • Pushing Android out of your domain = faster feedback

Slide 12

Slide 12 text

Metrics • Cyclomatic complexity • Ecomp (imports, conditionals, inheritance) • Code coverage before and after test, if it has gone down then new behaviour has been added and it isn’t a refactor!

Slide 13

Slide 13 text

Structural patterns • Structure code according to responsibility • MVC - model-view-controller • MVP - model-view-presenter

Slide 14

Slide 14 text

More resources • Ecomp project on Github • TDD: Where Did It All Go Wrong? • Growing Object-Oriented Software