Upload
tatsuya-maki
View
144
Download
0
Embed Size (px)
Citation preview
public SearchRequest(String query, String lang, String order, String sort) {
this(query, lang, order, sort, LIMIT, PAGE);}
public SearchRequest(String query, String lang, String order, String sort, int limit, int page) {
mUrl = buildUrl(query, lang, order, sort, limit, page);}
パラメータの多いコンストラクタ
同じ型が連続すると 型チェックに弱い
public SearchRequest(String query, String lang, String order, String sort) {
this(query, lang, order, sort, LIMIT, PAGE);}
public SearchRequest(String query, String lang, String order, String sort, int limit, int page) {
mUrl = buildUrl(query, lang, order, sort, limit, page);}
任意のパラメータも 与える必要がある
public SearchRequest(String query, String lang, String order, String sort) {
this(query, lang, order, sort, LIMIT, PAGE);}
public SearchRequest(String query, String lang, String order, String sort, int limit, int page) {
mUrl = buildUrl(query, lang, order, sort, limit, page);}
public class Builder { private final String mQuery;
private String mOrder; private String mSort; private String mLang;
public Builder(String query) { mQuery = query; }
public Builder setOrder(String order) { mOrder = order; return this; }
public Builder setSort(String sort) { mSort = sort; return this; }
...
public SearchRequest build() { return new SearchRequest(this); }}
強制するのは 必須パラメータのみ
任意パラメータは メソッドから与える
public class Builder { private final String mQuery;
private String mOrder; private String mSort; private String mLang;
public Builder(String query) { mQuery = query; }
public Builder setOrder(String order) { mOrder = order; return this; }
public Builder setSort(String sort) { mSort = sort; return this; }
...
public SearchRequest build() { return new SearchRequest(this); }}
Request request = new SearchRequest.Builder("Java") .setLang("ja") .setOrder("date") .setSort("desc") .setLimit(100) .setPage(2) .build();
インスタンス生成時の 可読性が向上
public class Padding { public int left; public int top; public int right; public int bottom;}
関連するパラメータを 1つのクラスとして定義
Padding padding = new Padding();padding.left = 10;padding.top = 20;padding.right = 10;padding.bottom = 20;
object.setPadding(padding);
切り出したクラスをパラメータとして与える
public int getItemCount() { if (mCursor == null) { return 0; } return mCursor.getCount();}
public long getItemId(int position) { if (mCursor == null) { return NO_ID; } mCursor.moveToPosition(position); return mCursor.getLong(mRowIdColumn);}
public Cursor changeCursor(Cursor cursor) { Cursor oldCursor = mCursor; mCursor = cursor; return oldCursor;}
Nullableな フィールドを持つクラス
Nullになりうる 引数をそのまま受け取る
public int getItemCount() { if (mCursor == null) { return 0; } return mCursor.getCount();}
public long getItemId(int position) { if (mCursor == null) { return NO_ID; } mCursor.moveToPosition(position); return mCursor.getLong(mRowIdColumn);}
public Cursor changeCursor(Cursor cursor) { Cursor oldCursor = mCursor; mCursor = cursor; return oldCursor;}
public int getItemCount() { if (mCursor == null) { return 0; } return mCursor.getCount();}
public long getItemId(int position) { if (mCursor == null) { return NO_ID; } mCursor.moveToPosition(position); return mCursor.getLong(mRowIdColumn);}
public Cursor changeCursor(Cursor cursor) { Cursor oldCursor = mCursor; mCursor = cursor; return oldCursor;}
Nullチェックが点在し 実装が複雑化
public class NullCursor implements Cursor {
@Override public int getCount() { return 0; }
@Override public int getPosition() { return 0; }
@Override public boolean move(int offset) { return true; }
@Override public boolean moveToPosition(int position) { return true; }
...
}
適切な振舞をする Null Objectを定義
@Overridepublic int getItemCount() { return mCursor.getCount();}
@Overridepublic long getItemId(int position) { mCursor.moveToPosition(position); return mCursor.getLong(mRowIdColumn);}
public Cursor changeCursor(Cursor cursor) { Cursor oldCursor = mCursor; if (mCursor == null) { mCursor = new NullCursor(); } else { mCursor = cursor; } return oldCursor;}
Nullが与えられたら Null Objectを代わりに利用
@Overridepublic int getItemCount() { return mCursor.getCount();}
@Overridepublic long getItemId(int position) { mCursor.moveToPosition(position); return mCursor.getLong(mRowIdColumn);}
public Cursor changeCursor(Cursor cursor) { Cursor oldCursor = mCursor; if (mCursor == null) { mCursor = new NullCursor(); } else { mCursor = cursor; } return oldCursor;}
他の処理では Nullチェックが不要
public class UpdateTask extends Task {
@Override public void execute() throws TaskException { SQLiteDatabase db = mHelper.getWritableDatabase(); try { db.beginTransaction(); db.update(TABLE, mValues, mWhere, mArgs); db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new TaskException(e); } finally { db.endTransaction(); } }}
DB操作など 定型的な処理が多いクラス
責務ごとに 複数のクラスが存在
public class DeleteTask extends Task {
@Override public void execute() throws TaskException { SQLiteDatabase db = mHelper.getWritableDatabase(); try { db.beginTransaction(); db.delete(TABLE, mWhere, mArgs); db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new TaskException(e); } finally { db.endTransaction(); } }}
複数のサブクラスで似たような処理を実装
public class XxxTask extends Task {
@Override public void execute() throws TaskException { SQLiteDatabase db = mHelper.getWritableDatabase(); try { db.beginTransaction(); db.doXxx(); db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new TaskException(e); } finally { db.endTransaction(); } }}
public abstract class SQLiteTask extends Task {
public final void execute() throws TaskException { SQLiteDatabase db = mHelper.getWritableDatabase(); try { db.beginTransaction(); runInTx(db); db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new TaskException(e); } finally { db.endTransaction(); } }
protected abstract void runInTx(SQLiteDatabase db) throws SQLiteException;}
定型処理を抽象クラスで定義
public abstract class SQLiteTask extends Task { {
public final void execute() throws TaskException { SQLiteDatabase db = mHelper.getWritableDatabase(); try { db.beginTransaction(); runInTx(db); db.setTransactionSuccessful(); } catch (SQLiteException e) { throw new TaskException(e); } finally { db.endTransaction(); } }
protected abstract void runInTx(SQLiteDatabase db) throws SQLiteException;}
サブクラスの担当は 抽象メソッドとして定義
public class UpdateTask extends SQLiteTask {
@Override protected void runInTx(SQLiteDatabase db) throws SQLiteException { db.update(TABLE, mValues, mWhere, mArgs); }}
定型処理以外は 各サブクラスで実装
public class DeleteTask extends SQLiteTask {
@Override protected void runInTx(SQLiteDatabase db) throws SQLiteException { db.delete(TABLE, mWhere, mArgs); }}
定型処理以外は 各サブクラスで実装
public class ViewAnimator { private final Animation mAnimation; private final Animator mAnimator;
public ViewAnimator(int from, int to) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { mAnimation = ValueAnimation.ofInt(from, to); mAnimator = null; } else { mAnimation = null; mAnimator = ValueAnimator.ofInt(from, to); } }
...
public void start() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { mView.start(mAnimation); } else { mAnimator.start(); } }}
APIバージョンに応じて 異なるAPIやクラスを切替
バージョン判定が点在し クラスやメソッドを切替
public class ViewAnimator { private final Animation mAnimation; private final Animator mAnimator;
public ViewAnimator(int from, int to) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { mAnimation = ValueAnimation.ofInt(from, to); mAnimator = null; } else { mAnimation = null; mAnimator = ValueAnimator.ofInt(from, to); } }
...
public void start() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { mView.start(mAnimation); } else { mAnimator.start(); } }}
互換性担保のための共通インターフェースを定義
public abstract class ViewAnimator {
public ViewAnimator(int from, int to) { ... }
public abstract void setDuration(long duration);
public abstract void setInterpolator(Interpolator i);
public abstract void start();}
各サブクラスは 複数ではなく単一責務を負う
public class OldViewAnimator extends ViewAnimator { private Animation mAnimation; private View mView;
...
@Override public void setDuration(long duration) { mAnimation.setDuration(duration); }
@Override public void setInterpolator(Interpolator i) { mAnimation.setInterpolator(i); }
@Override public void start() { mView.startAnimation(mAnimation); }}
public class NewViewAnimator extends ViewAnimator { private Animator mAnimator; private View mView;
...
@Override public void setDuration(long duration) { mAnimator.setDuration(duration); }
@Override public void setInterpolator(Interpolator i) { mAnimator.setInterpolator(i); }
@Override public void start() { mAnimator.start(); }}
各サブクラスは 複数ではなく単一責務を負う
public static ViewAnimator create(int version, int from, int to) { if (version < Build.VERSION_CODES.HONEYCOMB) { return new OldViewAnimator(from, to); } else { return new NewViewAnimator(from, to); }}
インスタンスの生成は Factory methodで隠蔽
@Overridepublic boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.action_refresh) { refresh(); return true; } else if (itemId == R.id.action_edit) { edit(); return true; } else if (itemId == R.id.action_delete) { delete(); return true; } else if (itemId == R.id.action_clear) { clear(); return true; } return super.onOptionsItemSelected(item);}
条件によって 処理をディスパッチ
条件が増えると 条件分岐が肥大化
@Overridepublic boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.action_refresh) { refresh(); return true; } else if (itemId == R.id.action_edit) { edit(); return true; } else if (itemId == R.id.action_delete) { delete(); return true; } else if (itemId == R.id.action_clear) { clear(); return true; } else if (itemId == R.id.action_setting) { showSetting(); return true; } else if (itemId == R.id.action_help) { showHelp(); return true; } return super.onOptionsItemSelected(item);}
private void refresh() {...
}
private void edit() {...
}
private void delete() {...
}
private void clear() {...
}
private void showSetting() {...
}
private void showHelp() {...
}
メソッドも増えて クラスも肥大化
public class RefreshCommand implements Command {
@Override public boolean execute() { ... return true; }}
処理ごとに サブクラスを作成
mCommands = new SparseArray<>();mCommands.append( R.id.action_edit, new EditCommand());mCommands.append( R.id.action_delete, new DeleteCommand());mCommands.append( R.id.action_clear, new ClearCommand());mCommands.append( R.id.action_refresh, new RefreshCommand());
コマンドの識別子がキーの コマンドマップを定義
@Overridepublic boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); Command command = mCommands.get( itemId, new NullCommand()); return command.execute(getApplicationContext());}
コマンドマップから コマンドを引き当てて実行
public void changeState(int state) { if (state == STATE_LOADING) { mLoadingView.setVisibility(View.VISIBLE); mSuccessView.setVisibility(View.GONE); mFailureView.setVisibility(View.GONE); } else if (state == STATE_SUCCESS) { mLoadingView.setVisibility(View.GONE); mSuccessView.setVisibility(View.VISIBLE); mFailureView.setVisibility(View.GONE); } else if (state == STATE_FAILURE) { mLoadingView.setVisibility(View.GONE); mSuccessView.setVisibility(View.GONE); mFailureView.setVisibility(View.VISIBLE); }}
Viewの表示切替などで 複雑な条件分岐
public void changeState(int state) { if (state == STATE_LOADING) { mLoadingView.setVisibility(View.VISIBLE); mSuccessView.setVisibility(View.GONE); mFailureView.setVisibility(View.GONE); } else if (state == STATE_SUCCESS) { mLoadingView.setVisibility(View.GONE); mSuccessView.setVisibility(View.VISIBLE); mFailureView.setVisibility(View.GONE); } else if (state == STATE_FAILURE) { mLoadingView.setVisibility(View.GONE); mSuccessView.setVisibility(View.GONE); mFailureView.setVisibility(View.VISIBLE); } else if (state == STATE_SETTING) { ... } else if (state == STATE_INITIAL) { ... }}
状態が増えると 条件分岐が更に肥大化
public enum State { LOADING { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.VISIBLE); layout.getSuccessView() .setVisibility(View.GONE); layout.getFailureView() .setVisibility(View.GONE); } }, SUCCESS { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.GONE); layout.getSuccessView() .setVisibility(View.VISIBLE); layout.getFailureView() .setVisibility(View.GONE); } }, ... };
abstract void change(StatefulFrameLayout layout);}
抽象メソッド化して サブクラスに実装を強制
public enum State { LOADING { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.VISIBLE); layout.getSuccessView() .setVisibility(View.GONE); layout.getFailureView() .setVisibility(View.GONE); } }, SUCCESS { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.GONE); layout.getSuccessView() .setVisibility(View.VISIBLE); layout.getFailureView() .setVisibility(View.GONE); } }, ... };
abstract void change(StatefulFrameLayout layout);}
実際の処理は サブクラスや列挙型で実装
public enum State { LOADING { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.VISIBLE); layout.getSuccessView() .setVisibility(View.GONE); layout.getFailureView() .setVisibility(View.GONE); } }, SUCCESS { @Override void change(StatefulFrameLayout layout) { layout.getLoadingView() .setVisibility(View.GONE); layout.getSuccessView() .setVisibility(View.VISIBLE); layout.getFailureView() .setVisibility(View.GONE); } }, ... };
abstract void change(StatefulFrameLayout layout);}
状態ごとに サブクラスや列挙型を定義