• 検索結果がありません。

Preference への書き込みに個々で edit メソッドを呼んでいる

第 1 章 駄目コードとその対策 6

1.9 Preference への書き込みに個々で edit メソッドを呼んでいる

設定値や小さな値を保存するのに

SharedPreference

を使います。

SharedPreference

String

型以外のデータも

Key-Value

で値を保持できるのでちょっとした値を保存するのに便

利です。しかし便利ゆえにいくつかの弱点があります。

1. String

型でキーを指定しなければならず、しばしばキーを間違えてバグの原因になる

2.

誤って違うデータ型の値を入れると、取り出し時に

ClassCastException

がスローさ れる

これらの弱点は非常に厄介です。どのように厄介かというと、間違えてもコンパイルエラー にならず、実行時の例外やおかしな挙動を引き起こすからです。

リスト

1.26

を例に見てみましょう。このコードではボタンが2個あり、押されるとそのボ タン名を

SharedPreferences

に保存します。そして

Activity#onCreate

の中で

editText1

に そのボタン名を設定する処理になっています。動作には問題はありませんが前述の問題があ り、開発を進めていく上での不安となる点が残っています。

リスト1.26: 悪い例 SharedPreferencesDameActivity.java

public class SharedPreferencesDameActivity extends Activity implements View.OnClickListener {

private SharedPreferences mPref;

private EditText editText1;

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_many_handlers);

findViewById(R.id.button1).setOnClickListener(this);

findViewById(R.id.button2).setOnClickListener(this);

editText1 = (EditText) findViewById(R.id.editText1);

mPref = PreferenceManager.getDefaultSharedPreferences(this);

String lastButton = mPref.getString("lastButton", "");

editText1.setText(lastButton);

}

@Override

public void onClick(View v) { if (v.getId() == R.id.button1) {

mPref.edit()

.putString("lastButton", "button1") .apply();

} else if (v.getId() == R.id.button2) { mPref.edit()

.putString("lastButton", "button2") .apply();

} } }

SharedPreferences

に保存する際のキーは

"lastButton"

となっていますので、これを定数 化してみましょう。定数化したものはリスト

1.27

となります。

リスト1.27: 良い例 SharedPreferencesRefine1Activity.java

public class SharedPreferencesRefine1Activity extends Activity implements View.OnClickListener {

private SharedPreferences mPref;

private EditText editText1;

private String KEY_LAST_BUTTON = "lastButton";

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_many_handlers);

findViewById(R.id.button1).setOnClickListener(this);

findViewById(R.id.button2).setOnClickListener(this);

editText1 = (EditText) findViewById(R.id.editText1);

mPref = PreferenceManager.getDefaultSharedPreferences(this);

String lastButton = mPref.getString(KEY_LAST_BUTTON, "");

editText1.setText(lastButton);

}

@Override

public void onClick(View v) { if (v.getId() == R.id.button1) {

mPref.edit()

.putString(KEY_LAST_BUTTON, "button1") .apply();

} else if (v.getId() == R.id.button2) { mPref.edit()

.putString(KEY_LAST_BUTTON, "button2") .apply();

} }

}

見やすくなったでしょうか。そして前述の問題点は解決できているでしょうか。キー の

"lastButton"

が定数化されていますが、実は解決できていません。なぜならキーが複数ある 場合は、別のキーと取り違える可能性がまだ残っているからです。

こ の よ う な 場 合 、こ れ ら の 問 題 を 回 避 す る に は リ ス ト

1.28

の よ う な

SharedPref-erences

の ラ ッ パ ー ク ラ ス を 作 る と 回 避 で き ま す 。こ の ラ ッ パ ー ク ラ ス で は キ ー を

KEY_LAST_BUTTON

の定数にしていますが

private

になっているので、メソッドの呼

び元はキーを意識しなくてよくなっており、キーを取り違える心配もありません。またアクセ スする処理を

getLastButton

メソッドと

putLastButton

メソッドにまとめているので、誤っ て別のデータ型を入れることはありません。すなわちラッパークラスに前述の問題点を閉じ 込めているのです。

リスト1.28: MySharedPreferences.java

public class MySharedPreferences {

private static final String KEY_LAST_BUTTON = "lastButton";

private SharedPreferences mPref;

private SharedPreferences.Editor mEditor;

public MySharedPreferences(SharedPreferences mPref) { this.mPref = mPref;

}

public MySharedPreferences edit() { mEditor = mPref.edit();

return this;

}

public void apply() { mEditor.apply();

mEditor = null;

}

public String getLastButton() {

return mPref.getString(KEY_LAST_BUTTON, "");

}

public MySharedPreferences putLastButton(String value) { mEditor.putString(KEY_LAST_BUTTON, value);

return this;

} }

このラッパークラスを扱うように直したものがリスト

1.29

となります。

MySharedPrefer-ences

を使うようにしたことで、前述の問題がいずれも解決していることがわかります。

リスト1.29: 良い例 SharedPreferencesRefine2Activity.java

public class SharedPreferencesRefine2Activity extends Activity implements View.OnClickListener {

private MySharedPreferences mPref;

private EditText editText1;

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_many_handlers);

findViewById(R.id.button1).setOnClickListener(this);

findViewById(R.id.button2).setOnClickListener(this);

editText1 = (EditText) findViewById(R.id.editText1);

mPref = new MySharedPreferences(

PreferenceManager.getDefaultSharedPreferences(this));

String lastButton = mPref.getLastButton();

editText1.setText(lastButton);

}

@Override

public void onClick(View v) { if (v.getId() == R.id.button1) {

mPref.edit()

.putLastButton("button1") .apply();

} else if (v.getId() == R.id.button2) { mPref.edit()

.putLastButton("button2") .apply();

} } }

動けばいいという意味ではラッパークラス作らず、そのまま書いてしまっても違いはありま せん。しかしコード後から見たときや修正するときに、キーやデータ型のように手で合わせな いといけないものがそのままになっていると、誤って記述した際に不具合の原因となってしま います。特にこれらのキーやデータ型は誤って記述してしまうとコンパイルエラーにもなら ないため非常に厄介です。これら避けるため、ラッパークラスを作成して問題点の責務をラッ パークラスに持たせ、扱う側が無駄に悩まなくていいようにしましょう。ラッパークラスは単 にラッピングしているのではなく、そういった責務も封じ込めているのです。