デザインパターン プログラミング Java

【TemplateMethodパターン】売上レポートをテキストとHTML形式で出力分けする事例

投稿日:

概要

デザインパターンのTemplateMethodパターンの事例です。
デザインパターンは増補改訂版 Java言語で学ぶデザインパターン入門で学ぶことができますし、Qiitaでも取り上げられています。

ここで紹介するのは、私が自分で適用可能な状況を考えて実装した事例です。
本の写経やブログを読んで終わりではなく、どのような状況でどのように実装できるか?
と自分なりに考えることの重要性は、増補改訂版 Java言語で学ぶデザインパターン入門でも述べられています。

本を読んだり、写経したり、Qiitaを見て真似してみたけど、実際にどうやって使うんだろうか?
という疑問を持っている読者の方に、またオブジェクト指向プログラミングを身に着けたい方の参考になればと思います。

TemplateMethodパターンとは?

説明

ロジックが共通しているが、具体的な処理内容はサブクラスで実装するパターンです。
ロジック部分はスーパークラスで実装されており、ロジック内にある個別の処理内容をサブクラス固有の実装にすることで、ロジックの使いまわしと個別処理の実装を分離することができます。

共通のロジックがコピー&ペーストでいろんな場所に分散すると、修正することも、テストすることも大変になります。
そういった手間をかけないようにするためのパターンです。

クラス図


スーパークラスのSalesReportでoutputメソッドが共通ロジックになっており、その中でtitle、detail、summaryメソッドが実行されるようになっています。
title、detail、summaryメソッドをサブクラスであるTextSalasReportとHtmlSalesReportで個別に実装することで、表示形式を分離しています。

実装

SalesReportクラス

売上レポートのスーパークラス。
・title、detail、summaryの表示形式はサブクラスで定義するため抽象メソッドとしている。
・outputメソッドは共通ロジックであり、変更不可のためfinalとしている。
・summaryAmount、yenメソッドも共通ロジックですが、個別の実装を要求するものではなく、サブクラスで利用できるように提供しているだけです。

TextSalesReportクラス

テキスト形式で売上レポートを出力するクラスです。
出力形式の仕様は以下のようになります。
< yyyy年M月売上レポート >
A社 : 5000000円
B社 : 2500000円
C社 : 500000円
合計 : 8000000円

HtmlSalesReportクラス

HTML形式で売上レポートを出力するクラスです。
出力形式の仕様は以下のようになります。
<h1>yyyy年M月売上レポート</h1>
<table>
<tr><td>A社</td><td>5000000円</td></tr>
<tr><td>B社</td><td>2500000円</td></tr>
<tr><td>C社</td><td>500000円</td></tr>
<tr><td>合計</td><td>8000000円</td></tr>
</table>

SalesRepotMainクラス

売上レポートを表示するためのクラスです。

<出力結果>

サブクラスで実装した仕様の違い

 TextSalesReportクラスHtmlSalesReportクラス
titleメソッドタイトルを<>で囲って表示する。
タイトルの前後に半角スペースを入れること。
タイトルをh1タグで囲って表示する。
detailメソッド会社名と金額をコロンで区切って表示する。
会社名の前と、コロンの前後に半角スペースを入れること。
trタグ内に、tdタグで会社名と金額を表示する。
ただし、最初のtrタグを出力する時だけ、tableタグを追加しておく。
summaryメソッド"合計"と合計金額をコロンで区切って表示する。
"合計"の前と、コロンの前後に半角スペースを入れること。
trタグ内に、tdタグで"合計"と合計金額を表示する。
また、tableの閉じタグを追加しておく。

もし他の出力形式でレポートを出力する場合、ロジックのコピー&ペーストすることなく、別のクラスを追加することで対応できます。
例えば、CSV形式のファイルを出力する必要が出てきたら、CsvSalesReportクラスを作成すればよいということになります。
※サブクラスを追加した結果、スーパークラスに機能追加することが無いという意味では無く、必要に応じてリファクタリングは行われるべきかなとは思っています。

付録(テストコード)

新たなサブクラスの追加でスーパークラスの修正も行った場合、既存のサブクラスに影響を与える可能性があります。
そのためにテストコードを書いておくことはとてもとても重要だと思っています。

リファクタリングでも、既存のコードを修正するのであればテストコードを用意しておくことが望ましいと述べられています。
動いているコードを修正するリスクは高いのです!
テストコードを書けるのであれば、できる限り書くようにしていきましょう。

module-info

Java11の環境であるため、module-infoを利用しています。

TestTextSalesReport

TextSalesReportクラスのテストコードです。
個別に実装するメソッドのテストを行っています。

TestHtmlSalesReport

TestHtmlSalesReportクラスのテストコードです。
TestTextSalesReportと同様に、個別に実装するメソッドのテストを行っています。

テストコードのメリット

実際のところ、この記事を書きあげるまでに何度かコードを書き直しました。
まさしく、リファクタリングを行ったわけです。
その際、テストコードがあったおかげで私は自分の修正に確からしさを感じることができました。

よく「こんな小さな修正でバグが出るはずがない」という思い込みをすることがあります。
(読者のあなたにもあれば、要注意です!)
それでも、ポカはするものなのです。
なので、テストコードがあれば、自分の修正に問題がないことをある程度は保証することはできます。

ですから、何度も言うようですが、テストコードを書けるのであれば、できるだけ書いた方がよいです。
プロジェクト体制やスキルの問題もあると思いますが、私としては自分の仕事に確からしさをもたらし、品質と効率を求めていきたいと思っています。

まとめ

  • 共通ロジックをコピー&ペーストすることなく使いまわすことができるパターンである。
  • 個別の処理内容はサブクラスで自由に実装可能である。
  • 追加のサブクラスが必要になった場合、単純に追加するだけで対応可能である。

参考書籍

増補改訂版 Java言語で学ぶデザインパターン入門

 
最後までお読み頂き、ありがとうございました!
ご意見・ご要望がありましたら、遠慮なくコメント下さい!
もし内容が良かったらランキング評価を頂けると励みになります(^^)

ランキング評価する

-デザインパターン, プログラミング, Java
-, ,

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


関連記事

【Java】Optionalが処理できないならデフォルト値を返そう

概要 Optionalはnullである可能性を明示的に返してくれる便利なコンテナ・オブジェクトです。 if(obj == null)… といったことをしなくてよくなったのは素晴らしいことだ …

no image

【PHP】配列と連想配列と多次元配列の説明と使用例

配列とは? 1つの変数に複数の値を持つものです。 整数型や文字列型は1つの変数に1つの値を持ちますが、配列は同じようなデータの集まりを1つの変数に持つことができるようになっています。 配列には通常の添 …

【Java】TreeSetの使い方(順序付けSet)

TreeSetの使い方 基本的な使い方はHashSetと同じなので、以下を参照下さい。 【Java】HashSetの使い方いろいろ TreeSetは何も指定せずにインスタンスを生成すると、Collec …

Laravelのヘルパー関数とは?

Laravel includes a variety of global “helper” PHP functions. Many of these functions are …

SingletonパターンをJUnitでテストする

シングルトンパターンはインスタンスが1つであることを保証する設計です。 Junitを使って、本当に1つだけなのかを検証してみます。 またその手順をテスト駆動開発に従った手順(Red→Green→リファ …

記事を探す

SpringFramworkの良書

■おすすめ教材
Javaの基本を学んだ人が、次のステップとして読む本(中級者向け)

※SpringはJavaのFrameworkの1つです。
変更のしやすさ、保守性の高さが特徴です。

サイト内検索