本稿ではデザインパターンのうちの1つ、「Adapterパターン」について解説します。すでに用意されているインターフェースをアプリケーションで使いやすい形に「変換」し、違いを吸収するデザインパターンです。
Adapterパターンの例
Portfolioクラスは、ユーザーの株式購入履歴を追跡するアプリケーションの基礎となるものです。当然ながら、ポートフォリオに最も求められる機能は、これらの株式購入の価値を計算することです。
PortfolioTestクラス(リスト1を参照)は、Portfolioクラスが値を正しく取得できることを検証するのに役立つ単純なスタブの使用例を示しています。Portfolioクラス(リスト2を参照)は、StockLookupServiceインターフェイスを通じて呼び出しを行うことで、各シンボルの価格を取得します。StockLookupServiceのしくみは単純です。このサービスは、株式シンボルが与えられると、そのシンボルの現在のドル価格を返します。StockLookupServiceインターフェイスの定義は次のとおりです。
public interface StockLookupService { int currentPrice(String symbol); }
import static org.junit.Assert.*; import org.junit.*; public class PortfolioTest { private static final String MICROSOFT = "MSFT"; private static final int MICROSOFT_VALUE = 100; private static final String IBM = "IBM"; private static final int IBM_VALUE = 80; private Portfolio portfolio; @Before public void initialize() { StockLookupService service = new StockLookupService() { @Override public int currentPrice(String symbol) { if (symbol.equals(MICROSOFT)) return MICROSOFT_VALUE; if (symbol.equals(IBM)) return IBM_VALUE; return 0; } }; portfolio = new Portfolio(service); } @Test public void isEmptyOnCreation() { assertSize(0); assertEquals(0, portfolio.value()); } @Test public void storesSharesPerSymbol() { portfolio.purchase(MICROSOFT, 2); assertSize(1); assertEquals(2, portfolio.shares(MICROSOFT)); assertEquals(2 * MICROSOFT_VALUE, portfolio.value()); } @Test public void sumsSharesPurchasedForSameSymbol() { portfolio.purchase(MICROSOFT, 1); portfolio.purchase(MICROSOFT, 2); assertSize(1); assertEquals(3, portfolio.shares(MICROSOFT)); assertEquals(3 * MICROSOFT_VALUE, portfolio.value()); } @Test public void segregatesPurchasesBySymbol() { portfolio.purchase(MICROSOFT, 5); portfolio.purchase(IBM, 10); assertSize(2); assertEquals(5, portfolio.shares(MICROSOFT)); assertEquals(10, portfolio.shares(IBM)); int expectedValue = 5 * MICROSOFT_VALUE + 10 * IBM_VALUE; assertEquals(expectedValue, portfolio.value()); } void assertSize(int expected) { assertEquals(0 == expected, portfolio.isEmpty()); assertEquals(expected, portfolio.size()); } }
import java.util.*; public class Portfolio { private Map<String, Integer> symbols = new HashMap<String, Integer>(); private StockLookupService service; public Portfolio(StockLookupService service) { this.service = service; } public boolean isEmpty() { return 0 == size(); } public int size() { return symbols.size(); } public void purchase(String symbol, int shares) { symbols.put(symbol, shares(symbol) + shares); } public int shares(String symbol) { if (!symbols.containsKey(symbol)) return 0; return symbols.get(symbol); } public int value() { int total = 0; for (Map.Entry<String, Integer> holding: symbols.entrySet()) { String symbol = holding.getKey(); int shares = holding.getValue(); total += service.currentPrice(symbol) * shares; } return total; } }
このPortfolioクラスは、外部APIを呼び出す方法の簡単な例を示しています。通常、このような外部APIは自分の制御下にありません。今回のサンプルでは、NASDAQ証券取引所などの実際のAPIを呼び出す代わりに、いくつかの既知の株式の価格を提供するテストダブル(test double:テスト用の代役の意)を用意する必要があります。このテストダブルにより、決まった「答え」との比較を行う単体テストを書くことができます。
ところで、StockLookupServiceインターフェイスはどこから来るのでしょうか。ベンダのコードと一緒に提供されることもありますが、自分で作成しなければならないこともあります。まずはAPIの一部として提供されるインターフェイスについて考えてみましょう。
この記事は参考になりましたか?
- デザインパターンの使い方連載記事一覧
- この記事の著者
-
japan.internet.com(ジャパンインターネットコム)
japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
Jeff Langr(Jeff Langr)
本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle Bob Martin著、Prentice Hall、2008年8月)にも寄稿している。また、ソフトウェア開発に関する記事を80件以上執筆しており、そのうちの...
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
