今回はオブジェクト指向のプログラミングをするにあたり、避けては通れない「カプセル化」について説明します。
「カプセル化」と聞いて皆さんはどんなものをイメージしますか?
オブジェクト指向を理解するためには、コードのイメージすることが重要です。
今回は簡単な金額を扱うプログラムを例に「カプセル化」とはどのようなものなのか、具体的にまとめます。
カプセル化とは
カプセルコード化は、コードとコードが操作するデータをひとまとまりにして、外部の干渉や悪用から保護する仕組みです。
オブジェクト指向では、コードとデータを一つにまとめ、ブラックボックスを作成することができます。
この箱の中には、必要なデータとコードが全て入っているのです。
データとコードをリンクさせ、ボックスの中に閉じ込めるとオブジェクトを構成できます。
つまり、オブジェクトはカプセル化をサポートするための道具であると言うことがいえます。
C#のカプセル化の基本単位は、「クラス」です。
クラスはオブジェクトの雛形を定義して、データとデータに関する処理があります。
C#では、クラス定義を使用してオブジェクトを作成します…ry
文章だと分かりにくいので実演するよ
今回は「消費税」を扱う実装を例にして、具体的に「カプセル化」について説明します。
プロジェクトを作成しよう!
まず、Visual Studioを起動しましょう。
今回、エリ狐はVisual Studio 2019を使用しますが、2017でも2015でも可です。
図のように「Windowsフォームアプリケーション(.NETFrameWork)」を選択し「次へ」を押下します。
プロジェクト名称は適当で大丈夫です。
私は「Capsule」というプロジェクト名にして、作成しました。
すると、Form1.cs[デザイン]が表示されるので以下のプロパティ設定をしてください。
Form1のサイズ | size: 300, 300 |
---|---|
Labelの追加 | (Name):ShowLabel Text: XXXX |
Buttonの追加 | (Name):ShowButton Text: Show |
このようにラベルとボタンを配置してください。
次にボタンクリック時のイベント処理を追加します。
「Form1」に配置したボタンをダブルクリックするとボタンのクリックイベントが自動的に生成されます。
赤枠の処理が追加されたらOKです。
この枠で囲った処理は、処理名称(メソッド名)の通り、「ShowButton_Click」ボタンをクリックすると起動(発火)されます。
Webアプリケーション用語で”発火”とは、処理が実行されることを意味します(余談
Clickのほかにイベントを設定できます!
オブジェクトのプロパティー、「イナズママーク」をクリックすると、オブジェクトのイベントを選択することが可能です。
今回はクリックのイベントを用意したので「ShowButton_Click」と表示されています。
ここに、メソッド名を追加すると該当の処理がForm1.csに追加されるので、覚えておくとよいです!
処理を書いていく
本来であれば、データベースにアクセスして該当の金額データを取得したいところですが、色々説明が必要になってしまいます。
今回は、データベースより値を取得した程でプログラムを書いていきます。
using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Capsule { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void ShowButton_Click(object sender, EventArgs e) { var price = GetPrice(); ShowLabel.Text = price.ToString(); } private decimal GetPrice() { return 1000m; } } }
まずは「GetPrice」という何か金額を返却するメソッドを用意して、ラベルに表示するアプリを作成します。
ここでは、GetPriceがデータベースより値を取得した程で考えてください!
priceにGetPriceより取得した1000を格納します。
そして、ShowLabelのテキストプロパティに1000を文字列型に変換して代入します。
実際に実行すると…
このように、1000が表示されますね。
では、この値に消費税の10%をかけるにはどのようにすればよいでしょうか。
private void ShowButton_Click(object sender, EventArgs e) { var price = GetPrice(); ShowLabel.Text = (price * 1.1m).ToString(); }
このように、(price * 1.1m)としてあげればもちろん「1100.0」と表示されます。
今回のGetPriceよりデータベースやCSVなどから取得した値をdecimalやfloatなどの基本形を戻り値とすると、
その戻り値に対して、データを使う側(今回は画面側)でデータの加工作業が必要になります。
例えば、この画面以外にもその値を使う場合はそちらでも消費税を書けたり、単位をつけたりなどの処理が必要になってしまいます。
確かにそれもありなのですが…、「税抜き」の値も欲しくなったといった場合に値を2つ返さなければならなかったり…色々困ってしまいますよね。
関数を2つ作るとか、別のグローバル変数を使うかなどなど、イケてないコーディングになってしまいそうです。
そのようなときに必須になることが「カプセル化」です。
コードをカプセル化する
新しくクラスを準備します。
今回は金額を扱うクラスなので「Money」というクラス名にしました。
作成したら、Classのアクセス修飾子をどこからでもアクセスできるよう「Public」に変更します。
次にコンストラクタと、値を格納するためのプロパティ「Value」を作成します。
コンストラクタとはクラスのインスタンス化(初めて使うとき)に必ず実行される処理です。
初期化されるときに実行されるメソッドという感じで覚えておけばOKです。
プロパティを作成するときに小ネタがあるので、興味のある方はそちらもチェックしてみてください!
Moneyクラスは次のようにコーディングしました。
namespace Capsule { class Money { public Money(decimal value) { Value = value; } public decimal Value { get; } public decimal SetTax { get { return Value * 1.1m; } } } }
「public decimal Value { get; }」は読み取り専用のプロパティですので、他の処理から値を加工されるこのを防いでいます。
「SetTax」は呼び出すと消費税を書けた値を取得します。
つまり、MoneyクラスのValueを選択すると消費税がかかっていない値が取得でき、SetTaxを選択すると消費税がかけられた値を取得することができます。
それでは、Form1に「消費税付き」と「通常」ボタンを2つ追加して処理を見てみましょう!
このように配置したら、MoneyクラスのSetTaxを呼び出す処理と、Valueを呼び出す処理を定義してください。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Capsule { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void ShowButton_Click(object sender, EventArgs e) { var price = GetPrice(); ShowLabel.Text = (price * 1.1m).ToString(); } private decimal GetPrice() { return 1000m; } /* 追加 ここから */ //Show(Tax) private void button1_Click(object sender, EventArgs e) { var price = new Money(GetPrice()); ShowLabel.Text = price.SetTax + "円"; } //Show(NoTax) private void button2_Click(object sender, EventArgs e) { var price = new Money(GetPrice()); ShowLabel.Text = price.Value + "円"; } /* 追加 ここまで */ } }
次のように「button1_Click」と「button2_Click」を追加してそれぞれ実行してみてください!
実際に実行すると…
どこらへんが”カプセル化なのか”
実際にプログラムを書いてみたけれど、どこらへんが「カプセル化」されているのかというと、Moneyクラス全体です。
「public Money(decimal value)」のvalueはMoneyクラスのローカル変数(このクラス内でしか使えない値)です。
Publicで全体に公開している値は、関数越しや変数越しに参照しています。
つまり、privateで値を書き換えできるのはMoneyクラスだけになります。
外からアクセスできるものについてはPublicで宣言されているものだけで、privateの変数を加工して返却してあげる。
加工できるのは「Moneyクラスだけ」というようにすることが「カプセル化」という意味になります。
このようにしてあげることで、valueを加工するのはMoneyクラスだけだということが分かります。
さらに使用する側としては、仮に消費税が15%になっちゃったと修正が入ったとしてもSetTaxのかけている値を「1.1m」から「1.15m」に修正すれば全部直るというメリットがあります。
したがって、使用する側は何を使うのか”選択するだけ”でよいということになります。
使用する側にビジネスロジック(計算とか加工)を作成するのではなく、使われる側で値を保持してビジネスロジックを記載することが「カプセル化」ということになります。
このようにすると「Money」クラスには金額に関する処理が集まるようになります。
もしも、Moneyクラスがなかったら、画面のほうに同じようなロジックをそれぞれ書かなければならないため、ロジックが散らばってしまいます。
まとめ
カプセル化とは、コードとコードが操作するデータをひとまとまりにしたものです。
カプセル化を使用すると、消費税のように修正が必要になってしまってもカプセル化しているクラスの1部メソッドを修正すれば、参照している個所は全部直るというメリットがあります。
つまり、使用する側は何を使うのか”選択するだけ”でよいということになります。
基本的に値を使用する側にビジネスロジック(計算とか加工)を作成するのではなく、使われる側で値を保持することをビジネスロジックを記載することが「カプセル化」ということになります。
もしも、「カプセル化」をしていないと、使用する側にそれぞれ同じよなロジックを書かなければなりません。
修正が大変になってしまいバグの温床となってしまうので、オブジェクト指向のプログラミングに必須のスキルになるので覚えておいてください!
個人的にC#の経験がわりと長いので、このブログを通して色々アウトプットしていこうと思っています!
色々C#について小ネタだったり、プログラミングについて語っているので、ぜひこちらもチェックしてみてくださいね!