2014年7月28日月曜日

Javaでクラス図(1クラスだけ!)を書いてみた

グラフを書くためのライブラリの一つに、Graphvizというものがあります。
調べてみたところ、クラス図を書くこともできるようなので、Javaのリフレクションを使ってクラス図を書いてみました。

まだ単体のクラスを出力できるだけですが、関連を出力できるようになるとなかなかおもしろそうです。

参照
Graphviz
UML Diagrams Using Graphviz DOT

2014年7月21日月曜日

C#でアプリケーション設定ファイルを拡張する方法

.NETには標準で設定ファイルを読み込むためのAPIが公開されています。
そのままでも単純なキー=値形式の定義できます。
カスタマイズするには、主に次の3つのクラスを使用します。
  • ConfigurationSectionクラス
  • ConfigurationElementクラス
  • ConfigurationElementCollectionクラス
ConfigurationSectionクラス
カスタマイズした設定項目のルートになるクラスです。
    class NetworkConfigurationSection : ConfigurationSection
    {
        [ConfigurationProperty("LocalHost")]
        public NetworkConfigElement LocalHost
        {
            get { return base["LocalHost"] as NetworkConfigElement; }
            set { base["LocalHost"] = value; }
        }

        [ConfigurationProperty("Networks")]
        public NetworkConfigElementCollection Networks
        {
            get { return base["Networks"] as NetworkConfigElementCollection; }
        }
    }


ConfigurationElementクラス
設定値をもつXMLエレメントに対応するクラスです。
    class NetworkConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("IPAddress")]
        public string IPAddress
        {
            get { return base["IPAddress"] as string; }
            set { base["IPAddress"] = value; }
        }
    }


ConfigurationElementCollectionクラス
複数のConfigurationElementをまとめるクラスです。
    class NetworkConfigElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new NetworkConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            NetworkConfigElement elm = element as NetworkConfigElement;
            if (null == elm) return null;
            return elm.IPAddress;
        }
    }



2014年7月14日月曜日

ちょっとしたツールを作るのに便利なPythonのcmdモジュールを使うための3ステップ

シンプルなコマンドラインツールが必要なとき、Pythonのcmdモジュールが有力な選択肢になります。ツールを実装するために必要な3ステップを紹介します。
  1. cmd.Cmdクラスを継承したクラスを作る
  2. do_XXXメソッドを定義する
  3. cmdloopメソッドを呼ぶ
cmd.Cmdクラスを継承したクラスを作る
特に言うべきことはありません。普通に継承するだけです。
class HelloCmd(cmd.Cmd):
...

do_XXXメソッドを定義する
メソッド名の"XXX"がコマンドになります。
パラメータを受け取るためのargパラメータがポイントです。
    def do_hello(self, arg):
        print 'hello, ' + str(arg)

cmdloopメソッドを呼ぶ
クラスをインスタンス化し、cmdloopを呼ぶとREPLがスタートします。
終了するにはCtrl-Cで落とします。
>>> HelloCmd().cmdloop()
(Cmd) hello Taro
Hello, Taro
(Cmd) Traceback (most recent call last):
  File "", line 1, in 
  File "/usr/lib/python2.7/cmd.py", line 130, in cmdloop
    line = raw_input(self.prompt)
KeyboardInterrupt

以下Tips。必須ではありませんが、知っておくと便利です。

2014年7月7日月曜日

Google GuiceのBest Practicesを訳してみた - Guiceがインスタンス化するクラスのコンストラクタはできるだけ隠蔽する

Guiceがインスタンス化するクラスのコンストラクタはできるだけ隠蔽する
次のシンプルなインターフェイスについて考える。
public interface DataReader {
 
  Data readData(DataSource dataSource);
}

一般的には、次のようなpublicクラスで実装することだろう。
public class DatabaseDataReader implements DataReader {
  
   private final ConnectionManager connectionManager;

   @Inject
   public DatabaseDataReader(
      ConnectionManager connectionManager) {
     this.connectionManager = connectionManager;
   }

   @Override
   public Data readData(DataSource dataSource) {
      // ... read data from the database
      return Data.of(readInData, someMetaData);
   }
}

ちょっと見ただけでは、このコードには何も問題がないようにみえる。不幸なことに、

よく知られたことだが、コンストラクタをpublicにするといいことは何もない。publicコンストラクタは、ソースとともに誤った使用方法を広めてしまう。誤った使用方法には次のようなデメリットがある。
  • リファクタリングが困難になる
  • インターフェースによる抽象化を破壊する
  • ソースとの結びつきを強める
何よりも悪いのは、コンストラクタを直接使用すると、Guiceによるインスタンス化を避けることになる。

正すためには、単純に実装クラスとコンストラクタの両方の可視性を制限すればよい。通常はパッケージプライベートを選択すればよく、次のような効果が得られる。
  • クラスを同じパッケージ内のModuleでバインドする
  • クラスを対象とする単体テストは、直接インスタンス化する
publicと@Injectはエルフとドワーフのようなものだと覚えておくとよい。彼らは協力することもできるが、独立して存在しているのだ。

参照
Keep constructors on Guice-instantiated classes as hidden as possible.

2014年6月30日月曜日

Google GuiceのBest Practicesを訳してみた - Moduleでは条件分岐を避けよ

Moduleでは条件分岐を避けよ
Moduleに変化する部品をもたせることと、異なった環境で異なった動作をするように定義するのは推奨されない。

public class FooModule {
  private final String fooServer;

  public FooModule() {
    this(null);
  }

  public FooModule(@Nullable String fooServer) {
    this.fooServer = fooServer;
  }

  @Override protected void configure() {
    if (fooServer != null) {
      bind(String.class).annotatedWith(named("fooServer")).toInstance(fooServer);
      bind(FooService.class).to(RemoteFooService.class);
    } else {
      bind(FooService.class).to(InMemoryFooService.class);
    }
  }
}
条件分岐自身にはそれほど問題はない。しかし、設定がテストされていない時に問題は現れる。この例では、InMemoryFooServiceは開発環境で使用され、RemoteFooServiceは本番環境で使用される。しかし、この特定の状況をテストしていないと、RemoteFooServiceが統合されたアプリケーションと動作するかは保証できない。

このことからわかるのは、アプリケーションの設定項目は最小限にすべきだということだ。開発環境と本番環境を別々のModuleへ分割すると、本番コードの全てがテストされているのを証明するのはより容易になる。この例では、FooModuleをRemoteFooModuleとInMemoryFooModuleへ分割する。これはまた、製品版のクラスがテストコードに依存しなくなる効果もある。

参照
Avoid conditional logic in modules

2014年6月23日月曜日

Google GuiceのBest Practicesを訳してみた - Providerでの入出力に注意せよ

Providerの入出力に注意せよ
Providerは便利だが、以下の機能を欠いている。
  • Providerはチェック例外を宣言しない。特定のエラーからの復帰しなければいけないコードを書いているのなら、TransactionRolledbackExceptionはcatchできない。ProvisionExceptionにより、一般的な生成エラーから復旧でき、その原因を列挙することもできる。しかし、それらの原因を指定することはできない。
  • Providerはタイムアウトをサポートしない。
  • Providerはリトライ戦略を定義しない。値が有効でないとき、何度もget()を呼ぶと、何度も失敗することになるだろう。
ThrowingProvidersは、ExceptionをthrowするProviderを実装するGuice拡張だ。それはエラーのスコープを定義でき、それにより、リクエストやセッションで一度だけエラーが発生する。

参照
Be careful about I/O in Providers 

2014年6月16日月曜日

Google GuiceのBest Practicesを訳してみた - Moduleは高速で副作用がないほうがよい

Moduleは高速で副作用がないほうがよい
Guiceのモジュールは、設定にXMLファイルではなく、Javaコードを使用する。Javaは親しみやすく、IDEの機能を活用でき、リファクタリングにも対応できる。

しかし、Javaの力はコストももたらす。つまり、Moduleでやりすぎてしまうのだ。GuiceのModuleでは、データベースへ接続し、HTTPサーバーを起動することもできる。ダメだ!Moduleで困難な仕事をすると、問題が発生する。

  • Moduleは起動するが、終了しない ― データベース接続を開いたとき、その接続を閉じるフックはない
  • Moduleはテストされるべき ― 実行時にデータベースへ接続すると、単体テストが難しくなる
  • Moduleはオーバーライドできる ― GuiceのModuleはオーバーライドをサポートしており、製品版とテスト版や軽量版と置換えができる。製品版の機能がモジュール実行の一部として実装されたとき、そのようなオーバーライドは効果がない。
Module自身で実行するよりも、適切な抽象レベルのインターフェイスを定義する。アプリケーションでは、このようなインターフェイスを使用できる。
public interface Service {
  /**
   * Starts the service. This method blocks until the service has completely started.
   */
  void start() throws Exception;

  /**
   * Stops the service. This method blocks until the service has completely shut down.
   */
  void stop();
}
Injectorを生成した後でServiceを開始し、アプリケーションの起動を完了する。また、アプリケーションが停止したときに、リソースを解放するシャットダウンフックを追加する。
  public static void main(String[] args) throws Exception {
    Injector injector = Guice.createInjector(
        new DatabaseModule(),
        new WebserverModule(),
        ...
    );

    Service databaseConnectionPool = injector.getInstance(
        Key.get(Service.class, DatabaseService.class));
    databaseConnectionPool.start();
    addShutdownHook(databaseConnectionPool);

    Service webserver = injector.getInstance(
        Key.get(Service.class, WebserverService.class));
    webserver.start();
    addShutdownHook(webserver);
  }

参照
Modules should be fast and side-effect free