#流れるようなインターフェースの使い方はこれで正しいのか?
ある現場でJava経験の長い人にC#のプロジェクトを手伝ってもらったことがある。その中で、流れるようなインターフェースを使用しているコードがあったがずっと納得がいかない部分があった。そのときのコードを参考にしてサンプルコードを作成してみた。
仕様)DBから取得した結果に対して画面の値をマージしてDB更新する。
- DBにはHeader1,Header2,Header3があり、全て更新対象とする。
- 画面で値編集可能であり、マージのタイミングは登録ボタンクリック時。
/** メイン処理 **/
private Dao _dao = new Dao();
public void Main()
{
// DBから現時点の情報を取得する。
var param = new UpdateParameter(_dao)
.SetHeader(1)
.SetHeader2("a")
.SetHeader3(2);
// 画面の情報を反映する。
SetValueFromView(param);
// DBを更新する。
_dao.Update(param);
}
private void SetValueFromView(UpdateParameter param)
{
// 画面の値を取得したパラメータのHeader1,2,3に反映する。
// コードは省略
}
/** DB更新テーブルのDTOを一つにまとめたパラメータクラス **/
public class UpdateParameter
{
public Header Header { get; private set; }
public Header2 Header2 { get; private set; }
public Header3 Header3 { get; private set; }
private readonly Dao _dao = null;
public UpdateParameter(Dao dao)
{
_dao = dao;
}
public UpdateParameter SetHeader(int id)
{
Header = _dao.GetHeader(id);
return this;
}
public UpdateParameter SetHeader2(string code)
{
Header2 = _dao.GetHeader2(code);
return this;
}
public UpdateParameter SetHeader3(int id)
{
Header3 = _dao.GetHeader3(id);
return this;
}
}
/** Header1,2,3のDTO **/
public class Header
{
public int Id { get; set; }
}
public class Header2
{
public string Code { get; set; }
}
public class Header3
{
public int Id { get; set; }
}
/** データ取得、および更新用Dao **/
public class Dao
{
public Header GetHeader(int id)
{
// ヘッダーの取得
return new Header() { Id = id };
}
public Header2 GetHeader2(string code)
{
// ヘッダー2の取得
return new Header2() { Code = code };
}
public Header3 GetHeader3(int id)
{
// ヘッダー3の取得
return new Header3() { Id = id };
}
public void Update(UpdateParameter param)
{
// DB更新処理を実行する。コードは省略
}
}
java
private Dao dao = new Dao();
public void main() {
// DBから現時点の情報を取得する。
UpdateParameter param = new UpdateParameter(dao)
.setHeader(1)
.setHeader2("a")
.setHeader3(2);
// 画面の情報を反映する。
setValueFromView(param);
// DBを更新する。
dao.update(param);
}
private void setValueFromView(UpdateParameter param) {
// 画面の値を取得したパラメータのHeader1,2,3に反映する。
// コードは省略
}
public class UpdateParameter {
private Header header = null;
private Header2 header2 = null;
private Header3 header3 = null;
private final Dao dao;
public UpdateParameter(Dao dao) {
this.dao = dao;
}
public UpdateParameter setHeader(int id) {
this.header = dao.getHeader(id);
return this;
}
public UpdateParameter setHeader2(String code) {
this.header2 = dao.getHeader2(code);
return this;
}
public UpdateParameter setHeader3(int id) {
this.header3 = dao.getHeader3(id);
return this;
}
public Header getHeader() {
return header;
}
public Header2 getHeader2() {
return header2;
}
public Header3 getHeader3() {
return header3;
}
}
/** Header1,2,3のDTO **/
public class Header {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
public class Header2 {
private String Code;
public String getCode() {
return Code;
}
public void setCode(String code) {
Code = code;
}
}
public class Header3 {
private int Id;
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
}
/** データ取得、および更新用Dao **/
public class Dao {
public Header getHeader(int id) {
// ヘッダーの取得
Header header = new Header();
header.setId(id);
return header;
}
public Header2 getHeader2(String code) {
// ヘッダー2の取得
Header2 header2 = new Header2();
header2.setCode(code);
return header2;
}
public Header3 getHeader3(int id) {
// ヘッダー3の取得
Header3 header3 = new Header3();
header3.setId(id);
return header3;
}
public void update(UpdateParameter param) {
// DB更新処理を実行する。コードは省略
}
}
##気になったこと
UpdateParameterが流れるようなインターフェースになっているようなのだが、このコードには以下の問題がある。
- UpdateParameterがDaoを参照している。
- テーブルが増えたら直す箇所が複数ある。
- SetHeader1~SetHeader3を必ず呼び出さなければならない。
流れるようなインターフェースはStringBuilderのようなクラスだと利点が理解できる。一方、UpdateParameterのような単純な情報を持つだけのクラスで使うと違和感がある。
##無難なコードに直してみる。
/** メイン処理 **/
private Dao _dao = new Dao();
public void Main()
{
// DBから現時点の情報を取得する。
var param = new UpdateParameter();
// メイン処理でDBからデータを取得する。
param.Header = _dao.GetHeader(1);
param.Header2 = _dao.GetHeader2("a");
param.Header3 = _dao.GetHeader3(2);
// 画面の情報を反映する。
SetValueFromView(param);
// DBを更新する。
_dao.Update(param);
}
private void SetValueFromView(UpdateParameter param)
{
// 画面の値を取得したパラメータのHeader1,2,3に反映する。
// コードは省略
}
/** DB更新テーブルのDTOを一つにまとめたパラメータクラス **/
public class UpdateParameter
{
public Header Header { get; set; }
public Header2 Header2 { get; set; }
public Header3 Header3 { get; set; }
// Daoの参照を削除
}
/** Header1,2,3のDTO **/
public class Header
{
public int Id { get; set; }
}
public class Header2
{
public string Code { get; set; }
}
public class Header3
{
public int Id { get; set; }
}
/** データ取得、および更新用Dao **/
public class Dao
{
public Header GetHeader(int id)
{
// ヘッダーの取得
return new Header() { Id = id };
}
public Header2 GetHeader2(string code)
{
// ヘッダー2の取得
return new Header2() { Code = code };
}
public Header3 GetHeader3(int id)
{
// ヘッダー3の取得
return new Header3() { Id = id };
}
public void Update(UpdateParameter param)
{
// DB更新処理を実行する。コードは省略
}
}
java
private Dao dao = new Dao();
public void main() {
// DBから現時点の情報を取得する。
UpdateParameter param = new UpdateParameter();
// メイン処理でDBからデータを取得する。
param.setHeader(dao.getHeader(1));
param.setHeader2(dao.getHeader2("a"));
param.setHeader3(dao.getHeader3(2));
// 画面の情報を反映する。
setValueFromView(param);
// DBを更新する。
dao.update(param);
}
private void setValueFromView(UpdateParameter param) {
// 画面の値を取得したパラメータのHeader1,2,3に反映する。
// コードは省略
}
public class UpdateParameter {
private Header header = null;
private Header2 header2 = null;
private Header3 header3 = null;
// Daoの参照を削除
public void setHeader(Header header) {
this.header = header;
}
public void setHeader2(Header2 header2) {
this.header2 = header2;
}
public void setHeader3(Header3 header3) {
this.header3 = header3;
}
public Header getHeader() {
return header;
}
public Header2 getHeader2() {
return header2;
}
public Header3 getHeader3() {
return header3;
}
}
// 以下のコードは省略
UpdateParameterのDao参照を削除して、メイン処理側でDB取得するようにしただけであるが、このコードなら納得できる。
##まとめ
Javaの開発経験は1年程度なので、Javaだとサンプルであげたようなコードは普通なのか気になるところ。
また、「気になったこと」に記載した問題点をそのままにしてでも、流れるようなインターフェースを使う理由はなんだろう。
[前の記事(ちょっと気になるコード集)]
(http://qiita.com/csharpisthebest/items/7d9d2c7e4da4b89488fd)
[次の記事(冗長な代入文)]
(http://qiita.com/csharpisthebest/items/f9ebc741e40037553d5b)