オブジェクト指向の設計ではいくつかの原則があります。
その中でも有名な原則の一つが「単一責任の原則」です。
この原則は、その名の通り「一つのクラスに持たせる責任は一つにする」という原則です。
この原則が何のために用いられるのかと言うと、複数の理由で1つのクラスを修正するのを防ぐためです。
ここで言う「理由」を「案件」、「クラス」を「ソース」と捉えると分かりやすいと思います。
複数の案件で1つのソースを修正する必要が出てしまうと、並行開発やバージョン管理等が煩雑になってしまい、品質に悪影響が及びます。
「一つのクラスに持たせる責任は一つにする」と捉えると具体的なイメージが浮かびにくいと思うのですが、「複数の案件で1つのソースを修正しないような粒度で設計する」と捉えると分かりやすいと思います。
-----------------
以下では、javaのソースコードで例を出します。
商品の名前と値段を登録し、それを表示するという例です。
まずは悪い例からです。
【サンプルコード1】
・CommodityMain.java
public class CommodityMain {
public static void main(String[ ] args) {
RegistShowCommodity registShowCommodity = new RegistShowCommodity();
String commodityName = "PC";
// 商品の登録
registShowCommodity.setCommodityPrice(commodityName, 100000);
// 商品の表示
registShowCommodity.showCommodityPrice(commodityName);
}
}
・RegistShowCommodity.java
import java.util.HashMap;
import java.util.Map;
public class RegistShowCommodity {
private Map<String, Integer> commodityMap
= new HashMap<String, Integer>();
public void setCommodityPrice (String commodityName,int price) {
commodityMap.put(commodityName,price);
}
public void showCommodityPrice (String commodityName) {
System.out.println(commodityName + ":" +
commodityMap.get(commodityName) + " yen");
}
}
【実行結果1】
PC:100000 yen
-----------------
ここで、「商品の税込み価格を計算可能にする」という案件と、「表示を綺麗にする」という案件が発生したとします。
今の設計だと、どちらの案件も RegistShowCommodityクラスの修正となってしまいます。
そこで、商品の登録を行う RegistCommodityクラス、商品の価格計算を行う CalcCommodityクラス、価格表示を行う ShowCommodityクラスの3クラスに分割します。
【サンプルコード2】
・CommodityMain.java
public class CommodityMain {
public static void main(String[ ] args) {
RegistCommodity registCommodity = new RegistCommodity();
CalcCommodity calcCommodity = new CalcCommodity();
ShowCommodity showCommodity = new ShowCommodity();
String commodityName = "PC";
// 商品の登録
registCommodity.setCommodityPrice(commodityName, 100000);
// 商品の表示
showCommodity.showCommodityPrice(commodityName,
calcCommodity.calcPrice(
registCommodity.getCommodityPrice(commodityName)));
}
}
・RegistCommodity.java
import java.util.HashMap;
import java.util.Map;
public class RegistCommodity {
private Map<String, Integer> commodityMap
= new HashMap<String, Integer>();
public void setCommodityPrice (String commodityName,int price) {
commodityMap.put(commodityName,price);
}
public int getCommodityPrice (String commodityName) {
return commodityMap.get(commodityName);
}
}
・CalcCommodity.java
public class CalcCommodity {
public int calcPrice (int price) {
return price;
}
}
・ShowCommodity.java
public class ShowCommodity {
public void showCommodityPrice (String commodityName,int price) {
System.out.println(commodityName + ":" +
price + " yen");
}
}
【実行結果2】
PC:100000 yen
-----------------
3クラスに分割した結果、「商品の税込み価格を計算可能にする」という案件は CalcCommodityクラスの修正、「表示を綺麗にする」という案件は ShowCommodityクラスの修正で対応できるようになり、別々の案件で1つのクラスを修正する状態を防ぐことができました。
【サンプルコード3】
・CommodityMain.java
public class CommodityMain {
public static void main(String[ ] args) {
RegistCommodity registCommodity = new RegistCommodity();
CalcCommodity calcCommodity = new CalcCommodity();
ShowCommodity showCommodity = new ShowCommodity();
String commodityName = "PC";
// 商品の登録
registCommodity.setCommodityPrice(commodityName, 100000);
// 商品の表示
showCommodity.showCommodityPrice(commodityName,
calcCommodity.calcPriceWithTax(
registCommodity.getCommodityPrice(commodityName)));
}
}
・RegistCommodity.java
import java.util.HashMap;
import java.util.Map;
public class RegistCommodity {
private Map<String, Integer> commodityMap
= new HashMap<String, Integer>();
public void setCommodityPrice (String commodityName,int price) {
commodityMap.put(commodityName,price);
}
public int getCommodityPrice (String commodityName) {
return commodityMap.get(commodityName);
}
}
・CalcCommodity.java
public class CalcCommodity {
private double taxRate = 1.1;
public int calcPrice (int price) {
return price;
}
// 「商品の税込み価格を計算可能にする」案件
public int calcPriceWithTax (int price) {
return (int) (price * taxRate);
}
}
・ShowCommodity.java
public class ShowCommodity {
// 「表示を綺麗にする」案件
public void showCommodityPrice (String commodityName,int price) {
System.out.println("■CommodityName :" + commodityName);
System.out.println("■CommodityPrice:" + price + " yen");
}
}
【実行結果3】
■CommodityName :PC
■CommodityPrice:110000 yen