Thursday, June 28, 2012

Expect to fly


I love being a female developer. When I go to a hacker night, I feel like Scarlet in GI Joe, Firestar with Spiderman and his friends. Sysadmins and DBAs are happy to answer questions for a smile. In interviews and in conference speaker selection, I stand out. Colleagues open doors and invite me to happy hour. Teammates respect me and enjoy my company. My gender is an asset in my programming career.

Yet - when I meet another developer, I don't expect to be taken seriously.

It's a subtle effect. It took me a decade to notice, but much less time to cope with it. When I start on a team, half of what I say is a joke. That way it's okay when my words are laughed off. I demonstrate competence with good questions - these are more welcome than statements. When necessary, I can be very assertive, forceful enough that the men in the conference room can't blow me off. These strategies get me past the initial impressions, and within a few days I have the team's full respect. Gender is a consideration only before they know me.

The effect is so subtle I can't call it a prejudice. It's a shortcut our brains take: categorization. I do it myself. In my experience, women tend to contribute less in discussions. They don't fit the image of "strong developer" in my head. This adds up to lower expectations than for a man, at least for a short time. 

The scary part is that expectations influence behavior. Subconsciously we conform to the expectations of those around us. Because females are expected to be quiet, because they are interrupted more than men, women volunteer less. Which leads our subconscious minds to expect female developers to be quiet and contribute less.

The spiral of lower expectations can be reversed. For every woman technical speaker we hear at a conference, we'll take the next female developer we meet a little more seriously. For each woman who shows up to the user group, we'll be more likely to invite our female colleagues. Every time we direct a question to one of the women in the meeting, or ask an interrupting colleague to let the woman finish speaking, she's inspired to speak up more.

We turn this spiral around one tiny action at a time. Women, go to user groups. Men and women, recognize the bias and fight it - expect more from the women around you. Every woman can fly like Firestar.

Sunday, June 17, 2012

Android: Confirmation Dialog


Warning! This technique is suited to Android API Level 8-10 (Gingerbread). For Honeycomb or newer, use DialogFragment.

Problem: my activity knows how to do something (delete a picture), but it needs to ask the user for confirmation first.

Solution: pop a confirmation dialog, whose "yes" button responds by deleting the picture.

Code:
For the very simplest case, where

  • the activity uses no other dialogs
  • the message in the dialog never changes

then two steps are required:

  1. Instead of taking the action, call showDialog.

    private void deletePicture() {
        showDialog(0);
    }
(where 0 is any integer - we don't care because this is the only dialog used by this activity)

  1. override onCreateDialog(int) to return the dialog object. Building the confirmation dialog is a good-sized chunk of code. The following sample create pulls the creation of a Yes/No dialog into a method, so that the defining of the action we want to take on "Yes" is separated from the cruft of creating this simple-case dialog.


    @Override
    protected Dialog onCreateDialog(int id) {
        return createConfirmationDialog("Delete this picture?", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                reallyDeleteThePicture();
            }
        });
    }


    private AlertDialog createConfirmationDialog(String message, DialogInterface.OnClickListener yesAction) {
        return new AlertDialog.Builder(this).setMessage(message)
                .setPositiveButton("Yes", yesAction)
                .setNegativeButton("No", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        dialogInterface.cancel();
                    }
                }).create();
    }

Unfortunately the code that executes on "yes" is obfuscated by Java's painful syntax, and we all bang our heads on our desks and wish for Java 8. Good thing blog posts have color. If you're here to cut and paste, then replace the red message with your text and the green method call with your own code, and you're done.

What is happening here? showDialog triggers a call to onCreateDialog. Here we're ignoring the int argument because we only ever use one dialog in this activity. AlertDialog.Builder is used to create a dialog with two buttons: Yes, which performs the desired action wrapped in an OnClickListener; and No, which closes the dialog and takes no other action. The user can also close the dialog by pressing back.

The onCreateDialog method will be called only once the first time the user activates the deletePicture() functionality. After that, the same dialog object will be reused.

Let's look at what we need to do in case our assumptions are still not valid. This is still just a confirmation dialog, but what if:

  • the activity uses another dialog.

Suddenly the int passed to showDialog and onCreateDialog matters. They match up, so that the one override onCreateDialog can handle construction of two different dialogs.

Code: Perhaps we also need to confirm overwriting some changes. The below example makes use of the createConfirmationDialog method defined above.

    private static final int DIALOG_DELETE_PICTURE = 1;
    private static final int DIALOG_OVERWRITE_PICTURE = 2;

    private void deletePicture() {
        showDialog(DIALOG_DELETE_PICTURE);
    }


    private void savePicture() {
        showDialog(DIALOG_OVERWRITE_PICTURE);
    }


    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_DELETE_PICTURE:
                return createConfirmationDialog("Delete this picture?", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        reallyDeletePicture();
                    }
                });
            case DIALOG_OVERWRITE_PICTURE:
                return createConfirmationDialog("Save changes? This will overwrite the original picture.", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        reallySavePicture();
                    }
                });
        }
        throw new IllegalArgumentException("unknown dialog ID: " + id);
    }

This onCreateDialog method will be called up to twice in the activity's lifetime, once per id.

That wasn't too bad. Now what if

  • the message is different each time the dialog pops

Since onCreateDialog is called only once for each dialog ID, there's another hook for customizing the dialog per use. Override onPrepareDialog(int, Dialog).

    @Override
    protected void onPrepareDialog(int id, Dialog dialog) {
        switch (id) {
            case DIALOG_DELETE_PICTURE:
                ((AlertDialog) dialog).setMessage("delete picture " + pictureDescription + " ?");
                break;
            default:
                super.onPrepareDialog(id, dialog);
        }
    }

These are simple cases for a simple dialog. For anything more complicated, dig around in the user guide.
This isn't pretty code, with the same concern (this simple dialog) scattered around the activity's code. For Honeycomb and higher, this is abstracted into a DialogFragment, which looks like more fun. Too bad my phone is on Gingerbread.

Tuesday, June 12, 2012

Connecting Java Projects in Gradle

The problem: multiple Java projects, each with their own Gradle build, dependent on each other but not linked.
The goal: connect them into one great build to rule them all.
The caveat: without messing with the individual builds. It is 3 days before go-live at this startup.

The solution:
1) get all of the projects under one subdirectory. They don't have to be immediate subdirectories, just somewhere under one directory to rule them all. Note that soft links won't work, because the subprojects need to see up to the rule-them-all.
2) in that directory to rule them all, create settings.gradle to list the projects
import 'picture', 'tools:hammer', 'tools:hanger'
Note the colon delimiter for subdirectories.

3) in the directory to rule them all, create build.gradle to set up the dependencies.


project(':picture') {
  dependencies {
    compile project(':tools:hammer'), project(':tools:hanger')
  }
}


this is all that's needed to define the dependencies... except that it doesn't quite work. The error is:


> No signature of method: org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.compile() is applicable for argument types: (org.gradle.api.internal.project.DefaultProject_Decorated, org.gradle.api.internal.project.DefaultProject_Decorated) values: [project ':tools:hammer', project ':tools:hanger']

Before that 'compile' concept exists, we have to apply the Java plugin to each subdirectory. Even though each local build.gradle applies the plugin, we need it earlier than that, at the top level.

4) Put this at the top of the new build.gradle file:
subprojects {
  apply plugin: 'java'
}

Once the dependencies are defined here, builds of individual projects will trigger builds of their dependencies. The output of each dependency will automatically be added to the classpath of the dependent project. It's pretty sweet: whether you perform the build from the rule-them-all directory or from the subproject you're currently working on, Gradle looks upward in the directory structure, learn about dependencies, and build other subprojects as necessary.

Anecdote:
On the path from the cryptic error message, I consulted the one true documentation (the source code).
The example in the documentation ran, but not my build. My build gave that "No signature of method" error. To learn what was going on inside the dependencies block, I printed info about the delegate objects (see this post); the delegate objects were the same. No compile method existed on either of them. Where to look next? How is it finding the compile method in the example that runs?
This is where just a little knowledge of Ruby comes in. I spotted a method in the delegate object called methodMissing. In Ruby, creating this method lets the object decide what to do with any method call not already defined. It turns out that Groovy works the same way. This was the right place to put the debug point in IntelliJ IDEA.
Comparing the behavior of methodMissing showed that the compile configuration existed in the example but not in my build. That led to "oh, maybe we need to apply the Java plugin first."

The multiproject build is working beautifully now, with 0 modifications to the subprojects. Happy birthday, Gradle 1.0!

Wednesday, June 6, 2012

Why Functional Matters: Your white board will never be the same

Why learn functional programming? For better design skills!

The other day we designed a process to match cleared deposits with dispensations. This is how I would have white-boarded it a few years ago:

Since then I've played around with functional programming. It encourages one to think about processing in terms of data streams: data comes in, gets transformed and filtered and manipulated, and finally results are output. Not only do programs work this way, but each piece of the program -- each function -- is also modeled as data in, data out. No state changes in the middle.
Thinking about the problem in this way leads to this kind of diagram:
Thinking about the program as a data pipeline keeps me thinking about what needs to happen, instead of how each step should be done. It's a higher level of thinking that keeps us from bogging down in implementation details at the design phase.

Whatever language we use to write the solution, thinking about it this way has the following advantages:
* It breaks down into pieces. (The orange boxes represent JIRA tasks.) The requirements are already specified on the diagram: what goes in, what comes out. Each task can be developed independently.
* Each bit is testable. The database-impacting pieces are called out; other than that, each box is defined entirely by input and output. That's the easiest kind of component to test.

In this way, thinking functionally helps with agile (task breakdown), TDD, and maintainability. The code is more composable. Thinking about the problem is easier because we can see what the data looks like at each step.
Independent, testable components: that's functional.

For how functional thinking helps at a lower level too, check this post.

New coding tricks are fun, but new white board techniques are serious business.