単一責任の原則とは

オブジェクト指向の設計ではいくつかの原則があります。
その中でも有名な原則の一つが「単一責任の原則」です。

この原則は、その名の通り「一つのクラスに持たせる責任は一つにする」という原則です。
この原則が何のために用いられるのかと言うと、複数の理由で1つのクラスを修正するのを防ぐためです。
ここで言う「理由」を「案件」、「クラス」を「ソース」と捉えると分かりやすいと思います。
複数の案件で1つのソースを修正する必要が出てしまうと、並行開発手順やバージョン管理等が煩雑になってしまい、品質に悪影響が及びます。

「一つのクラスに持たせる責任は一つにする」と捉えると具体的なイメージが浮かびにくいと思うのですが、「複数の案件で1つのソースを修正しないような粒度で設計する」と捉えると分かりやすいと思います。


以下では、javaのソースコードで例を出します。
商品の名前と値段を登録し、それを表示するという例です。

まずは悪い例からです。

【サンプルコード1】

・CommodityMain.java

・RegistShowCommodity.java

【実行結果1】


ここで、「商品の税込み価格を計算可能にする」という案件と、「表示を綺麗にする」という案件が発生したとします。
今の設計だと、どちらの案件も RegistShowCommodityクラスの修正となってしまいます。

そこで、商品の登録を行う RegistCommodityクラス、商品の価格計算を行う CalcCommodityクラス、価格表示を行う ShowCommodityクラスの3クラスに分割します。

【サンプルコード2】

・CommodityMain.java

・RegistCommodity.java

・CalcCommodity.java

・ShowCommodity.java

【実行結果2】


3クラスに分割した結果、「商品の税込み価格を計算可能にする」という案件は CalcCommodityクラスの修正、「表示を綺麗にする」という案件は ShowCommodityクラスの修正で対応できるようになり、別々の案件で1つのクラスを修正する状態を防ぐことができました。

【サンプルコード3】

・CommodityMain.java

・RegistCommodity.java

・CalcCommodity.java

・ShowCommodity.java

【実行結果3】


いかがでしたでしょうか。

オブジェクト指向の設計と言うと覚えることが多くて大変だという印象を持つかもしれませんが、いくつかの原則を抑えるだけでも良い設計ができます。
(デザインパターンも、原則に則って設計を考えると自ずと導き出されるものだと思っています)

単一責任の原則は代表的な原則の一つなので、覚えておいて損はないと思います。

クラス図とjavaソースの対応一覧

クラス図とはUMLの一種で、以下のような形でクラスの定義と各クラスの関係を表す設計図のことです。

今回は、クラス図で使われる記法とjavaソースの対応について、一覧にしてみました。
参考になれば幸いです。

【クラス定義】

【各クラスの関係】


いかがでしたでしょうか?

クラス図を書かなくともjavaのプログラムは作れてしまうので、情報を伝達したり整理したりするためにクラス図を書かなければならないとなった時に書き方を忘れてしまうことは意外とあると思います。
javaのプログラムに慣れた方であれば、日本語で説明されるよりもソースで説明された方がわかりやすいと思ったので、このような記事を書いてみました。

java:関数を引数として渡す

C#では、こちらの記事のようにdelegateの仕組みを利用して関数を変数として扱うことができます。
例えば、引数と引き渡してコールバック処理を実現することができます。

javaでも、8以降であれば関数型インターフェースを利用して関数を変数として扱うことができます。
以下、サンプルコードです。

【サンプルコード】

・FunctionMain.java

【実行結果】


上記のようにFunction型を用いるのが基本的な形なのですが、いくつか発展的な記法が存在します。

例えば、引数と戻り値が同じ型である場合は、UnaryOperator型を使用することでジェネリクスの表記を省略することができます。
(他にも、戻り値がvoidの場合に適用できるConsumer型、戻り値がboolean型の場合に適用できるPredicate型等が存在します)

また、引き渡す関数をラムダ式で表記することで、関数の定義を省略することができます。

【サンプルコード】

・FunctionMain.java

【実行結果】


いかがでしたでしょうか。

最近ではJava8以降の文法が使われる機会が多く、今回紹介したような関数型インターフェースも使われることが少なくありません。
従来のJavaと比べると書き方が独特に感じるかもしれませんが、慣れておくと実際の案件で困ることが少なくなると思います。

無名クラスとは

javaやC#では、インスタンス変数をnewする際に、後ろに中かっこを記述し定義を行うことができます。
ここで定義されるものは「無名クラス(匿名クラス)」と呼ばれ、クラス名を新たに定義することなく、インスタンス変数のクラス・インターフェースを継承・実装したクラスを定義することができます。
(ちなみに、「ラムダ式」と呼ばれる文法は「無名クラス」を応用したものになります)

以下、javaの例です。

【サンプルコード】

・AnonymousClass.java

・AnonymousInterface.java

・AnonymousMain.java

【実行結果】


いかがでしたでしょうか。

無名クラスは、使い捨てのクラスを作りたい時に便利です。
また、前述のラムダ式等を理解する上でも、無名クラスを理解する必要があります。

javaの入門書には出てくることが少ない文法だと思いますが、javaに慣れたらこの文法も早い内に理解することをお勧めします。

java:標準入出力の入力元・出力先を変更する

javaには
・System.setIn
・System.setOut
・System.setErr
の3つのメソッドが用意されており、これらのメソッドにより標準入出力の入力元・出力先を変更することができます。
これらのメソッドを利用することで、標準入力や標準出力を使用している処理の立ち振る舞いを外から変えることができます。

以下は、標準入力をハードコーディングで与え、標準出力・標準エラー出力を文字列として受け取りファイルに出力する例です。

【サンプルコード】

・SystemInOut.java

・SystemInOutMain.java

【処理結果】

コンソールは入出力無し

・C:\tmp\test1.txt

・C:\tmp\test2.txt


いかがでしたでしょうか。

簡易なシステムやレガシーなシステムだと、標準入出力を入出力とするプログラムが少なくないと思います。
そのようなプログラムに対して、外側から立ち振る舞いを変えられるのは便利です。
例えば、JUnitでテストする時に便利だと思いますし、リファクタリングを行う一つの手段にもなり得ると思います。

役に立ちそうな技があれば今後も紹介していきたいと思います!