🚀

OSSのデザインパターン解説シリーズ:Adapter(Wrapper)パターンの活用と悪いコード例

2024/05/28に公開

1. はじめに

このシリーズでは、オープンソースソフトウェア(OSS)のソースコードを通じて、GoF(Gang of Four)デザインパターンの活用方法を解説します。
特に悪い例と良い例を見ることで、デザインパターンのメリットを考えていきます。

なお、OSSやデザインパターンの詳しい解説は書きません。
最小限のコード例と解説を書いていきます。

2. Adapterパターンとは

Adapterパターンとは、既にある機能を別の場所/用途/インターフェースでも使えるようにするパターンです。

既存のコードを別のコードに適用するので、Adapterパターンと言います。

実装では、既存のクラスを包む(Wrapperする)ようなコードになるので、Wrapperパターンとも呼ばれています。

今回は、Javaの以下のクラスを例に考えていきます:

  • HashMap
  • HashSet

3. 悪いコードの例

以下は、Adapterパターンを使用せずに書かれたJavaのコードの例です。

以下は、HashMapとHashSetの実装の一部です。説明のために、省略/変更しています。
※例えば、実際にはnullチェックやハッシュ値の衝突等を考える必要があります。

public class HashMap<K,V> implements Map<K,V> {
  Node<K,V>[] table;

  public V put(K key, V value){
    int n = table.length;
    int hash = hash(key);
    table[ hash % n ] = new Node(hash, key, value);
    return null;
  }
  //その他のメソッド

  static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    //実装
  }
}
public class HashSet<E> implements Set<E> {
  Node<E>[] table;

  public boolean add(E e) {
    int n = table.length;
    int hash = hash(key);
    table[ hash % n ] = new Node(hash, e);
    return null;
  }
  //その他のメソッド

  static class Node<E> {
    final int hash;
    E element;
    //実装
  }
}

このコードを比べると、共通部分が多そうです。
特にハッシュの計算や値の格納方法が似ています。

共通処理をまとめる方法としてユーティリティクラスが思いつきますが、今回の例ではAdapterパターンが適用できます。

4. デザインパターンの適用

以下は、Adapterパターンの実装例です。公式のJavaのソースコードも同じ構造になっています。

今回の場合は、HashMapをHashSetに適用(Adapter)しています。

HashMapの実装はそのままですが、HashSetのコードがかなりシンプルになっています。

public class HashMap<K,V> implements Map<K,V> {
  Node<K,V>[] table;

  public V put(K key, V value){
    int n = table.length;
    int hash = hash(key);
    table[ hash % n ] = new Node(hash, key, value);
    return null;
  }
  //その他のメソッド

  static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    //実装
  }
}
public class HashSet<E> implements Set<E> {
  HashMap<E,Object> map;
  static final Object PRESENT = new Object();//mapに格納するダミーの値

  public boolean add(E e) {
    return map.put(e, PRESENT)==null;
  }
  //その他のメソッド
}

5. 改善されたコードの比較

Adapterパターンを適用することで、HashMapとHashSetに散らばっていた共通処理をまとめることができました。これにより、保守性が上がりそうですね。

また、HashSetの実装はHashMapを利用するだけなので、簡単なコードになりました。

6. まとめ

今回はAdapterパターンを利用する/しない場合のコードを基に、Adapterパターンのメリットを考えました。
このデザインパターンは「実装がシンプルになり、保守性が上がることがメリット」と考えられますね。

7. 参考文献

  1. Java言語で学ぶデザインパターン入門第3版 結城 浩 SBクリエイティブ
  2. JDKソースコード
    3. JDK HashMap
    3. JDK HashSet

Discussion