ユーニックス総合研究所

  • home
  • archives
  • programming-object-shikou-chusyouka

オブジェクト指向プログラミングで抽象化する方法を解説します

オブジェクト指向で抽象化する方法

プログラミングをやっているとオブジェクト指向は身に付けておきたい技術です。
それでオブジェクト指向的にコードを抽象化するにはどうしたらいいんでしょうか。

結論としては↓です。

  • クラスにデータを持たせる
  • クラスのメソッドを抽象的にする

今回はこの方法について具体的に、またオブジェクト指向や抽象化とはなにか?
という基本的なところから解説します。

この記事を読めばオブジェクト指向の威力、便利さがわかります。

関連記事

オブジェクト指向プログラミングで抽象化する方法を解説します
C言語でオブジェクト指向する【単一継承の方法】

オブジェクト指向とはなにか?

まずは基本的なところを確認しておきましょう。
オブジェクト指向とはなんなのでしょうか?

これはプログラミングにおける特定の処理のまとまり、データを「オブジェクト」として扱うことを言います。
そしてそのオブジェクトを駆使してプログラミングをするのがオブジェクト指向プログラミングです。

これは具体的に言うとクラスを使います。
クラスにデータと関数を持たせて、そのクラスから生成したものをオブジェクトと呼びます。

オブジェクトのデータや関数を使い、ほかのオブジェクトとも連携させてプログラミングします。

普通の手続き的な処理と比較して違うのはどんなところか?
それはオブジェクト指向はデータと関数を一体的に表現しているところです。

つまりデータがあり、それに関連する関数をメソッドとしてまとめているのです。
これがオブジェクト指向は簡単な概要です。

データと関数をクラスにするのがオブジェクト指向の基本

データと関数をクラスにまとめるのがオブジェクト指向の基本です。
逆に言うとデータと関数がクラスの中で関連していないなら、それはオブジェクト指向的ではありません。

オブジェクトに持たせるデータは絶対です。
そのデータに従属するのがオブジェクトのメソッドです。

もっともデータを持たせないでメソッドだけを定義するクラスなどもあります。
こういうのは例外です。

オブジェクトは1つの物体として扱います。
つまり車クラスであればガソリンと言うデータがあり、それをアクセルというメソッドで消費します。

車と車の衝突処理はどこにやらせるか、これは議論がありますが、たとえばマップに衝突判定させる方法もあります。
データとメソッドを車と言うオブジェクトにまとめ一体的に扱い、それを1つの物体として扱う。
そうすることで「車同士の衝突」という抽象的な処理を書けるというわけです。

抽象化とはなにか?

ではプログラミングにおける抽象化とはいったい何なのでしょうか?
まずは「抽象化」を辞書で引いてみましょう。

抽象化 【abstraction】
抽象化とは、対象から細部や具体性を取り去り、本質的に重要な要素や、着目している側面のみを取り出して、一つの概念として定義すること。 また、異なる複数の対象に共通する性質や要素を見出し、共通点を組み合わせて汎用的な概念を構成すること。

という意味になっていました。
つまり細かいところをはしょって重要なところを抜き出し、それを扱うようにする。
というのが抽象化です。

具体的で詳細な手続きをやわらかく抽象的にする

プログラミング言語で書いた詳細なコードがあるとします。
関数の中では変数が定義されてループなどの処理が回っています。

変数は色々なところで参照されて、この関数は一連の処理を行います。
これもある意味抽象化している状態ですが、まだ細部は鮮明です。
具体的には変数やループなどがむき出しになっています。

そういった処理や変数をクラスにまとめて、クラスのメソッドに仕事をさせます。
そうすると詳細な処理はクラスという抽象的なものになります。

たとえば関数の中には車のガソリン残量やタイヤの消耗度など変数が散らばっていて、それを1つの「車」と言うクラスにまとめます。
こうすることで抽象化が実現されます。

抽象化することで得られるメリット

抽象化することで得られるメリットは何があるでしょうか?

これはいろいろあります。
具体的には以下の3つがそうです。

  • 生産性の向上
  • メンテナンス性の向上
  • コードの可読性の向上

生産性の向上

抽象化を行うと生産性が向上する。
これが一般的な話です。

もっともこれは抽象化に成功している場合に限ります。
抽象化に失敗している場合は逆に生産性が落ちることがあります。

  • 抽象化は基本的には生産性をあげる
  • しかし抽象化に失敗すると逆に生産性が下がる

これはプログラミングに限った話ではありません。
世の中の仕事は具体的に扱うより抽象的に扱えた方が生産性が向上します。

会社の中で社員にばらばらに仕事をさせるのではなく、部署という抽象的な概念に所属させ仕事をさせる。
そうすると生産性が向上しますよね。
抽象化と言うのはけっこうありふれていることなのです。

メンテナンス性の向上

抽象的にしたコードは一般的にはメンテナンス性が向上します。
これはもちろん抽象化に成功している場合です。

なぜメンテナンス性が向上するのかと言うと、コードがシンプルになりわかりやすくなるからです。
複雑なif文やfor文がからみあって変数をバシバシ参照している数百行のコード。
これよりは抽象化してまとめて数十行のコードのほうがわかりやすいですよね。

ですのでメンテナンス性が向上するわけです。

コードの可読性の向上

メンテナンス性の向上と関連しますが、抽象化はコードの可読性も向上します。

これは粒度の細かい詳細なコードが、抽象的になって荒くなるからです。
つまり100個ある画像ファイルが1つのフォルダにまとめられる、というのが抽象化です。

100個ある画像を見るよりフォルダの名前でどんな画像か判断できた方が管理しやすいですよね。

データと関数をオブジェクトにして抽象化するには?

データと関数をオブジェクトにして抽象化するにはどうしたらいいんでしょうか。
これはクラスにまとめることで行います。

これのコツは、データと関数は関連があるものにすることです。
たとえば車のデータだったら車に関連する関数を持たせます。

  • データと関数は関連させたものにする

車のデータなのにかつ丼の関数を持たせたらちぐはぐです。
このような設計は失敗してしまうでしょう。

しかし車のデータをかつ丼に処理させたいなら話は別です。
つまり車のガソリンをかつ丼の……えーと……あ、無理ですね。無理です。

データと関数を連携させるのがコツ

データと関数は連携させましょう。
ちぐはぐではいけません。
ちぐはぐだとクラスにする意味がないです。

人間クラスだったら人間に関するデータ、そして人間の行動に関する関数です。
たとえば空腹度を表すデータ、そして食べ物を食べる関数などです。

  • 人間の空腹度
  • 人間の行う食事
  • これらは関連しているデータと関数である

関連していないデータと関数は、それらは別々に扱うべきです。
またデータに関連する関数が生まれたらクラスにすることを検討してください。

具体的な抽象化方法

それでは実際にコードで具体的な抽象化方法を見ていきましょう。
今回はPythonのコードを使いたいと思います。

座標を抽象的に扱う

扱う題材は「座標」です。
座標はゲームプログラミングなどでおなじみのデータです。

この座標も抽象化することができます。
具体的に見ていきましょう。

まずは抽象化していないダーティなコードを

最初に抽象化していないコードを見ましょう。
座標がマップの範囲外かどうか判定するコードです。

x = 10  
y = 20  

if x < 0 or x >= MAP_WIDTH or y < 0 or y >= MAP_HEIGHT:  
    print('座標はマップの外です。')  

などほど、xyが座標です。
そしてif文でMAP_WIDTHMAP_HEIGHTと比較しています。
座標がマップの範囲外なら「座標はマップの外です」と表示されます。

ゲームなどではよく書かれるコードと言えます。
これを抽象化してみましょう。

どのように抽象化するか?

まずここで登場するデータはxyなどの変数です。
これがデータです。
そしてこのデータに関連する処理はif文の処理です。

データと処理はクラスとそのメソッドに抽象化できます。
まずはクラスにデータを持たせるところから始めましょう。

データをクラスに持たせる

以下のようにクラスを作ります。

class Vector2:  
    def __init__(self, x, y):  
        self.x = x  
        self.y = y  

なんてことない、メンバに座標を代入しているだけのクラスです。
これはあっけないほどシンプルですが、このコードが一番重要です。

データはいつだって最優先されます。
データによってその他のことは決まるのです。

そしてオブジェクトのメンバ変数はデータです。
姿勢を正して接するようにしましょう。

メソッドを抽象的な名前にする

ではif文を抽象的にしてみましょう。
以下のようにメソッドを定義します。

class Vector2:  
    def __init__(self, x, y):  
        self.x = x  
        self.y = y  

    def is_out_of(self, size: Size):  
        if self.x < 0 or self.x >= size.width or \  
           self.y < 0 or self.y >= size.height:  
           return True  
        return False  

is_out_of()Sizeのオブジェクトと自身のメンバ変数を比較します。
Sizeには横幅を表すwidthと高さを表すheightが定義されています。
そして自身の座標がSizeの幅の範囲外であればTrueを返し、範囲内ならFalseを返します。
これで抽象化は完了です。

抽象化したオブジェクトを使ってみる

では実際に抽象化したコードを使ってみましょう。

v = Vector2(10, 20)  

if v.is_out_of(map_size):  
    print('座標はマップの外です。')  

データはVector2に、if文の処理はv.is_out_of()に書き換えられました。
そしてmap_sizeはマップのサイズ(Size)を参照しています。

座標がマップのサイズの範囲外であれば「座標はマップの外です」と表示されます。

if文の細かい処理がメソッドの名前に抽象化されているのがわかると思います。
英語のメソッドを読めばvmap_sizeの範囲外かどうかのチェックだと一目でわかりますよね。
これが抽象化です。

おわりに

今回はオブジェクト指向プログラミングの抽象化について解説しました。
オブジェクト指向プログラミングは身に付けると強力な武器になります。
ぜひ身に付けてみてください。

🦝 < 抽象化するぜよ

🐭 < 具体性なんていらんかったんや