オブジェクト指向プログラミングの3大要素 - Good Concepts in Programming -

# 概要
プログラミングの良い概念を理解するとプログラマーレベルが上がると考えています。
良い概念は特定のプログラミング言語に縛られないので、様々なプログラミング言語で役に立ちます。
前回は[関数は変数である](https://qiita.com/masaki_nakada_ari/items/183ce067efd486f77cf0)を紹介しました。
今回紹介する良い概念は「オブジェクト指向プログラミングの3大要素」です。

また、この記事はARIのアドベントカレンダーの前哨戦として、
ARIの名古屋支社メンバが毎日（土日は除く）投稿していく、
**「12/1までをカウントダウンするアドベントカレンダ」の3日目になります！**

**ARIのアドベントカレンダも3日後に始まる**ので、
こちらも応援のほどよろしくお願いします♪

https://qiita.com/advent-calendar/2022/ari-group

# オブジェクト指向プログラミングの3大要素
以前の記事[やらない理由を書いてやらない](https://qiita.com/masaki_nakada_ari/items/b702b38c4ac2dee7561d)ではプログラミングの3大要素について少し書きました。
オブジェクト指向プログラミング言語にはC#、Java、Kotolin、Swift、Ruby、Python、JavaScriptなどがあります。
現在広く使われているプログラミング言語の多くはオブジェクト指向プログラミング言語であるため、
オブジェクト指向とは何かを理解することはとても重要だと考えています。
オブジェクト指向プログラミングの3大要素は「カプセル化」、「継承」。「多態性（ポリモーフィズム）」です。

# 1.カプセル化
カプセル化はデータと操作を1つにまとめ、外部に対して必要な情報のみを提供することです。
オブジェクト指向プログラミング言語でカプセル化を実現する方法はクラスになります。
カプセル化を意識したクラス設計をすることで凝集度が高くなり、良いクラスになると考えています。
以下、サンプルコードです。

``` BadContact.cs
class Contact
{
    Contact(string zipCode, string address, string name)
    {
        ZipCode = zipCode;
        Address = address;
        Name = name;
    }
    
	string ZipCode { get; set; }
	string Address { get; set; }
	string Name { get; set; }
}

class MainClass
{
    static void Main(string[] args)
    {
        var con = new Contact("100-0000", "Tokyo", "Hedi Slimane");
        var dest = con.ZipCode + " " + con.Address + " " + con.Name;

        Console.WriteLine(dest);
    }
}
```
Contact（連絡先）クラスを定義していますが、データ（プロパティ）定義だけです。
カプセル化を意識して、以下の修正をします。
・コンストラクター以外でデータ（プロパティ）の設定をできなくする。
・宛先を作成するメソッドをクラス内に定義する。

``` GoodContact.cs
class Contact
{
    Contact(string zipCode, string address, string name)
    {
        ZipCode = zipCode;
        Address = address;
        Name = name;
    }

    // setをprivateにして、クラス外から設定できなくする。
	string ZipCode { get; private set; }
	string Address { get; private set; }
	string Name { get; private set; }

    // 宛先を作成するメソッドを定義する。
    string CreeateDestination()
    {
        return con.ZipCode + " " + con.Address + " " + con.Name;
    }
}

class MainClass
{
    static void Main(string[] args)
    {
        var con = new Contact("100-0000", "Tokyo", "Hedi Slimane");
        var dest = con.CreateDestination();

        con.Name = "Thom Browne"; // ビルドエラーになる。

        Console.WriteLine(dest);
    }
}
```
データと操作を1つにまとめ、外部に対して必要な情報のみを提供することができました。

::: note info
クラス設計において、データと操作を1つにまとめることを意識するのは大切です。
:::

# 2.継承
継承はスーパークラス（親クラス）で定義した変数やメソッドをサブクラス（子クラス）に引き継ぐことができる概念です。
スーパークラスに同じ特性をまとめることによってサブクラスで再利用できます。
[OAOO原則](https://qiita.com/masaki_nakada_ari/items/1b4e1ea9a758ef86a498)を実現するための強力が概念ですが、使用方法には注意が必要です。

## Is-a関係
一般的にBがAを継承する場合、**"B is a A."**（BはAの一種である）という関係（**<font color="DarkOrange">Is-a関係</font>**）が成り立ちます。
以下、サンプルコードです。

``` Flower.cs
class Flower
{
    string Name { get; set; }
}

class Sunflower : Flower
{
}

class Dandelion : Flower
{
}

class Rose : Flower
{
}
```
::: note info
**Sunflower <font color="DarkOrange">is a</font> Flower.**　ヒマワリは花である。
**Dandelion <font color="DarkOrange">is a</font> Flower.**　タンポポは花である。
**Rose <font color="DarkOrange">is a</font> Flower.**　バラは花である。
:::
上記全てにIs-a関係が成り立ちます。

## 不適切な継承関係
同じデータや操作を持つ場合でも、意味的に無関係なクラス間に継承関係を持たせるのは適切ではありません。

``` Lion.cs
class Flower
{
    string Name { get; set; }
}

class Lion : Flower
{
    // ライオンは名前も持つが、花を継承するべきではない。
}
```
::: note alert
**Lion <font color="DarkOrange">is not a</font> Flower...**　ライオンは花ではない…
:::
Name（名前）というプロパティ（特性）があるからといってFlowerクラスを継承してLionクラスを作成するのは絶対にしないでください。
Flowerクラスを変更した場合、不要なデータや操作がLionクラスに継承されてしまいます。
スーパークラスの変更は全てのサブクラスに影響します。適切な継承関係を考えて使用しましょう。

# 3.多態性（ポリモーフィズム）
多態性とは同じメソッドを使用しても、作成するオブジェクト（newするクラス）によって結果が異なる特性のことです。
多態性はカプセル化（クラス定義）や継承（全てのクラスは暗黙的にobjectを継承している）のように
オブジェクト指向プログラミングで必須（避けては通れないもの）ではないので、
プログラミング初心者が理解するには時間がかかるかもしれません（私は時間がかかりました）。
しかし多態性を理解することでオブジェクト指向プログラミングの理解とプログラミングレベルは飛躍的に上がると考えています。

**<font color="DarkOrange">多態性は時間をかけても理解する価値はあるのでぜひ理解してほしい！</font>**

多態性を実現する方法にはオーバーライド（上書き）もありますが、今回はインターフェイスを使用します。
以下、サンプルコードです。

``` EnglishGreeting.cs
interface Greeting
{
    string SayInTheMorning();
    string SayAtNoon();
    string SayAtNight();
}

class EnglishGreeting : Greeting
{
    string SayInTheMorning()
    {
        return "Good morning.";
    }
    
    string SayAtNoon()
    {
        return "Hello.";
    }

    string SayAtNight()
    {
        return "Good night.";
    }
}

class MainClass
{
    static void Main(string[] args)
    {
        var greeting = new EnglishGreeting();

        Console.WriteLine(greeting.SayInTheMorning()); // Good Morning.
        Console.WriteLine(greeting.SayAtNoon()); // Hello.
        Console.WriteLine(greeting.SayAtNight()); // Good night.
    }
}
```
Greeting（挨拶）するインターフェイスです。英語で挨拶するクラスを実装しています。
日本語で挨拶するクラスを実装してみましょう。

``` JapaneseGreeting.cs
class JapaneseGreeting : Greeting
{
    string SayInTheMorning()
    {
        return "おはようございます。";
    }
    
    string SayAtNoon()
    {
        return "こんにちは。";
    }

    string SayAtNight()
    {
        return "おやすみなさい。";
    }
}

class MainClass
{
    static void Main(string[] args)
    {
        // 作成するオブジェクトをJapaneseGreetingに変更する。
        var greeting = new JapaneseGreeting();

        // 同じメソッドを呼び出しているが、出力結果が異なる。
        Console.WriteLine(greeting.SayInTheMorning()); // おはようございます。
        Console.WriteLine(greeting.SayAtNoon()); // こんにちは。
        Console.WriteLine(greeting.SayAtNight()); // おやすみなさい。
    }
}
```
EnglishGreetingクラスとJapaneseGreetingクラスはどちらもGreetingインターフェイスを実装しています。
同じメソッドを呼び出しているが、出力結果が異なることがポイントです。

::: note info
多態性によって同じ操作をしても、結果が異なる。
:::

# まとめ
今回はオブジェクト指向プログラミングの3大要素を紹介しました。
オブジェクト指向プログラミング言語の特性を理解して使いこなすのはとても大変です。
プログラミング言語の仕様だけではなく、原理原則を体系的に理解するのが大事だと考えます。
オブジェクト指向プログラミングの3大要素を理解してプログラマーレベルが上がったと感じていただければ幸いです。

最後まで読んでいただきありがとうございます。