Page List

Search on the blog

2012年8月31日金曜日

デザインパターン(7) Builder

まえおき
以下の書籍でBuilderパターンについて勉強した。


まとめ
  • 構造を持ったインスタンスを組み上げるときに利用する。
  • Builder、ConcreteBuilder、Director、Clientによって構成される。
疑問点
  • Builderパターンはオブジェクト生成のためのデザインパターン。Factory Methodパターンと何が違うのか?
Factory Methodパターンは異なる種類のオブジェクトを生成するときのパターンで、Builderパターンはオブジェクトの生成手順をきれいにまとめるためのパターンなので、目的がまったく違う。以下のような記述が参考サイト[1]にあった。
The rule of thumb I noticed after some time was the following: Consider a restaurant. The creation of "Todays Meal" is a factory pattern, because you tell the kitchen "get me todays meal" and the kitchen/factory decides what object to generate. based on hidden critereas.

The builder appears if you ordered a custom pizza. In this case, the waiter tells the chef/builder "I need a pizza, add cheese, cheese, onions and bacon to it!". Thus, the builder exposes what attributes the generated object should have, but hides how to set them.
  • パッと見、Template Methodパターンと同じに見えるんだけど、何が違うのか?
これは明確な違いが自分ではよく分からなかった。Builderパターンの方が抽象度や独立性が高いような気がする。Template Methodパターンだと親-子クラスという関係があるし、同じ部品を別の処理フローでテンプレート化したいときに処理フローの数だけ親-子のペアを毎回書かないといけないような気がする。それに比べて、Builderパターンの方はdirector-builder間の関係が移譲なのでそれぞれの独立性が高いのかなと思う。参考サイト[2]になんとなくしっくりくるような説明があった。

Template method is really just a way to define some methods that each subclass must define.

The builder pattern is used to construct a more complex object.

Lets say that we want to construct different Saab (car brand) models. Each model has different engines,lights etc.

If we were to use the template method pattern we had to create one class for each single combination of cars or use some nasty inheritance hierarchies. A lot of those methods would also contain duplicate code.

With the build pattern we can instead take different parts and compose those together to a complete car. Hence we can reuse a engine for every model if needed and we can also customize each part of the car.

その他
  • 書籍の練習問題がおもしろかった。久しぶりにswingでGUIを書いた。今度簡単なゲームか何か書いてみようかな。
参考サイト
  1. When would you use the Builder Pattern? [closed]
  2. Differences between builder pattern vs template method

2012年8月29日水曜日

Spring Framework -Aspect Oriented Programming-

Spring特集第3弾。Aspect Oriented Programming(以下AOP)について書きます。

AOPとは?
共通的な機能を後から付け加える仕組みです。 具体的に言うと、
  1. ロギング
  2. キャッシング
  3. セキュリティ
  4. 認証
などの共通的に利用される機能をビジネスロジックから切り離して、後から横断的に追加できるような仕組みのことです。

Before AdviceとAfter Advice
まずは、一番簡単なBefore AdviceとAfter Adviceの例を書きます。Adviceとは、AOPの用語で追加する共通処理のことです。何をやっているかは以下のサンプルを見れば一目瞭然です。
// src/cc/co/goodpreparations/Person.java
package cc.co.goodpreparations;

public class Person {
    public void smile() {
        System.out.println("s/he is smiling.");
    }
    
    public void cry() {
        System.out.println("s/he is crying.");        
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- // src/Beans.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

    <aop:config>
        <aop:aspect id="myaop" ref="aoptest">
            <aop:pointcut id="all"
                expression="execution(* cc.co.goodpreparations.*.*(..))" />            
            <aop:pointcut id="smile"
                expression="execution(* cc.co.goodpreparations.*.smile(..))" />
            <aop:before pointcut-ref="all" method="beforeAdvice" />
            <aop:after pointcut-ref="smile" method="afterAdvice" />
        </aop:aspect>
    </aop:config>

    <bean id="person" class="cc.co.goodpreparations.Person">
    </bean>

    <bean id="aoptest" class="cc.co.goodpreparations.AOPTest" />
</beans>
// src/cc/co/goodpreparations/AOPTest.java
package cc.co.goodpreparations;

import org.aspectj.lang.ProceedingJoinPoint;

public class AOPTest {

    public void beforeAdvice() {
        System.out.println("@This is a before advice.");
    }

    public void afterAdvice() {
        System.out.println("@This is an after advice.");
    }
}
// src/cc/co/goodpreparations/Main.java
package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainApp {
   public static void main(String[] args) {
      ApplicationContext context = 
             new ClassPathXmlApplicationContext("Beans.xml");

      Person person = (Person) context.getBean("person");
      
      person.smile();
      System.out.println("---------------------------------");
      person.cry();
   }
}
ポイントとなる設定ファイルの書き方を説明します。AOPの設定は、aop:config要素の中に書きます。aop:aspectの中に、Aspectの設定を書きます。Aspect=Advice + (処理を埋め込む場所)です。ちなみに(処理を埋め込む場所)のことをPointcutと呼びます。
Aspect = Advice + Pointcut
と覚えておくとよいです。設定ファイルの説明に戻ります。簡単に設定箇所を纏めると以下のようになります。
  1. aop:aspect要素のid属性にAspectのIDを、ref属性にAdviceが定義されたクラスのBean IDを書きます。
  2. aop:pointcut要素のid属性にPointcutのIDを、expression属性に共通処理を埋め込みたい場所を正規表現で書きます。
  3. aop:before要素のpointcut-ref属性に使用するPointcutのIDを、method属性に1.で指定したBeanのメソッド名を書きます。(このメソッドがAdviceになる)
  4. aop:after要素もaop:before要素と同様に書きます。
実行結果は、以下のようになります。
@This is a before advice.
s/he is smiling.
@This is an after advice.
---------------------------------
@This is a before advice.
s/he is crying.
このようにAOPを利用すると、任意の処理を任意の場所に後から埋め込むことができます。

その他のAdvice
Before Advice、After Adviceの他にもAdviceはあります。以下にAdviceの種類を纏めます。
種類 用途
Before Advice 処理をメソッドの実行前に追加したい場合。
After Advice 処理をメソッドの実行後に追加したい場合。
Around Advice 処理をメソッドの実行前後に追加したい場合。
After-returning Advice 処理をメソッドが正常終了した後に追加したい場合。
After-throwing Advice 処理をメソッドが異常終了した(例外がスローされた)後に追加したい場合。
このように、どのタイミングでAdviceを挿入したいかに応じて様々な種類のAdviceを使うことができます。それぞれのAdviceの使用方法はSpringSource.orgのreferenceを参照してください。

まとめ
DIがオブジェクト間の依存性を外部に掃き出し、それを後から注入する機能だと考えると、AOPはオブジェクトと共通機能の依存性を外部に掃き出し、それを後から注入する機能だと考えることができます。AOPを使うことで機能追加/変更に強いソースコードを書くことができます。

2012年8月28日火曜日

Spring Framework -Dependency Injection-

Spring FrameworkのDependency Injection(以下DI)の機能について書きます。

"bought a bracelet"

DIとは?
いい例が浮かばなかったのですが、例えば以下のようなプログラムを考えてみてください。
// src/cc/co/goodpreparations/Investor.java
package cc.co.goodpreparations;

public class Investor {
    public void buy() {
        GoodCalculator calculator = new GoodCalculator();
        
        // do something

    }
}
// src/cc/co/goodpreparations/GoodCalculator.java
package cc.co.goodpreparations;

public class GoodCalculator {

    public double calculate() {
        // do difficult calculations
        
        return 0;
    }
}
何かに投資するInvestorクラスがあって、投資するために複雑な計算をします。その計算にGoodCalculatorというクラスを使おうというシチュエーションです。
上のように書いてしまうと、InvestorクラスはGoodCalculatorクラスが無いと使用できません。つまり、InvestorクラスはGoodCalculatorクラスに依存しています。一般にクラスが別のクラスに依存していると、

  • クラスの独立性が低いため、再利用しにくい
  • 独立したテストが行い辛い
というデメリットがあります。

Investorの例に戻ります。もし、今使われているGoodCalculatorより優秀なBetterCalculatorが開発されて、BetterCalculatorを使おう。となったらどうでしょう?GoodCalculatorを使っている場所をすべて書き直さなければなりません。使われている場所が1ヶ所だけならいいですが、何10箇所、何100箇所もあったら大変です。

以下のようにするとどうでしょうか?
// src/cc/co/goodpreparations/Calculator.java
package cc.co.goodpreparations;

public interface Calculator {
    public double calculate();
}
// src/cc/co/goodpreparations/GoodCalculator.java
package cc.co.goodpreparations;

public class GoodCalculator implements Calculator {

    @Override
    public double calculate() {
        // do difficult calculations
        
        return 0;
    }
}
// src/cc/co/goodpreparations/Investor.java
package cc.co.goodpreparations;

public class Investor {
    Calculator calculator = null;
    
    public Investor(Calculator calculator) {
        this.calculator = calculator;
    }
    
    public void buy() {
        // do something with the given calculator

    }
}
こうすると、Investorは中身がどのように実装されているかは知りませんが、Calculatorインターフェースを実装した何らかのクラスを使うことになります。GoodCalculatorがBetterCalculatorに変更されても、そもそもGoodCalculatorというクラス名を直接ソースコード上に書いていないので変更は生じません。もしBetterCalculatorがcalculateメソッドを実装していなければ、Adapterデザインパターンを使ってCalculatorインターフェースを実装するクラスを作ればいいでしょう。
さて、実際にどのCalculatorを使用するかですが、設定ファイルに記述してSpring Frameworkに具体的なクラスを注入してもらいましょう。このように、ソースコードからクラス間の依存性を取り除き、外部から依存性を注入することをDIと呼びます。以下では、
  1. コンストラクタによるDI
  2. setterによるDI
の2種類のDIを説明します。

コンストラクタによるDI
全章の後半で導入したように、コンストラクタで使用するCalculatorを渡す場合は以下のようにすればDIができます。ポイントはXML設定ファイルのinvestorのbean定義のconstructor-arg要素の部分です。この部分でコンストラクタに渡す具体的なクラスを指定しています。注入する具体的なクラスを変えたい場合は、この部分を変更するだけでOKです。
// src/cc/co/goodpreparations/Main.java
package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        
        Investor investor = (Investor)context.getBean("investor");
        investor.buy();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
   <bean id="goodCalculator" class="cc.co.goodpreparations.GoodCalculator"></bean>
   <bean id="investor" class="cc.co.goodpreparations.Investor">
            <constructor-arg ref="goodCalculator"/>
   </bean>
</beans>
setterによるDI
同様にsetterを利用して、具体的なクラスを注入することができます。
// src/cc/co/goodpreparations/Investor.java
package cc.co.goodpreparations;

public class Investor {
    Calculator calculator = null;
    
    public void setCalculator(Calculator calculator) {
        this.calculator = calculator;
    }
    
    public void buy() {
        // do something with the given calculator
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    
   <bean id="goodCalculator" class="cc.co.goodpreparations.GoodCalculator"></bean>
   <bean id="investor" class="cc.co.goodpreparations.Investor">
           <property name="calculator" ref="goodCalculator"/>
   </bean>
</beans>
setterを利用したDIでは、まず引数なしコンストラクタでInvestorクラスをインスタンス化した後、setterでcalculatorを設定しています。使用するcalculatorを変えたい場合は、investorのbean定義のproperty要素のref属性を変更すればOKです。

どちらを使うべきか?
コンストラクタを使用したDIとsetterを使用したDIを説明しました。「で、どっち使えばいいの?」という疑問が浮かぶと思いますが、必須の依存性を注入したい場合はコンストラクタによるDI、オプショナルな依存性を注入する場合はsetterを使用したDI、という使い分けをするのが一般的です。必要に応じて両方同時に使うことも可能です。

Spring Framework -オブジェクトの生成-

Spring Frameworkについて、基本的な機能を簡単に紹介します。
  1. オブジェクトの生成
  2. Dependency Injection
  3. Aspect Oriented Programming
の3つの機能を3回のpostに分けて書きます。まずは、オブジェクトの生成から書きます。

XML設定ファイルを利用したオブジェクトの生成
// src/cc/co/goodpreparations/Main.java 

package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        
        Person person =  (Person)context.getBean("person");

        person.saySomething();
    }
}
// src/cc/co/goodpreparations/Person.java

package cc.co.goodpreparations;

public class Person {
    private String message;

       public void setMessage(String message){
          this.message  = message;
       }

       public void saySomething(){
          System.out.println(message);
       }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="person" class="cc.co.goodpreparations.Person">
       <property name="message" value="Hello!"/>
   </bean>
</beans>
XMLファイルにBeanのidとクラスのマッピングを記述します。使用者はidを指定して対応するクラスのオブジェクトを生成します。普通にnewすればいいんじゃない?と思う人もいるかもしれませんが、上記のようにSpringが提供するコンテナからオブジェクトを生成することで、DI/AOPといった機能が使えるようになります。

フィールド値の設定
オブジェクトを生成するときにフィールドの初期値を設定出来ます。同一のクラスから生成されているがフィールド初期値が異なるインスタンスは、異なるBean idをつけることで区別できます。XMLのproperty要素内でフィールドの設定をしていますが、ここで設定するフィールドには必ずsetter(この例の場合はPerson#setMessage)を定義しなければいけません。(JavaBeansなので当然ですが。)
// src/cc/co/goodpreparations/Main.java
package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        
        Person taro =  (Person)context.getBean("taro");
        taro.saySomething();
        
        Person hanako =  (Person)context.getBean("hanako");
        hanako.saySomething();
        
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="taro" class="cc.co.goodpreparations.Person">
       <property name="message" value="ぼく太郎!"/>
   </bean>
   <bean id="hanako" class="cc.co.goodpreparations.Person">
       <property name="message" value="わたし花子!"/>
   </bean>
</beans>
と、このように太郎くんと花子さんを作れます。

Beanのスコープ
Beanを生成するときに、そのスコープを設定することができます。
  1. singleton
  2. prototype
  3. request
  4. session
  5. global-session
3.-5.はWebアプリケーションに特化したものなので、ここでは1.と2.のみを説明します。まずはsingleton
// src/cc/co/goodpreparations/Main.java
package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        
        Person person = (Person)context.getBean("person");
        person.setMessage("Goodbye!");
        person.saySomething();
        
        Person person2 = (Person)context.getBean("person");
        person2.saySomething();
        
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="person" class="cc.co.goodpreparations.Person" scope="singleton">
       <property name="message" value="Hello!"/>
   </bean>
</beans>
scope="singleton"のところに注目です。名前から想像がつくように、scopeをsingletonにすると同一のidを持つBeanはアプリケーション上で一つしか作られません。つまり、context#getBeanは毎回同じインスタンスの参照を返します。実行結果は
>Goodbye!
>Goodbye!
となります。ちなみにデフォルトのスコープはsingletonです。scope属性を明記しなかった場合、そのBeanのスコープはsingletonになります。
次に、prototypeを試してみます。先ほどと同様の例でscope属性のみをprototypeに変更すると、実行結果は
>Goodbye!
>Hello!
になります。スコープをprototypeにすると、毎回異なるインスタンスが生成されます。

コールバック関数の指定
Beanが生成されたとき/破棄されるときに、何か処理をしたい場合はコールバック関数を指定することができます。
// src/cc/co/goodpreparations/Main.java
package cc.co.goodpreparations;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        
        Person person = (Person)context.getBean("person");
        person.saySomething();
        person = null;
         ((AbstractApplicationContext) context).registerShutdownHook();
    }
}
// src/cc/co/goodpreparations/Person.java
package cc.co.goodpreparations;

public class Person {
    private String message;

       public void setMessage(String message){
          this.message  = message;
       }

       public void saySomething(){
          System.out.println(message);
       }
       
       public void initFunc() {
           System.out.println("A Person Bean is created!");
       }

       public void destroyFunc() {
           System.out.println("A Person Bean is destroyed!");
       }
}
<?xml version="1.0" encoding="UTF-8"?>
<!-- src/Beans.xml -->

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   <bean id="person" class="cc.co.goodpreparations.Person" scope="singleton"
            init-method="initFunc" destroy-method="destroyFunc">
       <property name="message" value="Hello!"/>
   </bean>
</beans>
XMLファイルのbean要素のinit-method属性、destroy-method属性にて、コールバックしたい関数を指定することができます。これは便利です。mainメソッド内でAbstractApplicationContext#registerShutdownHookを呼んでいますが、これを呼ぶことで確実にBeanが破棄されdestroy-methodで指定した関数が呼ばれるようにしています。


2012年8月26日日曜日

デザインパターン(6) Prototype

まえおき
Prototypeパターンの勉強をしました。書籍だけで大まかなイメージは掴めましたが、「複雑な過程を経てインスタンス生成されるものを簡単に作れる」という例があると、より分かりやすいと思います。


まとめ
  • プロトタイプと呼ばれるオブジェクトをコピーすることで、複雑な過程を経てインスタンス化されるオブジェクトの生成を簡単にすることができる。
  • コピーされたオブジェクトは、プロトタイプとは独立しているので、必要に応じてフィールドの内容を変更することができる。(参考サイト[2,3])
疑問点
  • 書籍には「複雑な過程を経てインスタンス生成されるものを簡単に作れる」という例が無くて具体的なイメージが湧かなかった。参考サイト[1]を見て複雑な過程(もしくは時間のかかる処理)を経て作られるインスタンスのイメージが掴めた。

その他
  • cloneメソッドとCloneableインターフェースに関して学んだ。cloneメソッドはshallow copyなので対象オブジェクトに参照型のフィールドが存在する場合は本当にshallow copyでよいのか注意する。
参考サイト
  1. TECHSCORE
  2. THE CODE PROJECT
  3. javapapers

2012年8月22日水曜日

Ubuntu12.04のキーボード設定


"Sunset on Beach Stage"

キーボードの設定を変えました。

HUDの無効化
デフォルトでは、Altキーを押すとHUDが立ち上がります。Altキーを押す度にウィンドウが開いてフォーカスが移り面倒なので、このサイトを参考にHUDを無効化しました。

右Altキーの設定
やっぱり右Altキーは欲しいです。ということで、以下の手順で変換キーを右Altキーに割り当てました。
  1. コマンドラインにxevと入力する。
  2. 変換キーを押すと、変換キーのkeycode(100)が表示される。
  3. ~/.Xmodmapに以下を設定
keycode 100 = Alt_L
add mod1 = Alt_L

2012年8月17日金曜日

GETとPOSTの違い

GETとPOSTの違いについて勉強しました。

HTML specificationsによる定義/推奨
■ GET
  • データはURLに含まれる
  •  idempotentな(永続的な効果をもたらさない、副作用をもたらさない)処理を行うときに使うことが推奨されている。 ⇒データを取得するときに使用する。

■ POST
  • データはmessage body部分に含まれる。 
  • idempotentではない処理を行うときに使うことが推奨されている。 ⇒データのソート、更新、商品の注文、Emailの送信など

実用的な違い/留意点
  • GETはサーチエンジンのクローラーに踏まれる可能性があるので、副作用のある処理をGETで処理すると、不用意にデータを消去されたり、更新されたりする可能性がある。
  • GETリクエストに対するレスポンスはキャッシュできる)場合が多い。POSTリクエストに対するレスポンスがキャッシュされることはほとんどない。
  • GETはURLをブックマーク保存できる
  • GETはURLにデータが載るので、Webサーバやプロキシサーバーのログに残る。リンクで他ページに遷移したときにRefererのところにも載る。⇒パスワードなどのセンシティブな情報を送る場合はGETは避けるべき。
  • GETにはASCII文字以外を含めない方がいい。(原則上は扱えないが、実用上は扱える。)マルチバイト文字をクエリーに含む検索処理の場合は、POSTを使う方が(idempotent=GETの基本精神に反するが)ベター。
  • GETはPOSTより送信できる情報量が少ない
  • ブラウザの「戻る」ボタン⇒POSTは警告を上げるGETはそのまま遷移
(※)ちなみにキャッシュはいろいろな種類があって、
  • ブラウザーキャッシュ
  • プロキシーキャッシュ
  • ゲートウェイキャッシュ(リバースプロキシー)
などがあります。


まとめ
基本的には、データのREAD処理の場合はGETを使い、CREATE/UPDATE/DELETE処理の場合POSTを使えばよいと思います。あとはセキュリティの観点からセンシティブな情報を送信する場合はPOSTを使うべきです。

参考サイト
  1. Methods GET and POST in HTML forms - what's the difference?
  2. Get vs Post
  3. CACHING TUTORIAL

2012年8月11日土曜日

Reading Java RunTime Source Code (3)

java.util.Stackのソースを読みました。実装は非常にシンプルで分かりやすかったです。気になったところだけ以下にまとめます。

class Stack<E> extends Vector<E>
Eはジェネリクスの型変数の宣言。クラス宣言部を見て分かるように、StackはVectorを継承しています。

synchronized
Stackでは以下のメソッドが定義されています。
  • push
  • pop
  • peek
  • empty
  • search
Stackから連想する処理は一通り実装されています。気になったのは、synchronized。赤字のメソッドのみsynchronized句が指定されています。synchronizedする/しないの違いが気になったので、考えてみました。

public synchronized E pop() {
    E obj;
    int len = size();
    
    obj = peek();
    removeElementAt(len - 1);

    return obj;
}

public synchronized E peek() {
    int len = size();

    if (len == 0)
        throw new EmptyStackException();
    
    return elementAt(len - 1);
}

まずpopですが、3行目と6行目の間で別のスレッドがpopを実行した場合に、ArrayIndexOutOfBoundsExceptionが発生する可能性があります。これを防止するためにオブジェクトにロックをかけていると考えられます。同様にpeekの場合も2行目と7行目の間に別スレッドからpopを実行されるとArrayIndexOutOfBoundsExceptionが発生する可能性があります。

searchは少し違います。ArrayIndexOutOfBoundsExceptionがthrowされそうな箇所は見当たりません。
public synchronized int search(Object o) {
    int i = lastIndexOf(o);
    
    if (i >= 0) {
        return size() - i;
    }
    return -1;
}
searchの仕様は、対象のオブジェクトがStack内に存在すればそのindexを1-basedで返します。存在しなければ-1を返します。もし、2行目と5行目の間にpopが別スレッドから実行されると、仕様と異なる値(0, -2, -3, ..)が返される可能性があります。これを防止するためにsynchronizedを設定していると考えられます。ただし、pushにはsynchronizedが指定されていないので、searchの実行中に別スレッドからpushが実行される可能性はあります。(スレッドがオブジェクトのロックを気にするのは、synchronizedブロックを実行しようとする場合のみなので。)しかし、これは返されるindexが1インクリメントされるだけなので問題はないです。

synchronizedされていないpush、emptyについては上記で見てきたような問題とは無関係ですし、特に他に同期させるべき場面も思い浮かばないので、無くてよいのかなぁという感じです。簡単に必要ないと言いましたが、本来は、他の処理から割り込ませないような場面がないことと、他のsynchronizedが付いている処理を邪魔する可能性がないことを保証した上で、synchronizedは付けなくていいという結論を出さないといけないんですよね。マルチスレッドを考慮したクラス設計は難しいですね。。

2012年8月7日火曜日

Reading Java RunTime Source Code (2)

java.lang.Booleanのソースを読みました。おもしろかったところだけ簡単にまとめておきます。

getPrimitiveClass
Class<Boolean> TYPE = Class.getPrimitiveClass("boolean");
でbooleanのラッパークラスのClassインスタンスを取得します。 Class.getPrimitiveClassメソッドの定義を見ると、以下のようにnative修飾子がついています。
static native Class getPrimitiveClass(String name);
native修飾子は、そのメソッドの実装が Java 以外の言語で記述されてコンパイルされていることを示します。ということで現段階で深堀りするのはやめておきます。

serialVersionID
シリアライズするときに使うもの。シリアライズしたオブジェクトをreadしようとしたとき、 クラスの仕様が変更されていたら不都合があるので、この番号でクラスの仕様が等しいことを確認するようです(http://www.mkyong.com/java-best-practices/understand-the-serialversionuid/)。

hashCode
これはもともとObjectクラスで定義されており、Booleanクラスでオーバーライドされています。 何のために使うのかというと、
Returns a hash code value for the object. This method is
supported for the benefit of hash tables such as those provided by java.util.HashMap(コメントより抜粋)
らしいです。

getBoolean
System.getProperty(property_name) で取得したプロパティ値をBooleanにキャストして返します。
プロパティはこんなのがあるらしいです。
http://docstore.mik.ua/orelly/java/fclass/ch10_02.htm 

上記のページに載っているプロパティを見てもいまいち使い方が分からなかったので、値がtrueになるプロパティがもともと定義されているかどうか以下のコードを書いて調べました。
public class Main {
 public static void main(String[] args) {
  Properties propaties = System.getProperties();
  Set<Entry<Object, Object>> pset = propaties.entrySet();
  for (Entry<Object, Object> entry : pset) {
   String pkey = (String)entry.getKey();
   String pvalue = (String)entry.getValue();
   if ("true".equalsIgnoreCase(pvalue))
    System.out.println(pkey);
  }  
 }
}
無かったです。。おそらく開発者がアプリ上で設定した値の真偽を調べるために使われるものだと考えられます。こんな風に。
public class Main {
 public static void main(String[] args) {
  System.setProperty("logined", "true");
  System.setProperty("network-connected", "true");
  
  System.out.println(Boolean.getBoolean("logined"));
  System.out.println(Boolean.getBoolean("network-connected"));
 }
}

2012年8月5日日曜日

デザインパターン(5) Singleton


まえおき
Singletonです。説明が分かりやすく、かつ、例題もどのような場面で使うのかをしっかり抑えている内容だったため簡単に理解できました。


まとめ
  • そのクラスのインスタンスがシステム上で1個しか存在しないことを保証する。
  • コンピュータそのものを表現したクラス、システム設定を表現したクラス、ウインドウシステムを表現したクラスなどが使用対象となる。

疑問点
  • 特になし

その他
  • コンストラクタをprivateにするというのは大事なポイント。意図を理解していない開発者に対象オブジェクトを普通にnewされると困ったことになってしまうので。
  • static フィールドが初期化されるタイミングも重要。
  • マルチスレッドに関する問題がおもしろかった。synchronizedについて勉強しなおした。

2012年8月4日土曜日

Recursive Grep

ソースコードにTODOと書いて、後からTODOと書いている箇所をまとめてgrepしようと思って、
$ grep TODO *
としてみましたが、ヒットせず。
$ grep TODO *
$ grep TODO */*
$ grep TODO */*/*
とかやっていくと出るんですけど、もっと楽にできないかなと思い、
find . -name "*" | grep TODO
とかやってみました。
なぜかヒットせず。。いやこれだとカレントディレクトリ配下にTODOという文字列を含むファイルorサブディレクトリがないか検索してるだけだ。findでヒットしたファイルをgrepの引数として渡してあげる方法はないんだろうか・・・。 ありました。xargsを使えばよさそうです。
find . -name "*" | xargs grep TODO
でやりたいことができました。ちなみにxargsは、extended argumentsの略らしいです。

2012年8月3日金曜日

Reading Java RunTime Source Code (1)

はじめに
コードを読む能力/綺麗なコードを書く能力を鍛えるため、Java RunTimeのソースコードを読んでいくことにした。まずは、動的な解析をするための環境を整えた。

ソースの取得
ソースコードが無かったので、以下のコマンドで取得した。
$ sudo apt-get install openjdk-7-source 
これで、/usr/lib/jvm/java-7-openjdk-commonにsrc.zipというzipファイルができる。ここにソースファイルが格納されている。このsrc.zipのソースをコンパイルして、jarにまとめたものが/usr/lib/jvm/java-7-openjdk-i386/jre/lib/rt.jar。

開発環境構築
~/codereading/javaというディレクトリを作成し、そこにsrc.zipを解凍。rt.jarをコピーして持ってきて、jarファイルの中身を取り出して、bin配下に格納した。最終的に以下のようなディレクトリ構成にした。
- codereading
    - java
          - rt.jar
          - src
              - (ソースコード)
          - bin
              - (クラスファイル)
              - META-INF
                  - MANIFEST.MF

/usr/lib/jvm/java-7-openjdk-i386/jre/lib/rt.jarの実体は削除して、上記のrt.jarへのシンボリックリンクにした。これで、ソースコードを適当にイジってコンパイルしてjarファイルを更新すれば、JVMがロードするクラスの内容も変わるはず。

確認
java.lang.Boolean.javaを少し変えて
public Boolean(boolean value) {
    this.value = value;
    System.out.println("Boolean Constructor Invoked.");
}
コンパイル。
~/codereading/java$ javac src/java/lang/Boolean.java -d bin
jarファイルも更新。
~/codereading/java/bin$ jar cmf META-INF/MANIFEST.MF test.jar *
~/codereading/java/bin$ mv test.jar ../rt.jar
テスト用のクラスを書く。
public class Main {
    public static void main(String[] args) {
        System.out.println("Start.");
        Boolean bool = new Boolean(true);
        System.out.println("End.");
    } 
}
以下が実効結果。
Boolean Constructor Invoked.
Boolean Constructor Invoked.
Start.
Boolean Constructor Invoked.
End.
よし、これでソースを読むための最低限の準備はできた!