--/--/--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2008/10/18

Java で動的にプロパティの指定を行う

Java にはプロパティというものが言語仕様上には存在しない(たぶん)。しかし一般的には、public な setter/getter を持つ属性がプロパティとして扱われている。最も、属性の名前をプロパティ名としたり、メソッドの prefix を取った名前をプロパティとしたり、その辺は色々と流儀がありそうだけど。

前述の通り、Java にはプロパティというものが言語仕様上にない。ゆえに動的にプロパティの指定を行うには、少し工夫が必要になる。

その1つの解が、文字列でプロパティ名を渡し、指定する方法である。これは最もお手軽な方法とも言える。しかし、これを使用すればするほどタイプミスによる誤りを生む可能性を増幅させてしまう。

そこで、どうにかシンボルで指定できないものかと考えた。そして行き着いた方法は、プロパティを enum で指定する方法である。各 Bean クラスの基底クラスとして、次の AbstractBean クラスを定義する。

AbstractBean

 1 public abstract class AbstractBean<P extends Enum<P>> {
 2
 3     private final Object[] values;
 4
 5     protected AbstractBean() {
 6         Class<P> propertyType = properties();
 7         values = new Object[propertyType.getEnumConstants().length];
 8     }
 9
10     protected <V> void set(P property, V value) {
11         values[property.ordinal()] = value;
12     }
13
14     protected Object get(P property) {
15         return values[property.ordinal()];
16     }
17
18     protected <V> V get(P property, Class<V> valueType) {
19         return valueType.cast(values[property.ordinal()]);
20     }
21
22     protected abstract Class<P> properties();
23
24 }

型引数として、enum クラスを受け取るようになっている。この enum クラスが、プロパティの列挙となる。properties() という abstract メソッドがある。AbstractBean クラスのサブクラスは、継承時に自身のプロパティ列挙クラスを指定し、properties メソッドでその列挙クラスを返すよう定義する。AbstractBean クラスのサブクラスの例が、次の Book クラスである。

Book

 1 public class Book extends AbstractBean<Book.Property> {
 2
 3     public enum Property { Name, Author, Price }
 4
 5     public Book() {
 6         super();
 7         setPrice(0);
 8     }
 9
10     @Override
11     protected Class<Property> properties() {
12         return Property.class;
13     }
14
15     public String getName() {
16         return get(Property.Name, String.class);
17     }
18     public void setName(String name) {
19         set(Property.Name, name);
20     }
21
22     public String getAuthor() {
23         return get(Property.Author, String.class);
24     }
25     public void setAuthor(String author) {
26         set(Property.Author, author);
27     }
28
29     public int getPrice() {
30         return get(Property.Price, Integer.class).intValue();
31     }
32     public void setPrice(int price) {
33         set(Property.Price, price);
34     }
35
36 }

さて、問題はこういった機構をいかに使い、シンボルを用いた動的なプロパティの指定を行うかである。そのサンプルとして、次のような Generator クラスを定義した。

Generator

 1 import java.util.HashMap;
 2 import java.util.Map;
 3
 4 public class Generator<T extends AbstractBean<P>, P extends Enum<P>> {
 5
 6     public static <T extends AbstractBean<P>, P extends Enum<P>>
 7         Generator<T, P> type(Class<T> type) {
 8         return new Generator<T, P>(type);
 9     }
10
11     private static <T> T newInstance(Class<T> type) {
12         try {
13             return type.newInstance();
14         } catch (Exception e) {
15             throw new Error(e);
16         }
17     }
18
19     private Class<T> type;
20     private Map<P, Object> buffer = new HashMap<P, Object>();
21
22     private Generator(Class<T> type) {
23         this.type = type;
24     }
25
26     public Generator<T, P> value(P property, Object value) {
27         buffer.put(property, value);
28         return this;
29     }
30
31     public T generate() {
32         T instance = newInstance(type);
33         for (Map.Entry<P, Object> prop : buffer.entrySet()) {
34             instance.set(prop.getKey(), prop.getValue());
35         }
36         buffer.clear();
37         return instance;
38     }
39
40 }

繰り返すが、実用性を求めるものでなく、あくまでも動的なプロパティの指定のサンプルとして作ったクラスである。どのクラスのインスタンスを生成するかを指定し、どのプロパティにどんな値を設定するという処理を行い、最後に generate を呼び出すとオブジェクトを生成する、というクラスである。例外処理は適当。

次はこのクラスを使ったテストケース。

GeneratorTest

 1 import org.junit.Before;
 2 import org.junit.Test;
 3 import static org.junit.Assert.*;
 4
 5 public class GeneratorTest {
 6
 7     @Test
 8     public void generate() {
 9         Book book =
10             Generator.type(Book.class)
11                      .value(Book.Property.Name, "うしおととら")
12                      .value(Book.Property.Author, "藤田和日郎")
13                      .value(Book.Property.Price, 250)
14                      .generate();
15
16         assertEquals("うしおととら", book.getName());
17         assertEquals("藤田和日郎", book.getAuthor());
18         assertEquals(250, book.getPrice());
19     }
20
21 }

このテストケースは成功する。動的なプロパティの指定をシンボルで行うことに成功している。ちなみにうしおととらは絶賛愛読中。

以上、どうにかして動的なプロパティの指定を、文字列から脱却できないものかと考えた結果でした。

スポンサーサイト

comment

post




上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。