あけましておめでとうございます

いまさらだけど、あけましておめでとうございます。昨年の今頃はちょうどニコ生アプリの開発とかで楽しく忙しく過ごしてましたが、今年も年明け早々新規プロジェクトをすすめており、楽しみな毎日を送っております。仕事以外では、なんといっても初ジュニア誕生という大イベントが控えているので、そちらもドキドキワクワクしています。
ところで、今日ふと本屋で「グーグルで必要なことは、みんなソニーが教えてくれた」という本を見つけて、すごく気になったので早速購入して一気に読み終わりました。

グーグルで必要なことは、みんなソニーが教えてくれた

グーグルで必要なことは、みんなソニーが教えてくれた


著者は、元ソニーでVAIOスゴ録を産み出し、その後グーグルの日本法人の社長をやっていた辻野晃一郎さん。特に面白かったのが、前半から中盤にかけてのソニーの栄光と凋落のくだり。いかにしてソニーが駄目な感じになっていき、そのなかでいろいろ苦労してきたか、ということが書かれている。「コクーン」やら「PSX」やら「ソニックステージ」やら「ATRAC」やら、いろいろ懐かしい製品の話が取り上げられていて、なるほど内部ではこんな経緯があったのかって感じ。そーいや俺が新卒の初任給で買ったのがソニーのNW-MS70Dだったなー。当時はデザインがかっこいいと思って買ったけど、物理的に速攻で壊れたし、ソニックステージやATRACは糞だった記憶しかないな。
SONY NW-MS70D ネットワーク ウォークマン

SONY NW-MS70D ネットワーク ウォークマン


で、本の後半はグーグルがいかにすごいか、クラウドがあーだこーだの話で、まあどーでもいい感じだったけど、全体としては結構おもしろかった。ソニー関連の話が多いだけあって、品川やら大崎やらウチのご近所もいっぱい登場したし。。。
それにしても久しぶりにブログ書いたな。

AdMob広告の威力を調査するためのアプリ「剣道」をリリースしたのだった

AdMob広告ってどれくらい儲かるのかなーと興味があったので、過去に作ってお蔵入りしていたiPhoneアプリを適当に手直しして適当に審査だした。AppStoreより無料でダウンロードできます。

ダウンロードはこちら。http://itunes.apple.com/jp/app/id392501848?mt=8

でAdMobがどれくらい儲かるかというと、アプリ一本あたりコンビニのアルバイト1〜3時間/日って感じかな。もちろんアプリの人気具合にもよりますが。

実装tips

iPhoneアプリを作る上で、よくハマるところや忘れがちなテクニックについてメモっておく。Twitterだと検索しづらいので。

IBを使ってUITableViewCellをカスタマイズする

http://d.hatena.ne.jp/KishikawaKatsumi/20081024/1224857278
基本的には上記エントリで紹介されている方法を使えばOK。必要なければUIViewControllerのサブクラスは作らなくてもいい。

IBを使ってUITableViewを内包したビューをつくると罫線がにじんだように表示される

サブビューにUITableViewを追加して、UITableViewのStyleを「Grouped」にすると、デフォルトでUITableViewのSeparatorが「Single Line etched」になる。この状態でアプリを起動すると、グループ化されてたテーブルの上端の罫線がにじんだように表示される。Separatorを「None」にすればOK。

AdMobの広告が表示されない

AdMob SDK付属のREADME.txtで説明されているIBを使った実装手順どおりに実装しても広告が表示されない。手順書に漏れがあるため。README.txtの「Step5(iPhone)」の、パブリッシャーIDを登録する前に以下の作業が必要。

In the UIViewController that owns the xib, create an AdViewController
IBOutlet that retains. Establish a connection from the owner UIViewController to the AdViewController. Remember to release the AdViewController in the UIViewController's dealloc method.

ちなみに上記説明文は「Step5(iPad)」のほうには書いてあるのに、iPhoneのほうには書いてない。

有隣堂書店のブックフェアにて技術書を推薦させていただきました

秋葉原のヨドバシカメラ7F有隣堂書店にて開催中のIT技術者向けブックフェアで、推薦者として参加しました!Java系技術書を中心に10数冊推薦させていただきました。

「2010春 世界に放つ!AKIBAエンジニア育成計画」

こんな感じで推薦しました。写真付き(近所にロケしに行って嫁さんに撮影してもらったもの)。いやー太ってるね。

それぞれの推薦書籍にコメント文を書いて送ったんですが、有隣堂の店員さんによる(?)可愛い感じのポップとして仕上がってました。


自分以外には、

  • 岩野和生さん(日本IBM執行役員)
  • 高橋征義さん(日本Rubyの会会長/高橋メソッド!)
  • 伊藤直也さん(はてなCTO)
  • 柴田芳樹さん(「Effective Java」等、技術書の翻訳を多数)
  • 開米瑞浩さん(「ネーミングの掟と極意」等の著者)

などなど、そうそうたるメンバーに紛れ込んでいるかたちとなっております(o_o;)

5月中旬までやっているらしいので、ぜひお立ち寄り下さい。

[有隣堂 ヨドバシAKIBA店]
http://www.yurindo.co.jp/shop/y_akiba.html

Guiceを使ってみた

今開発しているプログラムをGuiceを使って書いてみた。そもそもGuiceを使おうと決めた理由はテストを書き易くするため。実際に使ってみた感想としては「ま〜、こんなもんか」って感じ。

だいたいの使用パターンとしては以下のような感じになると思う。Guiceの高度な機能を使うと他の人がコードを読めなくなりそうなので最低限の機能だけ使うことにする。

まずはテスト時に差し替えたいクラスのIFを切り出しておく。

public interface LoginClient {
  public boolean login(String id, String password);
}

で、実コードを実装する。

public class LoginClientImpl implements LoginClient {
  public boolean login(String id, String password){
    //DB Access..
    return true;
  }
}

次に、それを使う側のクラスを定義する。こっちはIF切らなくてもよい。

import com.google.inject.Inject;

public class Service {
  
  private LoginClient loginClient;
  
  @Inject
  public Service(LoginClient loginClient){
    this.loginClient = loginClient;
  }
  
  public String execute(){
    boolean login = loginClient.login("ken", "xxxx");
    if(login){
      return "OK";
    }else{
      return "NG";
    }
  }
}

このServiceクラスはGuiceによってnewされることを想定している。
コンストラクタに「@Inject」をつけたので、Guiceインスタンスを作るときに、LoginClientをインジェクトしておいてくれる。

なお、フィールドに@Injectをつけておけば、コンストラクタを定義しなくてもGuiceがインジェクトしておいてくれる。privateなフィールドでもOK。本当は全部フィールドインジェクションでいければ美しいんだろうけど、テストが書き辛くなる(後述)という結論に達したため、コンストラクタインジェクションを使うことにする。

次にGuiceによるDIの設定をするためのモジュールクラスを定義する。どのIFにどの具象クラスがインジェクトされるか、ってことを定義するクラス。Javaで書ける設定ファイルみたいなもんですね。

import com.google.inject.AbstractModule;

public class MyModule extends AbstractModule{
  @Override
  protected void configure() {
    bind(LoginClient.class).to(LoginClientImpl.class);
  }
}

で、最後にエントリポイントのクラスを作る。

import com.google.inject.Guice;
import com.google.inject.Injector;

public class Main {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new MyModule());
    Service service = injector.getInstance(Service.class);
    String msg = service.execute();
    System.out.println(msg);
  }
}

最初にModuleを指定してInjectorを作っておき、それ以降はInjectorにインスタンスを作ってもらう。

まあ、ここまではいいんだ。問題ない。すっきりしててよい。
で、ここからServiceクラスのテストコードを書いて行きたいわけだ。ただし、Serviceクラスの中で使っているLoginClientImplはDBにアクセスするので、テストのときはDBにアクセスしないモックで差し替えてテストしたい。

実コードの方ではInjectorを使ってServiceを構築したわけなので、テストの場合もテスト用のModuleを使ってInjectorを作り、DBアクセス無しのLoginClientが差し込まれたServiceオブジェクトを構築したい。

するとテストコードはこんな感じになる。

import static org.junit.Assert.assertEquals;
import guicesample.LoginClient;
import guicesample.Service;

import org.junit.Test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

public class ServiceTestWithGuice {
  @Test
  public void testExecuteWhenLoginSuccess(){
    Injector injector = Guice.createInjector(new AbstractModule(){
      @Override
      protected void configure() {
        bind(LoginClient.class).to(LoginClientSuccess.class);
      }});
    Service service = injector.getInstance(Service.class);
    assertEquals("OK", service.execute());
  }
  
  @Test
  public void testExecuteWhenLoginFailure(){
    Injector injector = Guice.createInjector(new AbstractModule(){
      @Override
      protected void configure() {
        bind(LoginClient.class).to(LoginClientFailure.class);
      }});
    Service service = injector.getInstance(Service.class); 
    assertEquals("NG", service.execute());
  }
  
  static class LoginClientSuccess implements LoginClient{
    @Override
    public boolean login(String id, String password) {
      return true;
    }
  }

  static class LoginClientFailure implements LoginClient{
    @Override
    public boolean login(String id, String password) {
      return false;
    }
  }
}

あああ・・、ながい。複雑。
本当はLoginClientのモックは無名クラスとしてテストメソッド内で定義したいんだけど、bind().to()の引数はClassを指定する必要があり、かつGuiceの仕様により非staticな内部クラスはインジェクト対象に出来ない、という制限があるため、上記のように、ログイン成功用、失敗用のLoginClientモックをわざわざ定義した、ってわけです。めんどくせー。

あまりにもテストコードが嫌な感じなんで、テストコードはGuiceを使わず以下のように書くことにする。

import static org.junit.Assert.*;
import guicesample.LoginClient;
import guicesample.Service;

import org.junit.Test;

public class ServiceTest {
  @Test
  public void testExecuteWhenLoginSuccess(){
    Service service = new Service(new LoginClient() {
      @Override
      public boolean login(String id, String password) {
        return true;
      }
    });
    assertEquals("OK", service.execute());
  }
  
  @Test
  public void testExecuteWhenLoginFailure(){
    Service service = new Service(new LoginClient() {
      @Override
      public boolean login(String id, String password) {
        return false;
      }
    });
    assertEquals("NG", service.execute());
  }
}

まあ、だいぶすっきりしますね。

ただ、フィールドインジェクションを使っている場合は上記のような書き方はできない。とくに非privateなフィールドに対してインジェクトしている場合は、テストコードもGuiceを使うか、setterを別途用意しておくか、リフレクションを使う必要がある。privateフィールドに対するインジェクションはすっきりしていて好きなんですが。

そもそも、テスト時にオブジェクトの差し替えをしたいだけなら、DI使わずにモックライブラリ使えって感じですが。

何の話をしているのか混乱してきましたが、まーとにかく、簡潔で、理解し易く、かつテストしやすいコードになるように、うまくバランスをとらなきゃいけないですね。

追記(2010/03/21):
shinさんからのコメントより。こんな感じで書けるようです!

public class ServiceTestWithGuice {
  
  @Test
  public void testExecuteWhenLoginSuccess(){
    Injector injector = Guice.createInjector(new AbstractModule(){
      @Override
      protected void configure() {
        bind(LoginClient.class).toInstance(new LoginClient() {
          @Override
          public boolean login(String id, String password) {
            return true;
          }
        });
      }});
    Service service = injector.getInstance(Service.class);
    assertEquals("OK", service.execute());
  }
  
  @Test
  public void testExecuteWhenLoginFailure(){
    Injector injector = Guice.createInjector(new AbstractModule(){
      @Override
      protected void configure() {
        bind(LoginClient.class).toInstance(new LoginClient() {
          @Override
          public boolean login(String id, String password) {
            return false;
          }
        });
      }});
    Service service = injector.getInstance(Service.class);
    assertEquals("NG", service.execute());
  }  

ニコ動のランキングページから既に見た動画を隠すユーザスクリプト

ニコニコ動画(9)になって、ランキングページに沢山動画が表示されるようになって嬉しい。
しかし、既に見たものと、そうでないものの区別がつけにくい。
というわけで既に誰かが作っている気もするけど、なんとなく作ってみた。

以下のページからインストールできます。

すでに見たランキング動画をかくす君
http://userscripts.org/scripts/show/68547

すると、

これが・・・

こうなる!!!

全消し目指してがんばろう!