タグ付け出来る辞書をTDDで書いてみた。
TDDの練習として、ふと閃いた「タグ付け出来る辞書」を作ってみました。
仕様
細かい事は決めていなかったのですが、下記のようなのをイメージ。
その他、勉強用の仕様として、下記を追加。
- 総称型を使う
- assertThatを使う
- 可変長引数を使う
とりあえず、コードが書きたかったので、思いつきで書いてみました。
コード
- TagMapTest.java
package sample.tagmap; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.*; import java.util.Arrays; import org.junit.Before; import org.junit.Test; public class TagMapTest { TagMap<String, String> map; @Before public void setUp() throws Exception { map = new TagMap<String, String>(); } @Test public void 単一タグを持つマップを作成する() throws Exception { map.put("key", "value", "tag"); assertThat(Arrays.asList(map.getTags("key")), hasItem("tag")); } @Test public void 複数のタグを持つマップを作成する() throws Exception { map.put("key", "value", "tag1", "tag2"); assertThat(Arrays.asList(map.getTags("key")), hasItems("tag1", "tag2")); } @Test public void データを削除したらタグも削除されていること() throws Exception { map.put("key", "value", "tag"); map.remove("key"); assertThat(map.getTags("key"), nullValue()); } @Test public void タグを設定・削除できること() throws Exception { map.put("key", "value"); map.putTags("key", "tag1", "tag2"); assertThat(Arrays.asList(map.getTags("key")), hasItems("tag1", "tag2")); map.putTags("key", "tag10", "tag20"); assertThat(Arrays.asList(map.getTags("key")), hasItems("tag10", "tag20")); map.putTags("key", (String[]) null); assertThat(map.getTags("key"), nullValue()); } }
- TagMap.java
package sample.tagmap; import java.util.HashMap; /** * キーに対してタグを付けることが出来るMap * * @author sinsoku * * @param <K> * Key * @param <V> * Value */ @SuppressWarnings("serial") public class TagMap<K, V> extends HashMap<K, V> { HashMap<K, String[]> tagmap = new HashMap<K, String[]>(); /** * 指定されるキーに関連付られたタグを返します。 * * @param key * 指定されるタグが関連付けられるキー * @return 指定されたキーに関連したタグ。またはこのキーのタグが無かった場合は<code>null</code> */ public String[] getTags(K key) { return tagmap.get(key); } /** * 指定されたキーに指定された値とタグをこのマップに関連付けます。マップが以前にこのキーのマッピングを保持していた場合、古い値が置き換えられます。 * * @param key * 指定される値、タグが関連付けられるキー * @param value * 指定されるキーに関連付けられる値 * @param tags * 指定されるキーに関連づけれらるタグ * @return 指定されたキーに関連した値。または、キーのマッピングが無かった場合は<code>null</code>。 */ public V put(K key, V value, String... tags) { tagmap.put(key, tags); return super.put(key, value); } @Override public V remove(Object key) { tagmap.remove(key); return super.remove(key); } /** * キーに対して複数のタグを付ける事が出来る。マップが以前にこのキーのタグを保持していた場合、古い値が置き換えられます。 * * @param key * 指定されるタグが関連付けられるキー * @param tags * 指定されるキーに関連付られるタグ * @return 指定されたキーに関連したタグ。またはこのキーのタグが無かった場合は<code>null</code> */ public String[] putTags(K key, String... tags) { return tagmap.put(key, tags); } }
テストが少ないような気もしますが、タグに関するテストだと設定・追加・削除ぐらいなんだよな・・・
とりあえず、適当実装でこんな感じになりました。
まだ、継承元のHashMapのメソッドを適切にオーバーライドしていないので、clear()とか呼ばれると
タグだけ残るようなバグもあります。
また明日にでも弄ってみます。
追加機能
ここまで書いてから、追加機能として下記のような機能を考えてみた。
- タグのついているkeyを検索出来る。
- 複数のタグを指定して、タグの付いているkeyを検索出来る
- clone()出来る。
また、テストとして
- 総称型を確認するテストを書く。
現在の実装方法だと、タグからkeyを検索するのが難しいので、データ構造を見直す必要がありそう。
全体的にLRUキャッシュのよりはヌルイ題材ですし、TDDでやるには手頃だったかな。