オブジェクト指向の設計ではいくつかの原則があります。
その中でも有名な原則の一つが「単一責任の原則」です。
この原則は、その名の通り「一つのクラスに持たせる責任は一つにする」という原則です。
この原則が何のために用いられるのかと言うと、複数の理由で1つのクラスを修正するのを防ぐためです。
ここで言う「理由」を「案件」、「クラス」を「ソース」と捉えると分かりやすいと思います。
複数の案件で1つのソースを修正する必要が出てしまうと、並行開発手順やバージョン管理等が煩雑になってしまい、品質に悪影響が及びます。
「一つのクラスに持たせる責任は一つにする」と捉えると具体的なイメージが浮かびにくいと思うのですが、「複数の案件で1つのソースを修正しないような粒度で設計する」と捉えると分かりやすいと思います。
以下では、javaのソースコードで例を出します。
商品の名前と値段を登録し、それを表示するという例です。
まずは悪い例からです。
【サンプルコード1】
・CommodityMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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】
1 |
PC:100000 yen |
ここで、「商品の税込み価格を計算可能にする」という案件と、「表示を綺麗にする」という案件が発生したとします。
今の設計だと、どちらの案件も RegistShowCommodityクラスの修正となってしまいます。
そこで、商品の登録を行う RegistCommodityクラス、商品の価格計算を行う CalcCommodityクラス、価格表示を行う ShowCommodityクラスの3クラスに分割します。
【サンプルコード2】
・CommodityMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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
1 2 3 4 5 6 7 |
public class CalcCommodity { public int calcPrice (int price) { return price; } } |
・ShowCommodity.java
1 2 3 4 5 6 7 8 |
public class ShowCommodity { public void showCommodityPrice (String commodityName,int price) { System.out.println(commodityName + ":" + price + " yen"); } } |
【実行結果2】
1 |
PC:100000 yen |
3クラスに分割した結果、「商品の税込み価格を計算可能にする」という案件は CalcCommodityクラスの修正、「表示を綺麗にする」という案件は ShowCommodityクラスの修正で対応できるようになり、別々の案件で1つのクラスを修正する状態を防ぐことができました。
【サンプルコード3】
・CommodityMain.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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
1 2 3 4 5 6 7 8 9 |
public class ShowCommodity { // 「表示を綺麗にする」案件 public void showCommodityPrice (String commodityName,int price) { System.out.println("■CommodityName :" + commodityName); System.out.println("■CommodityPrice:" + price + " yen"); } } |
【実行結果3】
1 2 |
■CommodityName :PC ■CommodityPrice:110000 yen |
いかがでしたでしょうか。
オブジェクト指向の設計と言うと覚えることが多くて大変だという印象を持つかもしれませんが、いくつかの原則を抑えるだけでも良い設計ができます。
(デザインパターンも、原則に則って設計を考えると自ずと導き出されるものだと思っています)
単一責任の原則は代表的な原則の一つなので、覚えておいて損はないと思います。
コメント