Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Whispering

Code Whispering

Being a craftsman in development practises is only part of the problem! Structuring and refactoring your code can make it easier to add new functionality and reduce complexity, and make it easier for people to read your code. We'll take a brief look at some principles that help make great software, and the hints that your code can give you about what you can improve.

Alex Curran

March 13, 2015
Tweet

More Decks by Alex Curran

Other Decks in Technology

Transcript

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  4. 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

    View full-size slide

  5. 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;

    View full-size slide

  6. 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?

    View full-size slide

  7. 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();

    }

    }


    }

    View full-size slide

  8. 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() {


    }

    };

    }

    View full-size slide

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

    View full-size slide

  10. 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

    View full-size slide

  11. 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

    View full-size slide

  12. 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!

    View full-size slide

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

    View full-size slide

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

    View full-size slide