1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Listもどきでほんわか理解するオブジェクト指向 カプセル化編

Last updated at Posted at 2018-10-08

はじめに

この記事は自分なりにオブジェクト指向を解釈し、なんとなくほんわかとオブジェクト指向について理解してもらうためのものとなっています。
今回はオブジェクト指向の中でもカプセル化を取り扱います。継承やポリモーフィズムに関しても今後多分かくと思います

###2018/10/10:追記
shiracamusさんから素晴らしいコメントを貰ったので、
C言語しかわからない人は以下の情報を参考にこの記事を読んでもらうとありがたいです

ちょっと乱暴だけど、C言語が分かる人なら、、、

  • クラス(class)は構造体(struct)の拡張版、データ型定義
  • データがないならクラスにしなくていい
  • 構造体データに対する処理関数(メソッド)をクラス定義内に書ける
  • クラスは名前空間になるので、別クラスで使われてるメソッド名と同じでもいい
  • new は malloc(sizeof(構造体))、なので変数は実はポインタ変数
  • 実はポインタ変数だけど*を付けないので、-> ではなく . でアクセス

あなたは非オブジェクト指向プログラマー

あなたは会社でプログラマーをしています。
あなたはプログラムを書く際、配列は使いづらいと感じていたので、全く新しいListという以下のような汎用的(たぶん)なプログラムを作りました。

list
#include <stdio.h>
#include <string.h>

typedef struct {
    int array[50];
    int length;
}List;

//Listの初期化関数
void init(List* list) {
    memset(list->array, 0, sizeof(list->array));
    list->length = 0;
}

//Listに数字を追加する関数
void add(List* list, int num) {
    list->array[list->length] = num;
    list->length = list->length + 1;
}

//Listから数字を取り出す関数
int get(List* list, int index) {
    //arrayから数字を取り出す処理
}

//Listから数字を削除する関数
void delete(List* list, int index) {
    //Listのarrayから一つ数字を削除する処理
}
使用方法
int main() {
    List list;
    init(&list);
    add(&list, 8);
    add(&list, 9);
    add(&list, 3);
    printf("%d %d %d\n", get(&list, 0), get(&list, 1), get(&list, 2));
    // 8 9 3
    return 0;
}

すばらしい出来栄えです!
あなたはすぐに開発中のプロダクトにこのListを使い始め、メンバーと共有しました。
しかし、使っていくうちに大きな不満が出てきてしまいます。

1.構造体と関数の関係性がわかりにくい

上記の4つの関数は同じファイルに書かれた構造体Listとセットで使う関数になっています。
しかし、構造体Listと4つの関数は分離しているため、セットで使用するものだとひと目で理解することは難しく、もしかしたら、セットの関数を使わず、構造体Listだけで使う人も出てくるかもしれません

構造体Listと4つの関数の関係をもっとわかりやすく表現できるような定義方法はないのでしょうか?

2.構造体Listの中身の値を直接変更できてしまう

この構造体Listは値を持つだけのもので、List内の値の取得、変更はすべて4つの関数に任されています。
また、4つの関数もListの値が直接操作されないことを前提として正常に動くよう作られています。
もし、プロダクトの開発メンバーの中で、そのことを知らない人がいた場合、Listの値を直接操作してしまってバグを生んでしまう可能性があります。

対処の方法としてはルールの周知徹底や作ったコードのダブルチェックといった方法がありますが、そんな泥臭い方法ではなく、もっと簡単に構造体Listの中身を直接操作できないことを表す、もしくは操作できないようにする方法はないのでしょうか?

それオブジェクト指向で解決できるよ

あなたはオブジェクト指向というものがあると知って、すぐにListを以下のように書き直しました。

list
public class List {
    private int[] array = new int[50];
    private int length = 0;

    public void add(int num) {
        array[length] = num;
        length++;
    }

    public int get(int index) {
        //arrayから数字を取り出す処理
    }

    public void delete(int index) {
        //arrayから数字を取り除く処理
    }
}
使用方法
public class Main {
    public static void main(String[] args) {
            var list = new List();
            list.add(8);
            list.add(9);
            list.add(3);
            System.out.printf("%d %d %d\n", list.get(0), list.get(1), list.get(2));
    }
}

元のコードと新しいコードはどのように違うのでしょうか?

1.変数と関数をセットで定義できるようになった

元のコードでは、構造体の定義と関数の定義は独立して定義されていました。
しかし、新しいコードでは、クラスというものに中に変数1と関数2を定義できるようになりました。
それにより変数と関数はセットで使うものだということをより簡潔に表現できています。

また+αとして、関数を実行する際にも事前にnewをするのが必要になりました。3

List list = new List();

これは元のコードのこの部分

List list; //arrayとlengthのメモリを確保
init(&list); //初期化処理

と似たような処理4をしています。
つまり、関数を使う前に必要な変数の作成と初期化を言語機能で強制してくれます。
より変数と関数がセットになったと言って過言はないでしょう!

これにより問題点1は解決しました
これにはあなたもニッコリです!

2.List内の変数が外部から直接触れなくなった

クラスの定義ではarrayとlengthに何やらprivateというキーワードがついています。
これはList内で定義されている関数2内でしかその変数3の値を変えることができないことを表しています。
元のコードではそのような制限をかけることができないため、List内の変数をいじりたい放題でした。
しかし、直接触られたくないList内の変数をprivateをつけて隠すことで、プログラムを作った人が意図していないような変数の操作を事前に防げるようになりました。

これで問題点2も解決しました!あなたはもう笑顔が止まりません!

##まとめ
変数の集合体(構造体)と関数を一つにまとめ、直接触ってほしくない変数なんかを隠し、関数から操作してもらうようにすることを、オブジェクト指向ではカプセル化と言うんだぞ☆

##おまけ
###自分が考えるオブジェクト指向とは
オブジェクト指向に対する自分の解釈は、
オブジェクト指向ができるまでのプログラミングにおける、バグの発生につながるような自由さを制限して、秩序を作り、規則性を増やして、バグを減らしたり、修正箇所を減らしたり、使いやすくしたり、コードを再利用しやすくする手法もしくは考え方なのかなと思っています。

よく関心の分離って言われますが関心ってなんだよって思ってます。(あほ)

あと、「オブジェクト指向とは、現実世界を正しく捉えること」と言われている人がいますが、それってモデリング手法であって、それをプログラムに落とし込むのにはオブジェクト指向の言語じゃなくてもできるよねと思ってます。
実際に解決したい問題を現実世界の事象に置き換えて考えることは、問題への理解を容易にしてくれるとは思います。
ですが、大抵の場合においてそれができるとは思いません。例えば、JavaのRandomクラスなんかは現実世界の事象に置き換えたものとは、到底言えません。

なぜそう言われるようになったのかはたぶん、問題を現実の事象に置き換えるモデリング手法を使った際に、プログラムにそのまま落とし込むのにオブジェクト指向の言語が一番合ってたからなんだろうなと思ってます。

あとクラスの使い方なんかを説明する際に身の回りのものを例にしたほうが考えやすくて、それが今現在のよくある勘違いにつながっているのかなと私は思います。

###どうオブジェクト指向を教えたらいいのか
自分がオブジェクト指向を勉強した際は、「Dogクラスとか実際作ることないだろ!!!!」とか思ってました。
現実の事象に置き換えて教えるのは、現実感がなさすぎて逆効果になりやすい気がします。(自分はだめだった)

オブジェクト指向からプログラミングをやり始めた人には、過去に起こっていた問題を知らないので、オブジェクト指向の利点がどのようなものなのかが理解できてません。
(ex:privateなメソッドを定義しておけば、無名関数やラムダ式なんていらなくね by自分)
新しく生まれたからにはなにか利点があるわけなので、
過去にこんな問題があって、今ではこれで解決できるんだよ
と実例を挙げながら過去と現在を対比させ利点を教えていくことで理解が深まるのかなと思います。

今回の記事そのような意識で書きました。

###最後に
もしこの記事でオブジェクト指向についてほんわかでも理解していただけたら幸いです

###参考になった記事
新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡 - Qiita
この記事よりも内容も濃いので見てちょんまげ!

  1. これは本来フィールドと呼ばれたりしますが、元のコードに合わせて変数とよんでいます

  2. これは本来メソッドといいますが、元のコードに合わせt(ry 2

  3. staticをつければnewしなくてもいいけど、そのことについては今回無視 2

  4. 元のコードだと静的にメモリを確保、新しいコードでは動的に確保していたりと違う点はあるのですが今回重要ではないので無視

1
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?