C#でオブジェクト指向プログラミングをする上で大切な3つのこと「カプセル化」「インタフェース」「継承」がありますが,今回はその中の「カプセル化」について解説します。
オブジェクト指向設計のカプセル化ってどういう意味?
カプセル化とはどんな意味かといいますと,値をクラスで包むことです。
例えばこんな感じです。
public class Money { private readonly decimal _value; public Money(decimal value) { _value = value; } public decimal Value { get { return _value; } } public decimal TaxValue { get { return _value * 1.08m; } } }
ポイントは一番上の「private readonly decimal _value; 」の部分ですね。
このMoneyクラスは「_value」というdecimalの値をカプセル化していることになります。
「_value」はprivateに設定されているため,他のクラスから直接「_value」の値にアクセスことはできませんよね?だからこの「_value」を書き換えることも,読み込むことも,Moneyクラスにお願いしないと変更することができない。こういう状態を「_value」はMoneyクラスにカプセル化されている状態といいます。
カプセル化されていると何がいいのか?
このMoneyクラスのポイントは「_value」がMoneyクラス経由でしか扱えないことです。
Moneyクラスの「_value」はお金の金額を示します。
公開されているプロパティの「Value」はそのままの金額「_value」の値が返却されますが,「TaxValue」を呼び出すと,消費税込みの値「1.08倍」された値が取得できます。
例えばあなたが,お金を扱うシステムの開発を行っている場合,消費税を加えた値というのは,いたるところで扱うことになります。
このMoneyクラスを作らずに,単にプログラム上に「decimal」を宣言にしてコーディングしていた場合,消費税込みの値として扱いたくなった場合はどうすればよいでしょうか?
画面プログラムなどに直接decimal変数を宣言する場合は,次のようなコーディングになりがちです。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { var value = GetAmount(); this.Text = (value * 1.08m).ToString(); } private decimal GetAmount() { return 100m; //仮実装 } }
取得したGetAmount()のdecimal値を直接取得すると,その値に対して消費税を足そうとすると,直接1.08倍をするコーディングになる可能性があります。
こうなってしまうと,プログラムのあらゆるところで,消費税の計算ロジックが書き込まれ,消費税か変化したときに,修正箇所が膨大となります。
共通関数を作ればいいのではないか?と思われる方もいるかもしれません。その場合,あらゆるところを修正する必要はなくなるかも知れませんが,それだけでは問題は解決しません。
仮に「TaxFunc」という共通関数を作ったとしましょう。
確かに「TaxFunc」のような関数を作れば1.08倍というマジックナンバーは共通化できます。しかし,「金額」という値を扱うときに,全プログラマーが共通意識を持って,どの程度その「TaxFunc」を使えているか?そして,金額を扱っている場所が何箇所あって,すべてが「TaxFunc」になっていると確認できるか?という問題があります。
「Money」クラスのよいところはずばりこの一言に尽きます
値とロジックが1つになっている
オブジェクト指向設計がうまくできない人はこれが理解できていません。
オブジェクト指向設計の大切な考え方「カプセル化」は「クラス」というものが存在している理由そのものです。「値」と「ロジック」を1つにまとめておく。これが,可読性の高いプログラムを作る秘訣です。
今回の場合はdecimal型の「_value」という「値」と「TaxValue」という1.08倍するロジックがいったいとなった「Money」というクラスを作成しました。decimal型だけでは到底表現できなかったことをMoneyクラスは実現しています。
カプセル化するとどんなメリットがあるのか?
これまで解説してきたようにカプセル化することで,次のようなメリットが得られます。
- コードが読みやすくなる
- 保守性が高まる
- ロジックが散らばらない(ロジックの住処になる)
- 値とロジックをセット考えることができる
カプセル化されていないと何がいけないのか?
一方カプセル化しなかったら何が悪いのか?decimal型を単体で持つことになるので,値とロジックを別々に持つことになります。値が生成されたときに,その値自体にロジックがあれば,プログラマーはどこも探すことなく,値に付随しているロジックを使用します。
C#でいえば「.ドット」をつけたときに出てくる「インテリセンス」の中から,関数を選ぶだけでよくなります。
だから,例えばデータベースから「金額」を取得する場合は「decimal」型などの数値クラスで取得するのではなく「Money」クラスなどのカプセル化したクラスで返却しましょう。そうすれば,「金額」を扱っている場所が何箇所あるのか?税込み計算はどのようにしているのか?ということも「右クリック.すべての参照を検索」とすれば調べることができます。
まとめ
値を扱うときはintなどの数値型で扱うのではなく「Money」クラスのようなカプセル化されたクラスを多用する。
カプセル化のポイントは「値」と「ロジック」を1つのクラスで扱う。