どのORMを採用するかでアプリケーションの作成方法は大きくかわってきます。
SQLをがりがり書くタイプのORMを採用したり、そもそもORMを使っていない場合、レイジーロードやシングルテーブルインヘリタンスなどの仕組みは自前で用意しなければなりません。
ここではEnumを使ってシングルテーブルインヘリタンスをサポートしていないORMでもシングルテーブルインヘリタンスを実現する方法を紹介します。
(S2JDBCのドキュメントにあるEnumを使って多態を表す例を参考にしました)
要は、Entityが行う処理をすべてEnumに委譲してやればよいのです。
public class Player { | |
protected Long id; | |
protected PlayerBehavior behavior; | |
protected String name; | |
protected String club; | |
protected Double battingAverage; | |
protected Double bowlingAverage; | |
public Long getId() { return id; } | |
public void setId(Long id) { this.id = id; } | |
public String getName() { return behavior.getName(this); } | |
public void setName(String name) { behavior.setName(this, name); } | |
public String getClub() { return behavior.getClub(this); } | |
public void setClub(String club) { behavior.setClub(this, club); } | |
public Double getBattingAverage() { return behavior.getBattingAverage(this); } | |
public void setBattingAverage(Double battingAverage) { behavior.setBattingAverage(this, battingAverage); } | |
public Double getBowlingAverage() { return behavior.getBowlingAverage(this); } | |
public void setBowlingAverage(Double bowlingAverage) { behavior.setBowlingAverage(this, bowlingAverage); } | |
public static enum PlayerBehavior { | |
PLAYER, | |
FOTBALLER { | |
@Override | |
public String getClub(Player self) { return self.club; } | |
@Override | |
public void setClub(Player self, String club) { self.club = club; } | |
}, | |
CRICKETER { | |
@Override | |
public Double getBattingAverage(Player self) { return self.battingAverage; } | |
@Override | |
public void setBattingAverage(Player self, Double battingAverage) { self.battingAverage = battingAverage; } | |
}, | |
BOWLER { | |
@Override | |
public Double getBattingAverage(Player self) { return self.battingAverage; } | |
@Override | |
public void setBattingAverage(Player self, Double battingAverage) { self.battingAverage = battingAverage; } | |
@Override | |
public Double getBowlingAverage(Player self) { return self.bowlingAverage; } | |
@Override | |
public void setBowlingAverage(Player self, Double bowlingAverage) { self.bowlingAverage = bowlingAverage; } | |
}, | |
; | |
public String getName(Player self) { return self.name; } | |
public void setName(Player self, String name) { self.name = name; } | |
public String getClub(Player self) { throw new UnsupportedOperationException(); } | |
public void setClub(Player self, String club) { throw new UnsupportedOperationException(); } | |
public Double getBattingAverage(Player self) { throw new UnsupportedOperationException(); } | |
public void setBattingAverage(Player self, Double battingAverage) { throw new UnsupportedOperationException(); } | |
public Double getBowlingAverage(Player self) { throw new UnsupportedOperationException(); } | |
public void setBowlingAverage(Player self, Double bowlingAverage) { throw new UnsupportedOperationException(); } | |
} | |
} |
できた!
。。。と思ったんですが、どこにもインヘリタンス(継承)が見つかりません。Enumは継承が使えません。
この問題はEnumに処理を書くのではなくEnumから振る舞いを取得する事で解決できます。
ではEnumから振る舞いを取得するように変更してみましょう。
public class Player { | |
protected Long id; | |
protected PlayerBehaviorType type; | |
protected String name; | |
protected String club; | |
protected Double battingAverage; | |
protected Double bowlingAverage; | |
public Long getId() { return id; } | |
public void setId(Long id) { this.id = id; } | |
public String getName() { return type.getBehavior().getName(this); } | |
public void setName(String name) { type.getBehavior().setName(this, name); } | |
public String getClub() { return type.getBehavior().getClub(this); } | |
public void setClub(String club) { type.getBehavior().setClub(this, club); } | |
public Double getBattingAverage() { return type.getBehavior().getBattingAverage(this); } | |
public void setBattingAverage(Double battingAverage) { type.getBehavior().setBattingAverage(this, battingAverage); } | |
public Double getBowlingAverage() { return type.getBehavior().getBowlingAverage(this); } | |
public void setBowlingAverage(Double bowlingAverage) { type.getBehavior().setBowlingAverage(this, bowlingAverage); } | |
public static enum PlayerBehaviorType { | |
PLAYER(new PlayerBehaviorPlayer()), | |
FOTBALLER(new PlayerBehaviorFotballer()), | |
CRICKETER(new PlayerBehaviorCricketer()), | |
BOWLER(new PlayerBehaviorBowler()), | |
; | |
private final PlayerBehavior behavior; | |
PlayerBehaviorType(PlayerBehavior behavior) { | |
this.behavior = behavior; | |
} | |
public PlayerBehavior getBehavior() { | |
return behavior; | |
} | |
} | |
public interface PlayerBehavior { | |
String getName(Player self); | |
void setName(Player self, String name); | |
String getClub(Player self); | |
void setClub(Player self, String club); | |
Double getBattingAverage(Player self); | |
void setBattingAverage(Player self, Double battingAberage); | |
Double getBowlingAverage(Player self); | |
void setBowlingAverage(Player self, Double bowlingAberage); | |
} | |
public static class PlayerBehaviorPlayer implements PlayerBehavior { | |
@Override | |
public String getName(Player self) { return self.name; } | |
@Override | |
public void setName(Player self, String name) { self.name = name; } | |
@Override | |
public String getClub(Player self) { throw new UnsupportedOperationException(); } | |
@Override | |
public void setClub(Player self, String club) { throw new UnsupportedOperationException(); } | |
@Override | |
public Double getBattingAverage(Player self) { throw new UnsupportedOperationException(); } | |
@Override | |
public void setBattingAverage(Player self, Double battingAberage) { throw new UnsupportedOperationException(); } | |
@Override | |
public Double getBowlingAverage(Player self) { throw new UnsupportedOperationException(); } | |
@Override | |
public void setBowlingAverage(Player self, Double bowlingAverage) { throw new UnsupportedOperationException(); } | |
} | |
public static class PlayerBehaviorFotballer extends PlayerBehaviorPlayer { | |
@Override | |
public String getClub(Player self) { return self.club; } | |
@Override | |
public void setClub(Player self, String club) { self.club = club; } | |
} | |
public static class PlayerBehaviorCricketer extends PlayerBehaviorPlayer { | |
@Override | |
public Double getBattingAverage(Player self) { return self.battingAverage; } | |
@Override | |
public void setBattingAverage(Player self, Double battingAverage) { self.battingAverage = battingAverage; } | |
} | |
public static class PlayerBehaviorBowler extends PlayerBehaviorCricketer { | |
@Override | |
public Double getBowlingAverage(Player self) { return self.bowlingAverage; } | |
@Override | |
public void setBowlingAverage(Player self, Double bowlingAverage) { self.bowlingAverage = bowlingAverage; } | |
} | |
} |
Perlで書いたオブジェクト指向のプログラムみたいですが、PlayerBehaviorによるシングルテーブルインヘリタンスが実装できました!
S2JDBCやiBatisなどSQLをがりがり書くタイプのORM使っている、もしくはDBIしか使ってないから。。と嘆いている人、これで実装できます。
RubyとかPythonとか使っている人はちゃんとリッチなフレームワークを採用しましょう。