JavaScript勉強会

JavaScriptの学習日記

オブジェクト指向って便利なの?

プログラミングのやり方って、いろんな方法が考えられてきました。

何かを作るとき、ゼロから作るのは大変だけど、先人の努力や工夫を拝借して作ると、その分だけ楽ができますね!

プログラミングのやり方はまだまだ発展途上=改善の余地があるんだろうけど、現状はどうなっているのでしょうか?

 

shokuren.hateblo.jp

 

オブジェクト指向のやってることはわかるけど、説明してる人の何が言いたいのかはわからん

疑問が2つ。

 

一人で作ってて、小規模な案件なら、むしろ書く量が増えて面倒だったりしません?

なので、そうしないほうがいいこともある?というのが一つ。

 

腕が上がるとor効率化を図るといつの間にかオブジェクト指向になってるとか?が2つ目。

 

オブジェクト指向ってどうなのさ?

(1) オブジェクト指向プログラミングを使わない方が良い場合もあるの?

(2) オブジェクト指向プログラミングは効率化に役立っているの?

 

今どきのプログラミング技法について検討してみました。

f:id:jsstudy:20170326150347j:plain

(※個人の感想であり、効果・効能を示すものではありませんw)

 

文中の略記

OOP: オブジェクト指向プログラミング(Object Oriented Programming)

FP: 関数型プログラミング(Functional Programming)

 

(1) OOPを使わない方が良い場合もあるの?

→YES!

 

 

「簡単にできることを複雑にやる必要はない」(・∀・)

 

 

(´-`).。oO(OOPの欠点って何だろね?)

  1. OOPは、設計が複雑になりがち
  2. OOPは、テストが面倒
  3. ハードウェアに近い部分は、OOP不要
  4. OOPよりも、FPが向いている分野もある?

など。

 

OOPは設計が複雑になりがち

オブジェクト指向の現実

オブジェクト指向設計では、モジュールの切り分けが重要となる

組合せ爆発によるテスト限界 | imaicの日記 | スラド

注意しなければならないのは、構成によっては組合せ爆発が発生する場合があることだ。

 

組合せ爆発 - Wikipedia

情報システム開発では、実世界の複雑性を反映して、システムの各部分の状態の数や、モジュール間の関係の数、処理フローの数そしてテストケースの数は、組合せにより爆発的に大きなものになる

このため、設計、開発、テストを決まった期間で行うのは容易なことではない。

組合せ爆発の規模を極力おさえ、また爆発を前提としてうまく制御し対応することが、大規模システム開発を成功させる鍵である。

 

(´-`).。oO(複雑な設計→組合せ爆発→テスト地獄→テスト省略→炎上)

ニュース - みずほ銀行、勘定系システムの統合・刷新で2度目の延期を検討:ITpro

 

組込み用途など、シンプルな手続き型で済む場合OOPは不要

オブジェクト指向のデメリット - 千里霧中

低レイヤ層との親和性が損なわれる

WindowsLinuxUNIXなどのOSや、デバイスドライバといった低レイヤのプログラムは基本的にオブジェクト指向言語で記述されており、APIなどもその非オブジェクト指向言語に立脚した形でユーザー層に公開されています。

 

(OSを作る機会はないけど)Linuxを作ったリーナス・トーバルズ氏は、C++OOP言語)を批判しています。OOPが向いていない分野もあるようです。

Linus TorvaldsのC++批判は正しかったのか | スラド デベロッパー

 

OOPよりも、FPが向いている分野もある?

「オブジェクト指向プログラミング」と「関数型プログラミング」のたった一つのシンプルな違い - Qiita

OOPのような状態ありプログラミングの限界として、次の2つを挙げている。

  • 現実世界は並行的である
  • 現実世界は分散的である

 

並列処理(一度にたくさんの計算を行う)では、OOPよりFPの方が書きやすい場合があります。→ OOPを使うと苦労する?

 

その他、OOPに対する批判がいろいろあるようです。

 

以上…OOPが不要だったり、不向きな場面があるみたいですね!\(適材適所)/

 

(2) OOPは効率化に役立っているの?

→YES!

 

 

(´-`).。oO(キーワードは「差分プログラミング」「DRY原則」?)

 

 

OOPのメリットの1つは、

「継承」「ポリモーフィズム」という仕組みによって

差分プログラミング」をしやすい

という点だと思います。

 

d.hatena.ne.jp

 

差分プログラミングによって、何度も同じコードを書かなくて済むようになります。

(「Don't Repeat Yourself.」=DRY原則

 

d.hatena.ne.jp

 

オブジェクトは関数よりも高機能

プログラムを作るとき、よく行う処理は「関数」(function)にして、繰り返し利用できるようにしますね?

→もしも、「関数」という仕組みが用意されていないプログラム言語を使ったら、何度も同じ処理を書くはめになって大変!?(汗)

 

(例:PHPの関数 PHP: ユーザー定義関数 - Manual

<?php
// 関数を用意
function getTaxPrice($price, $taxRate)
{
    echo "税込価格を計算\n";
    $tax = $price * $taxRate;
    return $price + $tax;
}
// 繰り返しの処理
$sum = 0; // 合計金額
$taxRate = 0.08; // 税率
for ($i = 1; $i <= 10; $i++) {
    $price = $i * 100;
    $sum = getTaxPrice($price, $taxRate); // 関数を呼出
}
echo $sum;
?>

 

「関数」よりももっとたくさんの機能を1つにまとめたい場合、関数の代わりに、「オブジェクト」という仕組みを使うと便利です。

 

オブジェクトの機能

オブジェクト指向の用語「プロパティ」「メソッド」 - JavaScript勉強会

「データ」の束(構造体)に、「処理」(メソッド)も入れられるように改造したのがオブジェクトです。

「データ」(状態)に「処理」(操作)を付けたものがオブジェクト

 

OOP用語
  • オブジェクト: データ(変数)に処理(関数)をくっつけたもの
  • クラス: オブジェクトの設計図
  • インスタンス: クラス(設計図)から作った具体的なオブジェクト
  • プロパティー: クラスの中に用意する変数(状態を保持)
  • メソッド: クラスの中に用意する関数(処理を担当)
  • メンバー: クラスの中のプロパティーやメソッドをまとめてメンバーと呼ぶ

 

クラスをインスタンス化(実体化)してオブジェクトを作り、オブジェクトのメンバー(プロパティーやメソッド)を呼び出せば、関数を呼び出すよりもいろいろな仕事をやらせることができます。

→ 関数が提供する機能よりも、オブジェクトが提供する機能の方が高機能にできる!

→ 構造化プログラミングの単純な「関数」では物足りない場合、OOPの「オブジェクト」を利用すればOK!

 

(例:PHPのオブジェクト PHP: クラスの基礎 - Manual

<?php
// クラスを用意
class Tax
{
    // プロパティーの宣言
    public $taxRate = 0; // 税率

    // メソッドの宣言
    public function getTaxPrice($price) {
        $tax = $price * $this->taxRate;
        return $price + $tax;
    }
}

$calc = new Tax; // 「calc」というオブジェクトを作成(インスタンス化)
$calc->taxRate = 0.08; // プロパティーを使用
echo $calc->getTaxPrice(100); // メソッドを使用
?>

 

OOPの利点1

繰り返し使う機能をまとめるとき、関数よりもオブジェクトの方が高機能にできます。

これがOOPのメリットだと思います。

 

※上記の例では、メンバー(プロパティーやメソッド)が少ない=低機能ですが、メンバーをもっと増やすと高機能なオブジェクトにできます。

 

「継承」で差分プログラミング

オブジェクト指向プログラミングでは、いろんな仕組みが用意されています。

その1つが「継承」(inheritance)という仕組みです。

 

継承とは|インヘリタンス|inheritance - IT用語辞典

継承とは、オブジェクト指向プログラミングにおいて、既に定義されているクラスをもとに、拡張や変更を加えた新しいクラスを定義すること。

元になるクラスを「スーパークラス」(super class)、あるいは「基底クラス」「基本クラス」(base class)などと呼び、新たに定義されたクラスを「サブクラス」(subclass)、あるいは「派生クラス」(derived class)と呼ぶ。

スーパークラスの性質はすべてサブクラスに受け継がれ、サブクラスではスーパークラスとの違いを定義するだけでよい。

複数のスーパークラスから新しいクラスを定義することを多重継承という。

 

「継承」という仕組みを使うと、あるクラスを改造して別のクラスを作ることが簡単にできます。

 

継承の用語
  • スーパークラス: 基になるクラス
  • サブクラス: 基になるクラスを改造して新しく作ったクラス

 

OOPの継承を「子孫の継承」に例えて、

とも言います。

→子クラスからさらに子クラスを作ったら、「孫クラス」ですねw

 

(例:PHPの継承 PHP: オブジェクトの継承 - Manual

<?php
// 商品クラスを用意
class Product
{
    public $name; // 商品名
    public $price; // 価格
}

// 商品クラスを継承した鉛筆クラスを用意
class Pencil extends Product
{
    public $color; // 色
}

$pen = new Pencil;
$pen->name = "色鉛筆";
$pen->price = "100";
$pen->color = "";
var_dump($pen);
?>

 

上記の例だと、Pencilクラスを作るときに、親クラスのProductクラスからメンバー($name、$price)を継承しているので、同じ内容を書く手間が省けます。

(Pencilクラスを書くとき、$nameと$priceを書かなくてもメンバーに入ってる)

 

これが「差分プログラミング」で、子クラスでは親クラスと違うところ(新たに追加された$colorの用意)だけを書けばOKなので、少し楽ができます。

 

OOPの利点2

差分プログラミングで、DRY原則=同じようなコードを減らせることです。

 

継承の罠

ところがドッコイ!(ノ∀`)

クラスの「継承」には、複雑さをもたらし、バグの原因になりやすい、という欠点もあります!

 

ポリモーフィズム(多相性)

ポリモーフィズムとは|ポリモルフィズム|polymorphism - IT用語辞典

ポリモーフィズム 【 polymorphism 】 ポリモルフィズム / 多相性 / 多型性 / 多態性 / 多形性

ポリモーフィズムとは、オブジェクト指向プログラミング言語の持つ性質のひとつであり、同名のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のこと。

日本語では「多相性」「多態性」「多様性」などと訳される。

 

OOPの継承を使うとき、親クラスPと子クラスCのメンバーの引継ぎで、いろんな関係を設定することができます。

このいろんな関係のことを「ポリモーフィズム」(多相性)と言います。

 

  • カプセル化:クラスのメンバーを隠す=外から見えるメンバーと見えないメンバーを設定できる。
  • オーバーライド:親クラスPのメソッドを、子クラスCで上書きして別のメソッドに変える。

 

その他、オーバーロードアブストラクト、インターフェースなど、OOPではいろんな仕組みが用意されています。

→ 先人たち、頑張りすぎっっっ!!!

 

(参考)Java言語入門 ~C言語を学んだ君へ~ [第9回]継承 - ほぷしぃ

f:id:jsstudy:20170326212208p:plain

 

キャスト(型変換)

型変換 - Wikipedia

型変換(かたへんかん)とはプログラムにおいて、あるデータ型を他のデータ型に変換することである。

 

アップキャスト

あるクラスBaseと、Baseから派生したクラスDerivedがあるとする。

アップキャストとは、派生クラスから基底クラスへの型変換、すなわちDerivedのインスタンスをBaseに変換する操作である。

「DerivedのインスタンスはBaseのインスタンスである」ことは保証されているので、一般的にはこの変換は安全である。

そのため、多くの言語において、これは暗黙に行うことができる。

 

ダウンキャスト

ダウンキャストはアップキャストの逆で、基底クラスから派生クラスへの型変換、すなわちBaseのインスタンスをDerivedに変換する操作である。

Baseのインスタンスは必ずしもDerivedのインスタンスとは限らないので、この変換は一般に安全ではなく、エラーが発生する可能性がある。

そのため、多くの言語では明示的な構文が必要である。

 

(参考)VB開発者のためのポリモーフィズム(多態性)入門 − @IT

f:id:jsstudy:20170326232856g:plain

●8.1.1 型変換

 クラスも組み込みデータ型のように、型を変換することができます。ただし継承関係のあるクラスどうしに限ります。

 派生クラスは、基本クラスのメンバを継承しているので、派生クラスでは、基本クラスのメンバを保証していると考えられます。そのため、派生クラスのインスタンスは、基本クラスのインスタンスとしても利用できると見なされます。つまり、基本クラスへ型変換できるということです。この、派生クラスから基本クラスへの変換は、アップキャストと呼ばれ、暗黙的に行うことができます。

 反対に、基本クラスを派生クラスに型変換することをダウンキャストと呼びます。ダウンキャストは、アップキャストのように常にできるとは限りません。そのため、明示的に行う必要があります。

 基本クラスを拡張したものが派生クラスなので、当然、基本クラスをそのまま派生クラスに変換することはできません。

 

クラスの型変換(キャスト)という仕組みを使うと、

親クラスのオブジェクトを子クラスのオブジェクトとして扱ったり、

子クラスのオブジェクトを親クラスのオブジェクトとして扱ったりできます。

→(場合によっては便利な仕組みなんだけど)ややこしい!!!

 

OOPの継承や型変換で、差分プログラミングをやると便利な反面、複雑さをもたらしてバグの原因になるので注意が必要です。

 

これは別の言い方をすると、「副作用」が発生する場所に注意する、ということになります。

 

副作用とは?

「副作用」という言葉の一般的な意味は、薬の悪影響とかですね?

薬の効果と副作用|お薬を知ろう|日本調剤

主作用と副作用

かぜ薬の目的は、熱を下げたり鼻水を止めたりすることです。

このように病気を治したり軽くしたりする働きを「主作用」といいます。

それに対して、眠くなることなど本来の目的以外の働きを「副作用」といいます。

 

プログラミングでも「副作用」という用語が使われます。

副作用 (プログラム) - Wikipedia

プログラミングにおける副作用とは、ある機能がコンピュータの(論理的な)状態を変化させ、それ以降で得られる結果に影響を与えることをいう。

代表的な例は変数への値の代入である。

 

OOPでもFPでも、どんな方法でも何らかの「処理」を行えば、「副作用」が発生します。

→ 問題は、副作用が発生する場所です。

 

OOPの特徴は、カプセル化などによってクラスの中に副作用を隠蔽する(影響を閉じ込める)工夫を行っていることです。

 

f:id:jsstudy:20170326215711p:plain

(via オブジェクト指向プログラミングからリアクティブプログラミングへ、そして関数型プログラミングとの関係 - Qiita

 

しかし、設計の都合上、隠蔽が不十分な場合があって、副作用がちょっとずつ外に漏れ出てしまうことがあります。

 

FPの場合は、「モナド」という仕組みで副作用をまとめておき、後でチェックしやすくする工夫がなされています。

OOPやFP、他の方法でも)副作用がどこで発生するのか?を注意しながら設計すれば、バグの原因を見つけやすくなると思います。

 

プロトコル指向プログラミング

(余談)副作用を伴うOOPの欠点を改善する試みもあります。

 

iPhoneアプリを作るときに使う「Swift」というプログラミング言語があります。

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

 

 

gihyo.jp

 

Swiftでは、プロトコルJavaの「インターフェース」に相当)という仕組みを活用して、継承の問題点を克服するように工夫しています。

Swift プロトコル指向プログラミング - Google 検索

 

www.slideshare.net

 

プロトコル指向プログラミング」(POP)は、Swiftの言語仕様に依存する面があるので、今後普及していくのか?は未知数です。

しかし、OOPの欠点を克服する方法として参考になります。

 

プロトタイプベースのOOP

(余談2)OOPの種類

 

私は、最初Javaを勉強してOOPを学びました。

その後、JavaScript(JS)を勉強したので、Javaとだいぶ違っており、かなり戸惑いました。

OOPには「クラスベース」や「プロトタイプベース」などの種類があり、一口にOOPと言っても、ちょっと仕組みが違うので注意が必要です。

 

qiita.com

プロトタイプベース言語

Javascriptはプロトタイプベースのオブジェクト指向言語で、クラスベースのオブジェクト指向言語(例: C++, Java)とは異なる部分が多々ある。

例えば、クラスベース言語はクラスとインスタンスという概念があるのに対し、プロトタイプベース言語はPrototypical Objectというオブジェクトがあるだけ

 

正直、JSのOOPは、Javaと比べて使いづらいと思いました。

(特に「プロトタイプチェーン」という仕組みで混乱しました。)

 

JSのOOPを使いやすくするために、いろいろなAltJS(JSの代替)が提案されています。

AltJSの筆頭は、マイクロソフトが開発している「TypeScript」とか?

www.typescriptlang.org

 

まとめ

  • OOPは使わなくてもOK!(適材適所)
  • OOPで差分プログラミングを行うと効率的
  • ただし、「副作用」の発生に注意!
  • OOPの代わりにFPを使って、副作用を閉じ込めることも可能
  • プロトコル指向プログラミング」のやり方もOOPの参考になる?

  

オブジェクト指向は、便利だけど万能じゃない=短所もあるので、注意しながら使えば良いかな?

最終的には、「正誤」の問題ではなく、「好み」の問題になると思います。。。

 

ネタバレ

OOPやFPの特徴については、以下の本などを参考にさせていただきました! 

 

・プログラミング技法の発展史

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)

コーディングを支える技術 ~成り立ちから学ぶプログラミング作法 (WEB+DB PRESS plus)

 

 

・プログラム設計の歴史

 

OOPとFPの違い