メンバーの順序
意見に対して別の論を示したら強烈にdisられてしまいました。議論にならないのは残念ですね。
ともあれ、いい機会なのでメンバーの順序について掘り下げて考えたいと思います。といっても明確な結論は出なかったので、事実の確認と思考のメモということで。
話の発端になったコメントで、私は定義→使用の順で書いた方がいい、と書きました。
Enclosingクラスのデータ構造に密接に関わるようなInnerクラスの場合、最初に明示された方が全体のデータ構造が見通しやすいように思えます。
これは concurrent library でも有名な Doug Lea氏などもこう書いていますね。
JavaSE6.0 java.util.concurrent.atomic.AtomicStampedReference
public class AtomicStampedReference<V> { private static class ReferenceIntegerPair<T> { private final T reference; private final int integer; ReferenceIntegerPair(T r, int i) { reference = r; integer = i; } } private final AtomicReference<ReferenceIntegerPair<V>> atomicRef; public AtomicStampedReference(V initialRef, int initialStamp) { atomicRef = new AtomicReference<ReferenceIntegerPair<V>> (new ReferenceIntegerPair<V>(initialRef, initialStamp)); } public V getReference() { return atomicRef.get().reference; } public int getStamp() { return atomicRef.get().integer; } // 以下メソッド省略
引用にあたりコメントは省略しました。
もう一個例を。
JavaSE6.0 java.util.concurrent.ConcurrentLinkedQueue
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable { private static final long serialVersionUID = 196745693267521676L; private static class Node<E> { private volatile E item; private volatile Node<E> next; private static final AtomicReferenceFieldUpdater<Node, Node> nextUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Node.class, "next"); private static final AtomicReferenceFieldUpdater<Node, Object> itemUpdater = AtomicReferenceFieldUpdater.newUpdater (Node.class, Object.class, "item"); Node(E x) { item = x; } Node(E x, Node<E> n) { item = x; next = n; } E getItem() { return item; } boolean casItem(E cmp, E val) { return itemUpdater.compareAndSet(this, cmp, val); } void setItem(E val) { itemUpdater.set(this, val); } Node<E> getNext() { return next; } boolean casNext(Node<E> cmp, Node<E> val) { return nextUpdater.compareAndSet(this, cmp, val); } void setNext(Node<E> val) { nextUpdater.set(this, val); } } private static final AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> tailUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "tail"); private static final AtomicReferenceFieldUpdater<ConcurrentLinkedQueue, Node> headUpdater = AtomicReferenceFieldUpdater.newUpdater (ConcurrentLinkedQueue.class, Node.class, "head"); private boolean casTail(Node<E> cmp, Node<E> val) { return tailUpdater.compareAndSet(this, cmp, val); } private boolean casHead(Node<E> cmp, Node<E> val) { return headUpdater.compareAndSet(this, cmp, val); } private transient volatile Node<E> head = new Node<E>(null, null); private transient volatile Node<E> tail = head; public ConcurrentLinkedQueue() {} public ConcurrentLinkedQueue(Collection<? extends E> c) { for (Iterator<? extends E> it = c.iterator(); it.hasNext();) add(it.next()); } public boolean add(E e) { return offer(e); } // 以下メソッド省略
こういったコードが念頭にあったため、定義→使用の順という発言になったのですが、よくよく考えてみると普段これとは逆の書き方もしている事に気付きました。
というのは主にメソッド定義です。リファクタリングでメソッドの抽出を行う場合、新しいメソッドは抽出元よりも下に加えています。
■Before public T get() { try { Future<T> result = cache.get(); if (result != null) return result.get(); FutureTask<T> task = new FutureTask<T>(originalFactory); if (cache.compareAndSet(null, task)) { task.run(); } return cache.get().get(); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (InterruptedException e) { throw new CancellationException(e.getMessage()); } } ■After public T get() { try { return getFuture().get(); } catch (ExecutionException e) { throw new RuntimeException(e.getCause()); } catch (InterruptedException e) { throw new CancellationException(e.getMessage()); } } private Future<T> getFuture() { Future<T> result = cache.get(); if (result != null) return result; FutureTask<T> task = new FutureTask<T>(originalFactory); if (cache.compareAndSet(null, task)) { task.run(); } return cache.get(); }
定義→使用とは逆ですね。
これはメソッドの場合、重要な情報はシグネチャに全て含まれる、つまり使用箇所を見れば大体どんなメソッドなのか見当がつく(つかなかったら名付けか何かが悪い)のに対し、クラスがどんなクラスなのか見当つけるには、そのクラスがどんな振る舞いを持つのかといった情報が必要になるからなのではないかと思います。
また、上記とは関係なく内部クラスの定義を下に書いている場合がありました。
というのはインスタンスを別表現にするようなアダプタ(例えばIteratorなど)やクラスの内部構造にあまり大きな影響を与えないコンテナを書く場合ですね。
import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.NoSuchElementException; public class Timespan implements Iterable<Date> { private final long start; private final long end; public Timespan(final Date start, final Date end) { this(start.getTime(), end.getTime()); } public Timespan(final long start, final long end) { if (start >= end) throw new IllegalArgumentException("start >= end"); this.start = start; this.end = end; } // (中略) @Override public Iterator<Date> iterator() { return iterator(TimeUnit.DAY, 1); } public Iterator<Date> iterator(final TimeUnit timeUnit, final int amount) { return new IteratorImpl(timeUnit, amount); } private class IteratorImpl implements Iterator<Date> { private final Calendar current; private final TimeUnit timeUnit; private final int amount; IteratorImpl(final TimeUnit timeUnit, final int amount) { this.timeUnit = timeUnit; this.amount = amount; current = Calendar.getInstance(); current.setTime(new Date(start)); } @Override public Date next() { if (!hasNext()) throw new NoSuchElementException(); final Date result = current.getTime(); current.add(timeUnit.getCalendarTypeValue(), amount); return result; } @Override public boolean hasNext() { return current.getTimeInMillis() < end; } @Override public void remove() { throw new UnsupportedOperationException(); } } }
結局ケースバイケースで選択していくという感じですかね。
generic の自己再帰定義活用
Haskell の Monad の bind は、戻り値が第一引数と同じ Monadインスタンスでないとなりません。そうでないとモナド則が成り立たないですしね。
そもそも Haskell の ad-hoc polymorphism では、同じ型クラスの違うインスタンスを混ぜて扱うことは許していないようです。その辺、Java や C# などの polymorphism とは違うところなので混乱しやすいかもしれませんね。
さて、では Java の interface で、Haskell のように、戻り値を実装クラス形に限定するようなメソッドを定義できるでしょうか?
完璧には行かないまでも自己再帰定義によって実現することができます。
public interface Function<ARG, RET> { RET call(ARG argument); } public interface Monad<T, K extends Monad<T, K>> { K bind(Function<? super T, ? extends K> func); } public class Maybe<T> implements Monad<T, Maybe<T>> { @Override public Maybe<T> bind(Function<? super T, ? extends Maybe<T>> func) { return isNothing() ? this : func.call(value); } // isNothing() とか諸々省略 }
ちょっと型パラメータの定義がごちゃごちゃしすぎて、ぱっと見では意図が取りづらいですね。
それでもやりたかった事はおおよそできています。実装クラスで型パラメータ K を自クラスではなく別の実装クラスで指定できてしまうので、完璧とは言いがたいですが。
Pythonのメソッド引数に self を書くメリット
なぜPythonのメソッド引数に明示的にselfと書くのか に関連して。
確かに他の言語から移ってきた人からすると奇異に見えることが多そうですね。ただ、私はこの仕様を知ったとき「上手いな」と思いました。
というのも、
instance.method(arguments) == ClassName.method(instance, arguments)
これが成り立つ事によって、メソッドをコールバック関数として渡すのが非常に簡単になるのです。
つまり
class MyClass: def __init__(self, n): self.name = n def getname(self): return "name: " + self.name entities = [MyClass("A"), MyClass("B"), MyClass("C")] names = map(MyClass.getname, entities) # ["name: A", "name: B", "name: C"] が返される
という事ができてしまうのです。
JavaScriptでこれができないために以前エントリを書きました([id:gakuzo:20090403:1238715610])が、Python ではこの self の仕様のため簡単に実現できます。これは非常に魅力的。
ただ、Python のこの仕様にも問題があります。それは、ポリモーフィックには動作しないということ。
コードで例を示すと
class SuperClass: def __init__(self, n): self.name = n def getname(self): return "super: " + self.name class Sub1Class(SuperClass): def getname(self): # override return "sub1: " + self.name class Sub2Class(SuperClass): def getname(self): # override return "sub2: " + self.name entities = [Sub1Class("A"), Sub1Class("B"), Sub2Class("C"), Sub2Class("D")] # entities の各要素の getname を呼んで ["sub1: A", "sub1: B", "sub2: C", "sub2: D"] という値が欲しい。 names1 = map(SuperClass.getname, entities) # ["super: A", "super: B", "super: C", "super: D"] になる。 names2 = map(Sub1Class.getname, entities) # ["sub1: A", "sub1: B", "sub1: C", "sub1: D"] になる。 names3 = map(Sub2Class.getname, entities) # ["sub2: A", "sub2: B", "sub2: C", "sub2: D"] になる。
どうやっても上手くいきません。結局 lambda で書く必要があります。
仕組みを考えれば当たり前の話ではあるのですが、残念感が残りますね。この辺をスマートに解決してる言語をご存知の方がいらっしゃればご教授ください。
Ruby ぐらいブロックが簡潔に書ければそもそもこんな仕組みは必要ない気もしますが。
Singleton にするために enum を使う是非
以下思考の変遷。
priority という値を持つ DTO と、その DTO の List を priority順で処理する必要があるロジックがありました。
■Dto public class HogeDto { private short priority; public short getPriority() {return priority;} // その他の項目は省略 } ■Logic public class FooLogic { private Comparator<HogeDto> priorityComparator = new Comparator<HogeDto>() { public int compare(HogeDto o1, HogeDto o2) { // 桁あふれを起こさないためにキャスト return ((int) o1.getPriority()) - o2.getPriority(); } }; // その他省略 }
そんな中、他のロジックでも priority順で処理したいという要望があがってきたので無名クラスで作成していた Comparator を public class にすることにしました。
public class HogeDto { private short priority; public short getPriority() {return priority;} // その他の項目は省略 public static final class PriorityComparator implements Comparator<HogeDto>, Serializable { private static final long serialVersionUID = 699700470652630616L; @Override public int compare(HogeDto o1, HogeDto o2) { // 桁あふれを起こさないためにキャスト return ((int) o1.getPriority()) - o2.getPriority(); } @Override public boolean equals(Object object) { // このクラスのインスタンスは全て等しいとみなす return object instanceof PriorityComparator; } @Override public int hashCode() { // equals を override したのでそれに合わせる return 1; } } }
Comparator の実装は直列化可能であることを強く推奨されています。また性能要件から効率的な equals() をオーバーライドすることが推奨されています。
この PriorityComparator をはじめ、Comparator の多くはステートレスと思われるので、全てのインスタンスが同一であると見なせます。そうした場合、Object#equals() のデフォルト実装よりも、明示的にオーバーライドする方が効率のよい equals() を提供できます。したがって上記のようになりました。
ここで、はて?となります。
それならばいっそ Singleton にしてしまえば、equals() と hashCode() の明示的なオーバーライドは必要なくなるのでは?と考えました。
public class HogeDto { private short priority; public short getPriority() {return priority;} // その他の項目は省略 public static final Comparator<HogeDto> priorityComparator = new PriorityComparator(); public static final class PriorityComparator implements Comparator<HogeDto>, Serializable { private static final long serialVersionUID = 699700470652630616L; private PriorityComparator() {} @Override public int compare(HogeDto o1, HogeDto o2) { // 桁あふれを起こさないためにキャスト return ((int) o1.getPriority()) - o2.getPriority(); } private Object readResolve() throws ObjectStreamException { return HogeDto.priorityComparator; } } }
Singleton にしたコードです。最近ではその弊害が指摘され、安易に使用するのは憚られる Singleton ですが、PriorityComparator の場合、ステートレスなためそれほど気にせず Singleton にしても問題なさそうです。
これで equals() と hashCode() を実装しなくてすんで、コードがすっきり。と行きたい所ですがそう簡単にはいきません。
Serializable な Singleton クラスを作る場合、そのインスタンスが一意であることを保障するために readResolve() を実装しなければいけません。
そこで思い出すのが Joshua Bloch が Effective Java で書いていた内容です。彼曰く「Singleton を作りたければ enum を使え」
public class HogeDto { private short priority; public short getPriority() {return priority;} // その他の項目は省略 public static enum PriorityComparator implements Comparator<HogeDto> { INSTANCE; @Override public int compare(HogeDto o1, HogeDto o2) { // 桁あふれを起こさないためにキャスト return ((int) o1.getPriority()) - o2.getPriority(); } } }
という訳で enum で書き直したコードです。恐ろしくすっきり!enum であれば直列化可能な事は保障されていますので、implements から Serializable も除去しました。readResolve の問題も自動で解決してくれます。
すっきりしたのは良いのですが、何やら釈然としないものを感じます。
というのは、enum の意図している目的から外れた利用方法なのでは?という印象が残るからです。
例えば、定数 interface の implements によるコード量の削減など、機能としては可能だけれども意図が不明になるのでやるべきではない記述(一部でテクニックとか言ったりする人が居る)というのが存在します。この enum の利用もそれに含まれるのではないか?という思いが強いのです。
この辺、Singleton に enum を使ってよい理由/使ってはNGな理由というのを識者の方々に聞いてみたいところです。
S2JDBCによる不変オブジェクト活用
以前から S2JDBC を利用しています。
DTOとEntityを同じクラスにしたいため、public field は避けて JavaBeans にしていました。
最初、S2Dao と同じ感覚で各種アノテーションをアクセサに指定してたんですが、これが何故か動かない。
はて、と思って S2JDBC のコードを読んでみると、なんと直接 field を見てリフレクションでアクセス修飾子を無視して read/write を行っているではないですか。
一瞬違和感を感じましたが、よくよく考えればフレームワークですし問題ない気もします。それよりも見た目上 Entity を Immutable にできる事に大変強い魅力を感じました。
デフォルトコンストラクタを public で提供する必要がありますし、フィールドを final にすることはできませんが、class を final にして、getter だけを提供し、Mutableオブジェクト型のプロパティは防御的コピーに徹すれば、手で書くコード中では不変なオブジェクトとして扱うことができます。
値を設定したインスタンスが欲しい場合は Builderパターンを適用すればよさそうです。
これまでなるべくオブジェクトを Immutable にしたいと思ってもフレームワークが JavaBeans を要求しているためできないといった歯がゆさがありました。それが S2JDBC では堂々と不変オブジェクトを使うことができます。これは嬉しい。
問題はプレゼンテーション側のフレームワークが JavaBeans を要求している場合と、更新者名とか更新日時といった毎回手でセットするのは面倒なので AOP で設定しているプロパティの扱いをどうするか、といった点ですね。
Effective Java の Builderパターンとその拡張
Effective Java 第2版 (The Java Series)
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2008/11/27
- メディア: 単行本(ソフトカバー)
- 購入: 77人 クリック: 936回
- この商品を含むブログ (264件) を見る
ちょいと前から Effective Java 第二版の読書会的なものをやってます。
その中で Builderパターンが非常に美しいね、という話になりました。
Builderパターンとは数多くのパラメータを持つオブジェクトの生成に関するパターンです。
オブジェクト生成に通常のコンストラクタを使用する場合、パラメータ間の整合性は保てますが、数が多いと引数の順番を知っている必要があり書きづらく読みづらいコードになります。
それ以外でよく使われるのが JavaBeansパターンです。
デフォルトコンストラクタを使用し、setter を用いて値を設定する方法ですね。
この方法では、コードの意味合いは明確になりますが、パラメータ間の整合性を保てず、クラスを不変にする可能性を排除してしまいます。
そこで以下の様な Builder パターンの登場となります。
// ■オブジェクト側 public final class Commodity { private final int id; // 必須 private final String name; // 必須 private final long catalogPrice; // 必須 private final long lowestPrice; // オプショナル private final long averagePrice; // オプショナル private final String makerName; // オプショナル public static class Builder { private final int id; private final String name; private final long catalogPrice; private long lowestPrice; private long averagePrice; private String makerName; public Builder(int id, String name, long catalogPrice) { // 必須項目は Builder のコンストラクタで指定 this.id = id; this.name = name; this.catalogPrice = catalogPrice; } public Builder lowestPrice(long lowestPrice) { this.lowestPrice = lowestPrice; return this; } public Builder averagePrice(long averagePrice) { this.averagePrice = averagePrice; return this; } public Builder makerName(long makerName) { this.makerName = makerName; return this; } public Commodity build() { // パラメータ間の整合性は build メソッドで解決 if (lowestPrice > averagePrice) throw new IllegalStateException("lowestPrice > averagePrice"); return new Commodity(this); } } private Commodity(Builder builder) { id = builder.id; name = builder.name; lowestPrice = builder.lowestPrice; averagePrice = builder.averagePrice; makerName = builder.makerName; } // getter 省略 } // ■クライアント側 Commodity commodity = new Commodity.Builder(10, "hoge", 400).lowestPrice(100).averagePrice(300).build();
パラメータ間の整合性を保ったまま、クラスを不変にすることも可能にしています。すばらしいですね。
しかしこれ、必須のパラメータはコンストラクタで渡していますが、必須のパラメータが多い場合にはコンストラクタでオブジェクトを生成する場合と同じ問題が起ってしまいます。
そこで他のパラメータと同じようにメソッドで設定できるようにしたい、けれども必ず必須の項目が設定されるようにしたい、という問題を解決するために以下の様な形を考えてみました。
// ■オブジェクト側 public final class Commodity { private final int id; // 必須 private final String name; // 必須 private final long catalogPrice; // 必須 private final long lowestPrice; // オプショナル private final long averagePrice; // オプショナル private final String makerName; // オプショナル public static interface IdBuilder { NameBuilder id(int id); } public static interface NameBuilder { CatalogPriceBuilder name(String name); } public static interface CatalogPriceBuilder { Builder catalogPrice(long catalogPrice); } public static class Builder implements IdBuilder, NameBuilder, CatalogPriceBuilder { private int id; private String name; private long catalogPrice; private long lowestPrice; private long averagePrice; private String makerName; private Builder() { // Builder の生成はファクトリメソッドを使用するため private } public NameBuilder id(long id) { this.id = id; return this; } public CatalogPriceBuilder name(String name) { this.name = name; return this; } public Builder catalogPrice(long catalogPrice) { this.catalogPrice = catalogPrice; return this; } public Builder lowestPrice(long lowestPrice) { this.lowestPrice = lowestPrice; return this; } public Builder averagePrice(long averagePrice) { this.averagePrice = averagePrice; return this; } public Builder makerName(String makerName) { this.makerName = makerName; return this; } public Commodity build() { // パラメータ間の整合性は build メソッドで解決 if (lowestPrice > averagePrice) throw new IllegalStateException("lowestPrice > averagePrice"); return new Commodity(this); } } public static IdBuilder builder() { return new Builder(); } private Commodity(Builder builder) { id = builder.id; name = builder.name; catalogPrice = builder.catalogPrice; lowestPrice = builder.lowestPrice; averagePrice = builder.averagePrice; makerName = builder.makerName; } // getter 省略 } // ■クライアント側 Commodity commodity = Commodity.builder().id(10).name("hoge").catalogPrice(400) .lowestPrice(100).averagePrice(300).build();
必須パラメータにはそれぞれ buildメソッドを持たない interface を用意します。
Builderのファクトリメソッド及び、パラメータの設定メソッドがそれらの interface を数珠繋ぎに返します。
これによって、すべての必須パラメータに値を設定することが保障されます。
おまけとして IDE の補完機能を使えば何を設定したらいいか JavaDoc を見ずとも教えてくれるので Fluent Interface のメリットも最大限に享受できますね。
問題はコード量が増えてしまうところ。単純に読みづらいですし、IDE の支援がある JavaBeans パターンと違って書くのもメンドクサイです。書くのに関しては必須とオプショナルの区別がつけば自動生成は簡単そうなので Eclipse Plugin なりエディタのマクロなり組めばその辺の労力は JavaBeans と同じにできそうです。
2009-10-17 03:05
private → public や long → String の書き間違いを修正
Key-Value Store の統一インターフェイスが欲しい
最近 Tokyo Cabinet/Tokyo Tyrant で遊んでいます。分散楽しいです。
最初は Key-Value型のデータストアっていっぱいあってどれがいいかなーとか比較してたりしたんですが、やっぱりエンティティをシリアライズするコストがどうにも気になって。BigTable みたいにエンティティのフィールドをカラムとするような Key-Value Store が欲しいなと思ってたところで Tokyo Cabinet がテーブル的にアクセスできると知ったので触ってみたのでした。
んで、ひがさんがGAE/J 用に Fluent Interface な BigTableアクセサを作ってると聞いて wktk してます。BigTable 固有の機能をどこまで使うのかわかりませんが、interface と BigTable 向けの実装を切り分けて提供してくれると非常に嬉しいなと思っています。そうしてその流れるようなインターフェイスを Key-Value Store のデファクトスタンダードにしてしまえばいいんじゃないかと。
そんな感じで提供されたら Tokyo Cabinet 向けの実装を書きたいと思います。
というか interface と実装が分かれて無くてもパクって Tokyo Cabinet 用のバインディングラッパを作っちゃうと思いますが^^;;;