April 22, 2009

Pseudocode

This is probably old news, but I've decided that Java MVC architectures are really over valued. Lots of classes and generic interfaces and abstractions make the business people feel like they are professional and scalable, and that is why Java is used in business.

I think one of the motivating factors is that somewhere in the back of the mind of the CEO running the Java-based company is the thought that the code written there will someday have to be presented to other business types as part of a buyout or investment process, and that these investors will really be looking closely to see if everything is as abstract as is humanly possible. That's fine, but I'd rather spend five minutes writing concise code in a flexible language than spend fifteen writing rigid Java code.

As an experiment, I wrote a partially-complete program for a killing robot in Python (my recent favorite, after JavaScript) and in Java. Which code would you rather have your dev team/minions writing? (The actual slaying/maiming functionality has been left out)[1]

Python. killingRobot.py:

#! /usr/bin/env python3

import sys

def slay(victim):
    """Slays the given victim"""
    print('Slaying '+victim)
    # system calls
    
def maim(victim):
    """Maims the given victim"""
    print('Maiming '+victim)
    #system calls

def slayAll(filename, action=slay):
    """Applies the given action to all victims in the given file"""
    try:
        f = open(filename, 'r')
        for victim in f:
            action(victim.rstrip())
        f.close()
    except:
        print('Unable to open file')

# slay or maim all victims based on user input
if __name__ == '__main__':
    if len(sys.argv) > 2:
        action = sys.argv[1];
        filename = sys.argv[2];
        if action.lower() == 'slay':
            slayAll(filename, slay)
        elif action.lower() == 'maim':
            slayAll(filename, maim)
        else:
            print('Invalid action: use slay or maim')
    else:
        print('Give an action and a file name')

Now Java. We'll begin with the interface and implementing classes. personAction.java:

public interface personAction
{
    //applies some kind of action to some given victim
    public void doAction(String victim);
}

slayAction.java:

public class slayAction implements personAction
{
    //slays the given victim
    public void doAction(String victim)
    {
        System.out.println("Slaying "+victim);
    }
}

maimAction.java:

public class maimAction implements personAction
{
    //maims the given victim
    public void doAction(String victim)
    {
        System.out.println("Maiming "+victim);
    }
}

Finally, the program. killingRobot.java:

import java.io.*;

public class killingRobot
{
    //slay or maim all victims based on user input
    public static void main(String[] args)
    {
        if(args.length > 1)
        {
            String action = args[0];
            String filename = args[1];
            personAction pAction;
            
            if(action.equals("slay"))
                pAction = new slayAction();
            else if(action.equals("maim"))
                pAction = new maimAction();
            else
            {
                System.out.println("Invalid action: use slay or maim");
                return;
            }
            
            slayAll(filename, pAction);
        }
        else
            System.out.println("Give an action and a file name");
    }
    
    //applies the given action to all victims in the given file
    public static void slayAll(String filename, personAction action)
    {
        try
        {
            FileInputStream f = new FileInputStream(filename);
            DataInputStream d = new DataInputStream(f);
            InputStreamReader i = new InputStreamReader(d);
            BufferedReader b = new BufferedReader(i);
            String victim;
            
            while((victim = b.readLine()) != null)
            {
                action.doAction(victim);
            }
            
            i.close();
        }
        catch(Exception e)
        {
            System.out.println("Unable to open file");
        }
    }
}

I won't bother comparing line counts since both of these programs could probably be shortened by better coders. The point is that Java annoys me. And what super-annoys me are these crazy attempts to make everything generic so it can supposedly work better and be scaled more easily. It's not easier or better, it's Java-er. Blech.[2].

Notes:

  1. System calls for slaying and maiming only available in Windows ME and 7 Beta
  2. Announcing the first ever Killing Robot Programming Contest. Write the most concise (fully-functional) killing robot program. Losing entries will be slain by the winning entry's killing robot program. Go ahead. Do it in Java. I dare you.

2 comments:

  1. I'm afraid you didn't make the Java implementation complicated or abstract enough. Look at your main method, it's doing the type instantiation of personAction interface right there! Bad design, Andrew.

    You need a PersonActionFactory class, which would have a method getAction(String act) and would instantiate the appropriate PersonAction for you. Of course, it's obvious that PersonActionFactory should be a singleton. Boom! Two more design patterns to make your code even more abstract, which obviously makes it better.

    import java.io.*;

    public class PersonActionFactory {
    public static PersonActionFactory pActFact = null;

    public static personActionFactory getInstance() {
    if ( pActFact == null )
    pActFact = new PersonActionFactory();

    return pActFact;
    }

    private PersonActionFactory() { }

    public PersonAction getAction(String act) {
    if ( act.equalsIgnoreCase("slay") )
    return new SlayAction();
    else if ( act.equalsIgnoreCase("main") )
    return new MaimAction();
    else
    throw new PersonActionNotFoundException("can't " + act + " a person");
    }
    }

    public class KillingRobot {
    public static void main(String[] args) {
    if(args.length != 2) {
    System.out.println("Invalid action: use slay or maim");
    return;
    }

    String action = args[0];
    String filename = args[1];
    personAction pAction;
    pActFact = PersonActionFactory.getInstance();

    try {
    pAction = pActFact.getAction(action);
    KillingRobot.slayAll(filename, pAction);
    } catch (PersonActionNotFoundException e) {
    e.printStackTrace();
    }
    }

    public static void slayAll(String filename, PersonAction action) {
    try {
    FileInputStream f = new FileInputStream(filename);
    DataInputStream d = new DataInputStream(f);
    InputStreamReader i = new InputStreamReader(d);
    BufferedReader b = new BufferedReader(i);
    String victim;

    while((victim = b.readLine()) != null)
    {
    action.doAction(victim);
    }

    i.close();
    }
    catch(Exception e) {
    System.out.println("Unable to open file");
    }
    }
    }

    ReplyDelete
  2. Abstracter daniel = new JediMaster();
    Abstracter andrew = new Padawan();

    andrew.thankForCommenting(daniel);
    andrew.bowTo(new BowableTo(daniel));

    /*
    if I set up the CVS server and you start the ANT build, we should be able to ship this by sometime mid-2015. Good collaboration!
    */

    ReplyDelete