2011年12月22日木曜日

WYDKYDK が読めたよ! ワーイヾ(´ρ`)ノ゛ワーイ

Alistair Cockburn の本に出てくる変な綴りの言葉。意味は分かるけどなんて発音するのか、ずっと分からなかったけど、何の気に無しにググってたら見付けた。

なんかソフトウェア開発とは全然関係ない、オレゴン州の自治会か何かの議事録らしき文書だけど、WYDKYDK ("wid-kee-dik")とある。カタカナにするとウィドキーディクみたいな感じだろうか。

綴りが珍しいのは略語だからで、もとはこんな文。
What you don't know you don't know

簡単な英語だけど、構文が分からない人が意外と多い。順を追って見てみると、

You don't know it.
あなたはそれを知らない
↓
What you don't know.
あなたが知らないもの
↓
You don't know you don't know it.
あなたはそれを知らないという事を知らない
↓
What you don't know you don't know.
あなたが知らないという事を知らないもの
といった感じで、入れ子になってたりして、プログラマには親和性が高い気がするんだが。

ところで、現場ではこの WYDKYDK への認識が足りない人が、例えば WBS を作ったり作らせたりすると、困った事になる。

「これだけ詳細化したんだから、見積りは正確なはずだ」なんて思考様式なんだけど、そもそも認識されていない事を詳細化しようもないし、予測に組み込みようもない。

正解は、何らかの形で現実に実行してみて「知らない事」を「知らなかった事」に変えていく事なんだけど、下手な人はこの当たり前の事ができず、日程にバッファを組み込むといった程度の、稚拙な手に走ってしまう。 まあ現場ごとの文化とか、プロジェクトごとの事情とかもあるけどね。

2011年12月21日水曜日

小事の思案と割れ窓理論

葉隠と言えば、「武士道といふは死ぬ事と見付けたり…別に仔細なし。胸すわって進むなり。」で有名だけど、正直、自分みたいな「すくたれもの」には難しい。

だけど実は葉隠には、そんなムリなやつばかりじゃなくて、普通に実践的なアドバイスも結構ある。

『大事の思案は軽くすべし。/小事の思案は重くすべし。』

これについて自分は、やっぱり開発者で飯を食っているせいか、前半をリスク管理の問題、後半を品質管理の問題と捉えてしまう。

前半の『大事の思案』がなぜ軽いかについては、非常にクリティカルな事態に関しては、いざというときに判断に迷って対応が遅れたりしないように、普段から方針を定めておいて、即断即決できるように備えておくべきだという解釈になる。

後半の『小事の思案』がなぜ重いかについては、三島由紀夫が葉隠入門の中で、アリの穴から堤防が崩れるように、瑣末なことを軽んじる事によって生活の体系や重大な思想までもが台無しになると解説している。

で、もちろん前半も後半もどちらも大事なんだけど、どちらかというと自分なんかには後半の方が、より切実にピンと来る。(管理の比重が大きい人は前者かもしれない。)

例えば、コーディング規約なんかみたいなものでも、「ぎりぎり読めりゃいいじゃん」みたいに適当にしないで、きっちり律儀に守ってかないといけない。と自分は思う。

そこを疎かにするから、規約違反の放置->->内部品質の低下->->外部品質の低下->->製品品質の低下 ってな感じで、結局、また地獄になってくんじゃないかと。


で、この小さい事こそ大事ってのを、別角度から現代風に理論化したのが、割れ窓理論だと思う。

一般には、ジュリアーニ市長の時代にニューヨーク市が治安を回復したときにベースになった理論として有名なアレだけど、Wikipedia によるとこうなる

「建物の窓が壊れているのを放置すると、誰も注意を払っていないという象徴になり、やがて他の窓もまもなく全て壊される」

ささいに見える Duplicate code の放置が、コピペだらけの巨大な糞コードの山に繋がっていくという、まあ、ありがちなあれと同じじゃないだろうか。

で、この割れ窓理論をベースにした政策が「ゼロトレランス」だけど、開発現場に当てはめてみると、さしずめ Refactoring Mercilessly といったところか。

なんて事を考えていると、例えば、「途中return は是か非か」みたいな、一見不毛でアホらしい事についてムキになって議論するのも、けっこう大事というか、度を越さない範囲ならむしろ健全だとも思えてくる。もちろん自分もわりとムキになる方だ。

2011年12月20日火曜日

Spring 3.1 の Cache Abstraction

Spring 3.1 が GA になった。最後に Spring を触ったのはこれを書いたときだから、もう2年近く前になるけど、こんときは 3.0 が出た頃だった。あんまり進んでなかったのかも。

いろいろググってみると、キャッシュがどうかしたとか書いてあるので、寝る前にちょっと試してみる。

package spring.trial1.ex1;

import java.util.HashMap;
import java.util.Map;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

class Bar {
   Map<Integer, String> map = new HashMap<>();
   {
      map.put(1, "one");
      map.put(2, "two");
   }
   @Cacheable("bar")
   public String findString(int key) {
      System.out.printf("findString(%d) called%n", key);
      return map.get(key);
   }
}

public class Foo {
   public static void main(String[] args) {
   ApplicationContext ctx = new ClassPathXmlApplicationContext(
         "applicationContext.xml");
   Bar bean = ctx.getBean(Bar.class);

   System.out.println(bean.findString(1));
   System.out.println(bean.findString(2));
   System.out.println(bean.findString(3));
   System.out.println(bean.findString(1));
   System.out.println(bean.findString(2));
   System.out.println(bean.findString(3));
   }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
  <cache:annotation-driven />

  <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <property name="caches">
      <set>
        <bean
          class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
          p:name="bar" />
      </set>
    </property>
  </bean>
  <bean id="bar" class="spring.trial1.ex1.Bar"/>
</beans>
pom.xml の dependency に spring-context 3.1.0.RELEASE と、cglib 2.2.2 を指定して、ビルド→実行したら、以下のような結果が得られる。 (既に Maven Central に入ってるらしく、特に repository を指定したり、ローカルリポジトリに入れたりとかしなくても、普通に 3.1 がダウンロードされる。)
findString(1) called
one
findString(2) called
two
findString(3) called
null
one
two
null

findString の 中身が呼ばれるのは最初だけで、2回め以降はキャッシュが使われているらしいのが分かる。

■ 雑感

JavaConfig/JSR-330 で xml を書かずに済むようになったと思ってたけど、この <cache:annotation-driven> に関しては、やり方が分からない。

仕方なく、昔ながらのapplicationContext.xml を書いてしまった。もしかすると、ちゃんとしたやり方があるのかもしれないけど、部分的にアノテーションで部分的にXMLみたいにマジでなるとしたら、結構イヤかも。

2011年12月18日日曜日

gtk2hs で落書きしてみる

前回の書き込みで、gtk2hs の準備ができたので、もう一歩進めてみる。

10年以上前、Visual C++を使ってた頃、MFC のチュートリアルで Scribble というのがあった。これを、Haskell で試してみた。

ただし、MDI とか ドキュメントの保存とかは割愛して、とりあえず描画の API とマウスイベントのハンドリングだけを気にすることにした。

仕組みは簡単で、マウスボタンが押下されたときから放されるまでのマウスポインタの軌跡を記録し、ウィンドウ描画時に各点を線でつないでいくというもの。コードは以下のようになる。

import qualified Graphics.UI.Gtk as G
import Graphics.UI.Gtk.Gdk.GC
import Graphics.UI.Gtk.Gdk.Events
import Data.IORef
import Graphics.UI.Gtk.Gdk.Drawable

main = do
    G.initGUI
    w <- G.windowNew
    G.onDestroy w G.mainQuit

    da <- G.drawingAreaNew

    isDrawingIORef <- newIORef False
    figuresIORef   <- newIORef [[]]

    G.onExposeRect da (const $ do dw <- G.widgetGetDrawWindow da
                                  gc <- gcNew dw
                                  f <- readIORef figuresIORef
                                  drawFigure dw gc f)

    G.onMotionNotify da True $ \Motion {eventX = x, eventY = y}-> do
      isDrawing <- readIORef isDrawingIORef
      case isDrawing of
        True  -> do  modifyIORef figuresIORef (\f->addPoint f x y)
                     G.widgetQueueDraw da
        _     -> return ()
      return True
  
    G.onButtonPress da $ \Button {eventX = x, eventY = y}-> do
      writeIORef isDrawingIORef True
      modifyIORef figuresIORef (\f->[point x y]: f)  
      return True

    G.onButtonRelease da $ \Button {eventX = x, eventY = y}-> do
      writeIORef isDrawingIORef False
      modifyIORef figuresIORef (\f->addPoint f x y)
      return True
  
    G.containerAdd w da
    G.widgetShowAll w
    G.mainGUI

  where
    point x y = (round x, round y)
    addPoint figures x y = (point x y: head figures): tail figures
    drawFigure _ _ [] = return ()
    drawFigure d g (f:fs) = do {
      G.drawLines d g f; drawFigure d g fs }

Haskell 自体にまだ慣れていないので、いかにも未熟な感じだけど、イベント処理と描画の作法がなんとなく分かってきた。ネット上で API を調べる事にも、だんだん慣れてきた。

ただ、IORef というのが、なんだかしっくり来ない。結局、状態を持ってしまってる事になり、どうも気持ち悪い。初心者なりに勝手に純粋関数型言語に期待していたのは、もっと、引数と戻り値だけでつながっていく感じなんだけど、まだまだ勉強が足りないのかもしれない。

結果としては、マウスボタンの判別をしていないので、右でも左でも書けてしまうけど、一応思ったとおりに動作するものができた。スプーの絵もこんな風に書く事ができる(頭頂部の突起がやや足りない事を除けば、我ながらよく描けたと思う)。

gtk2hs と Glade の相性

gtk2hs を Glade と組み合わせて使うときのメモ。

Haskell の GUI プログラミングを試したくて、テキストボックスに文字列を入れて Enter を押したらラベルに表示されるような簡単なプログラム echo を、gtk2hs を使って書く実験をしてみた。

で、まず Glade というツールで GUI を定義する XMLを生成して、これを適当に書いた Haskell プログラムに読ませたら、こんなエラーが出た

$ ./echo 

(echo:4887): libglade-WARNING **: Expected <glade-interface>.  Got <interface>.

(echo:4887): libglade-WARNING **: did not finish in PARSER_FINISH state
echo: user error (Pattern match failure in do expression at echo.hs:8:5-12)

なんか Glade が生成した GUI定義XML の形式に問題があるっぽい。

調べてみると、Glade の出力形式には libglade と GtkBuilder の二通りの方法があり、gtk2hs が対応しているのは前者という事らしいのだが、ファイルを保存するときに libglade を選んでも、事態は全然変わらない。

さらに調べてみると Glade の3.8系 と 3.10系 で大きな違いがあって、出力XML に関してだと 3.10系では libglade 形式が無くなってるらしい。自分は、特に意識しないまま 3.10 を使っていた模様。

この2つのバージョン間で、保存時のダイアログに以下のような違いがある。

3.10系
3.8系
XML出力の違いは以下のような感じ
3.10系
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <requires lib="gtk+" version="2.24"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
    …
3.8系
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
  <widget class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
    …

というわけで、Glade の 3.8 系を使えば、gtk2hs がちゃんと処理できる形式の XML になる。

ただし、うちの環境で使ってる Fedora16 なんかでは、「ソフトウェアの追加/削除」から普通に Glade をインストールすると 3.10系が入って来てしまう。従って、この場合 3.8系を別途インストールする必要がある。

これは、ここから glade3-3.8.1.tar.xz を落としてきて、tar Jxf glade3-3.8.1.tar.xz -> ./configure -> make -> make install のようにすれば、/usr/local/bin/glade-3 が使えるようになる。

ちなみに、以下が実験で使った Haskellコード

module Main where

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
import Graphics.UI.Gtk.Gdk.Events as Evt

main = do
    initGUI
    Just xml    <- xmlNew "echo.glade"
    window      <- xmlGetWidget xml castToWindow "window1"
    onDestroy window mainQuit
    label       <- xmlGetWidget xml castToLabel "label1"
    entry       <- xmlGetWidget xml castToEntry "entry1"
    onKeyPress window $ \(Evt.Key _ eventSent _ _ _ _ _ _ name _) -> do
      keyPressHandler label entry name
      return eventSent
    widgetShowAll window
    mainGUI

keyPressHandler :: Label -> Entry -> String -> IO ()
keyPressHandler label entry "Return" = do
  name <- get entry entryText
  set label [labelText := name]
keyPressHandler _ _ _ = return ()
たかだかこれだけのコードを書くのに、かなり骨を折るが、いちおう動作する。

2011年12月14日水曜日

現場でよくある Agile 認識の齟齬

現場で、開発プロセスをどうしましょうかなんて話をしていると、対話の流れを止めて指摘するほどではないけど、認識の違いが引っかかって嫌なときがある。今日は、そんな事象のメモ。

★ Agile プロセスはユルいものだという認識

ユルいキビしい
なんか違うAgile全般ウォーターフォール
たぶんこうAdaptive Software Development、Crystal ClearXP、Scrum、ウォーターフォール

Agile が総じてユルいのではなく、実はユルいのもキビしいのもある。この見方は、確かものすご〜く前に読んだ Cockburn の本に載ってたものだけど、仮に読んでなかったとしても同じ認識になってた気がする。

ちなみに、同じ High-Discipline プロセスの中でも、XP と ウォーターフォールでは、前者が高効率・高難度で後者が低効率・低難度って事になると思う。寛容な方のグループの Crystal Clear なんかは、ユルい分、XP のような究極的な生産性は得られないけど、その分、負担が少ないって事になると思う。

★ 反復型といえば軽量プロセスだという認識

軽量重量
なんか違う反復型プロセス全般ウォーターフォール
たぶんこうAgile系UP系(AgileUPは除く)、ウォーターフォール

反復型のプロセスはどれもみな軽量だという思い込みもよくあるけど、たいてい「軽量プロセスはドキュメントを作らない」ってドクサも併発してるから、とりあえず反復型にしてみませんかなんて提案しても、習慣による心理的抵抗が大きい。

ウォーターフォール色の強い組織でも、ドキュメント体系を差し当たり尊重したまま、やや長めの期間の反復型プロセスに切り替えた上で、反復型が浸透してきた頃合いを見て、段々と期間を短くしたり文書や儀式を減らして、段階的に軽量にしていくというやり方もある。

2011年12月13日火曜日

Alloy 本

土曜に Amazon のお急ぎ便で注文したら、日曜の夜に届いた。 Alloy本
日曜は暇がなかったので、今日、帰宅してから早速 Alloy Analyzer をダウンロードして、テキストに取りかかる。(単なる jar なので java -jar ですぐ動く。)

下図の左側ペインのようにコードを書き込んで、[Execute]>[Show Metamodel]を実行すると、、、

、、、このようなダイアグラムが得られる。

このダイアグラムは、四角形の配置を変えたり、色やフォントを変えたりして、ある程度見せ方を好きなように変えられるようだけど、どうもダイアグラムはそれほど本質的なものではないっぽい。

まあ、1時間ほどいじっただけだし何も理解できてないけど、上の上の図で左側のペインに書いてある Alloy コードがやっぱり主役っぽい。
これに述語(pred)やファクト(fact)をちょっと書き足しては、それに対してアサーション(assert)をちょっと付け加えて、そしたら実行して右側のペインで確認して、って流れになる模様。そうだとしたら、どこか TDD に似ていてとっつきやすい。

とは言っても、関心の重点は実装ではなくむしろ仕様であって高い抽象レベルで仕様をモデリングする事が目的の技術らしい。
ちなみに、抽象度をレベル分けするときに、伝統的に分析レベル、設計レベル、実装レベルなんて言ったりするけど、Alloy と同じ雰囲気で、なおかつレベルの違う他言語を持ってくると、分析レベル:Alloy、設計レベル:LePUS3、実装レベル:Coq てな感じに当てはまるんじゃないかと思う。

まあ、いきなりいろんな事をやり始めなくても、とりあえず Alloy だけでも、例えば金融システムのビジネスルールとかに適用して、仕様バグを取り除くような事は、結構すぐにでも始められそうな予感。