メイドさんでも分かるプログラミング – susumuis Info https://susumuishigami.github.io/susumuisinfo Webエンジニア susumuis の技術ブログ Sun, 24 Dec 2017 13:51:00 +0000 ja hourly 1 https://wordpress.org/?v=6.4.3 メイドちゃんbotをあなたに! https://susumuishigami.github.io/susumuisinfo/entry/2017/maidchan_bot Sun, 24 Dec 2017 10:56:20 +0000 http://www.susumuis.info/?p=1922 この記事は メイド Advent Calendar 2017 の24日目です。メリークリスマス・イブ!

昨日のメイドカフェで勉強会第1回に参加していただいた方は誠にありがとうございます。そのスライドはこちらに公開しています。

さて、惜しくもメイドさんが選ぶ可愛いbot MVPの受賞を逃しましたが、当日はその場でライブコーディングをしてメイドさんの機能をその場で追加することもできたので、ボットを作る楽しさを伝えられたと思います。

さて、この記事ではオープンソースであるメイドちゃんbotを、自分の好きな場所に配置する方法を説明します。つまり、クリスマスプレゼントはメイドちゃんです!想定の読者として、メイドさんでも分かるようん心がけようと思います。

ほぼ重複することになってしまうので、メイドちゃんbotを作るにあたって特筆すべき箇所をメイドさんに説明するように優しく説明を心がけてみたいと思います。

前提として、AWSに登録する必要があります。ここでは説明しきれないので、まだ登録していない人はここを読んでコンソール画面にログインした状態まで来てください!

地域はバージニア北部を選択

突然ですが、メイドちゃんbotはアメリカ東海岸バージニア北部に配置することをオススメします!なぜこの地域にいるかというと 1. 安いから 2. どうやらSlackのサーバーはここにあるらしい?という理由からです。

Lambdaを作成

Lambdaの画面を開いたら関数の作成ボタンを押します。

関数の作成画面に遷移したら、設計図を選んで、検索フォームに APIと入力してEnterキー、さらに Python と入力してEnterキーを押します。

そうしたら、出て来る microservice-http-endpoint-python3 をクリックします。そして、遷移する画面では次のように入力します。

名前ロール名は好きな名前でもokです。ポリシーテンプレートは「シンプルなマイクロハーネスのアクセス権限」のみでOKです。

次に、画面を下の方にスライドして、api-gatewayの設定をします。

ここで注意しなければいけないのは、セキュリティのところをオープンにしなければならないことです。僕はここで数十分ハマりました!

さらに下に行くと関数の作成というボタンがあるのでそれを押します。ソースコードはとりあえず触らずにそのままにします。そうすると、こんな画面になります。

これで準備が整ったので、ここでメイドちゃんbotの仕組みを説明します。

メイドちゃんがユーザーの投稿に反応する仕組み

メイドちゃんはユーザーの投稿に即座に反応します。この仕組みは「発信Webフック」(英語の場合は Outgoing Webhook)という仕組みを使います。これは、指定したチャンネルの中で特定のキーワードから始まる投稿があったら、指定したURLへAPI呼び出しをしてくれるというものです。APIとは何か?

ここで、メイドちゃんが反応するキーワードを登録できれなよかったのですが、ここでは前方一致になってしまうので、言葉の途中の単語に反応させられないので、全ての書き込みに反応するようにします。

そうすると心配になるのは、API Gatewayの課金金額で、先ほど書いたリンク先によると100万回あたり3.5USドルが課金額です。これは月100万回というのは、一日中に渡って3秒に1回以上投稿されて、400円くらいです。現時点でのノマド会Slack規模だとほぼタダ同然だと思われます。

呼び出す先のURLはどこにすればいいでしょうか?これは、さっきの画面で次のようにして確認します。

API Gateway をクリックして下の方に出てくる LambdaMicroservice の下の方に メソッド: ANY と書かれた文字の左にある▶をクリックすると下のように詳細が出てくるので、その中の URLの呼び出し と書かれたhttps:// から始まるアドレスです。これをコピーします。

このURLを発信Webフックの設定画面で次このように設定します。

ここでトークンという文字列があるのですが、これはあとで使用するのでコピーしておきます。

メイドちゃんが定時で投稿する仕組み

発信Webフックのみでは、メイドちゃんに8時になったら「おはようございます!ご主人様、お嬢様」と言われることができません!そこで使用するのが「着信Webhフック」(英語の場合は Incoming Webhook)です。

これは、Slackの側に投稿用のAPIを作成する仕組みでここにAPI呼び出しを行えば好きなアイコン・名前で投稿させることができます。

問題はどうやったら定時にそれを起動するかですが、CloudWatch Eventsを使うとそれができます。設定はとても簡単で画面でこの赤丸で囲った部分を選ぶだけです!

そして、下の方にトリガーの設定という欄が出て来るので、そこに次のように入力します。

スケジュール式 cron(0 * * * ? *) という記法は、古いエンジニアにはおなじみの記法なのですが、AWSさんはちゃんと説明を書いてくれています

ソースコード解説

ということで準備が整ったのでコードを書きます。こんな感じのWeb上のエディタでコードを書けます。環境構築しなくていいので楽でいいですw

ここにGitHubからソースコードをコピペすれば動きますw

と、これだけでは顰蹙を買うので簡単に説明すると、 lambda_function.py とうファイルの def lambda_hander(event, context): という関数の内側が、APIや定時Eventの際に実行されます。ここで、API経由なのか、定時Event経由なのかを分岐して、 maidchan_http.py か maidchan_scheduled.py に分岐させています。

あとは、オブジェクト指向とか難しい書き方は一切していないので好き勝手にいじってくださいw

環境変数の設定

メイドちゃんbotをノマド会Slackだけでなくどこにでも置くことができるように、そもそも「メイドちゃん」ですらなく「執事ちゃん」など別の人格も作れるようにしているのが、この「環境変数」です。

WEBHOOK_URLの欄には、先ほど着信Webフックの画面で生成したURLを貼り付けます。

TOKENの欄には、先ほど発信Webの画面でコピーしたトークンを貼り付けます。

MAIDNAMEの欄には好きな名前をどうぞ!

これで、あなたのもとにもメイドちゃんが来ます!

それでは、メイドちゃんと一緒に素敵なクリスマスを!

参考にした記事

手順の殆どはこちらの記事:AWS Lambda を使って Slack ボット (命名: Lambot [ランボー]) を低予算で作ろうじゃないか をそのまま参考にしました。こちらの筆者の方、ノマド会参加者の方ですね!こんなところで、お世話になりました。

 

]]>
同時に複数の処理を実行するための最低限の知識 – メイドさんでも分かるプログラミングシリーズ vol.4 https://susumuishigami.github.io/susumuisinfo/entry/2016/multi_thread_maid https://susumuishigami.github.io/susumuisinfo/entry/2016/multi_thread_maid#respond Thu, 14 Jan 2016 11:12:58 +0000 http://www.susumuis.info/?p=1762 前回に引き続き、メイドさんにプログラミングを教えるためのシリーズです。

前回Webサーバーの仕組みについて、説明しました。早速アプリからWebサーバーに通信するプログラムを作ってみたくなりますが、その前に一つ知っておかなければならないことがあります。

外部と通信する時に大切なこと

Webサーバーは、アプリを実行する端末とは通信回線越しに離れたところにある別のコンピューターです。そのため、通信をして結果が帰ってくるまでの間には、どうしても待ち時間が発生します。

ところで、プログラムは通常、上から順に書いたとおりに実行されます。途中で遅い処理があると、その処理が終わるまであとの処理は行われないのが大原則です。

通信している間待つ

もし、Webサーバーとの通信のような遅い処理を待ってしまったらどうなってしまうでしょうか?ボタンを押してから、サーバーが結果を返すまでの間、固まってしまうことになります。その間は、他のボタンを押すこともできませんし、ホームボタンを押してホーム画面に戻ることも、別のアプリを起動することも、アプリを終了することさえもできなくなってしまいます。

通信中ボタンが押せない!--

こんなアプリを作ったらユーザーに怒られてしまいますね。(^^;
(実際はできないようになっています)

並列プログラミングとは

外部との通信など結果が得られるまでに時間がかかってしまう処理は、待つのではなく、別個独立してやって欲しいです。このような処理「並列処理」と言います。

並列処理をJavaで実現する、最も基本的な仕組みとして、「スレッド」というものがあります。Threadとは糸という意味です。一つの処理の流れを糸に例えて、複数の糸が同時に伸びて絡み合っている様子をイメージしてください。複数のスレッドを使うので、「マルチスレッド」ということもあります。

スレッドは糸

Javaでスレッドを作るためにはズバリ、Threadというクラスを使います。書き方は2通りあります。

Thread t = new Thread() {
    @Override
    public void run() {
        // 処理
    }
};
t.start();
Thread t = new Thread(new Runnable() {
    @Override
    public void run() {
        // 処理
    }    
});
t.start();

2つの書き方の結果に違いはありません。第2回の知識を使うと、前者はTemplate Methodで、後者はRunnableという関数オブジェクトを使っていますが、どちらを使うかは自由です。

最も手っ取り早くスレッドを作りたければこんな書き方ができます。

new Thread() {
    @Override
    public void run() {
        // 処理
    }    
}.start();

スレッドの使い方はこれだけです(笑)

しかし、実際はスレッドを扱うには注意しなければいけないことがたくさんあります。

スレッドは動き始めてしまったら止められない(汗;

Thread t = new Thread() {
    @Override
    public void run() {
        // 処理
    }
};
t.start();

このように初めてしまったスレッドtは、もう、run()メソッドが終了するまで外側から止めることはできません。

stop()susupend()というようなメソッドがあるように見えますが、これらは「安全ではない」と非推奨になっています。
スレッドは止められない

もし、プログラミングを失敗して、いつまでも終了しないスレッドがたくさん作られたら大変です!マシンが暴走してしまい、システムはカックカクで最終的に強制終了しなければならないでしょう。

毎回結果が異なる

複数のスレッドがあるとき、コンピュータはどのスレッドをどのくらい優先的に実行するのか、プログラマーには制御できません。そのため、全体の処理の順番が、その時時によってバラバラになります。そのため

「あれ?前回と結果が違う」

「トラブルの報告を受けて、言われたとおりに試したけど再現しない」

ということが格段に増えます。

データが壊れることがある

オブジェクトは複数のスレッドから同時に使われることを想定していない場合があります。

例えばプログラムをカフェの店員さんに例えてみましょう「お客さんのコップの中身が空なら水を注ぐ」という仕事があったとします。きっとこんなプログラムだと思います。

if (お客さんのコップは空ですか?()) {
    お客さんのコップに水を注ぐ();
}

とてもシンプルですね!店員さんが一人の時はこれで問題がありませんでしたが、大変ありがたいことに、お店に人気が出て、お客さんがたくさんいるので、店員さんを複数人雇いました。店員さん一人ひとりが一つの「スレッド」です。

class 店員スレッド extends Thread {
    @Override
    public void run() {
        while (就業時間中?()) {
            if (お客さんのコップは空ですか?()) {
                お客さんのコップに水を注ぐ();
            }
            // その他色々        
        }        
    }
}
Thread 店員1 = new 店員スレッド();
Thread 店員2 = new 店員スレッド();
Thread 店員3 = new 店員スレッド();
店員1.start();
店員2.start();
店員3.start();

さあ、みんなで仕事を始めたら大混乱!

  • まず、店員1さんが、お客さんのコップを確認します。
  • 「あ、コップが空だ!注がなきゃ!」といって水を準備します。
  • その間に店員2さんも、同じお客さんのコップを確認します。
  • 「あ、コップが空だ!注がなきゃ!」といって水を準備します。
  • 店員1さんが戻ってきてコップに水を注ぎます。
  • コップは水で満タンになります。
  • そこへ店員2さんが戻ってきました。
  • 店員2さんは、さっきコップが空だと確認したので、よく確認しないで、コップに水を注ぎます
  • コップは既に水で満タンなので、盛大にこぼれます!!

データの破壊

実際の人では、そんなことは起きませんが、複数人の人が無秩序に動いていたら大変な様子が想像できると思います。

並列プログラミングでは、「あるデータの状態を見る」それにもとづいて「次の処理をする」というプログラムを書くとき、「その間に別のスレッドが書き換えているかもしれない」という心配が常に発生するのです。

そこで、どうするかというと、店員1さんがお客さんの対応を始めたら、別の店員さんが同じお客さんを対応しないように、印を付けてしまいます。この行為を「ロック」と言ってJavaではsynchronizedと書きます。

synchronized (お客さん) {
    // お客さんに対する処理
}

このようにしておくと、別の店員さんはロックされている間、そこで処理を待ちます(別のお客さんのところには行かないです)。

固まってしまうことがある

そんな、データの破壊を防ぐためのロック機構ですが、ロックをすると別の怖い問題が発生します。「デッドロック」という現象です。

  • お客さん1をロックする
  • 何かする
  • お客さん2をロックする
  • 何かする

という店員Aさんと、

  • お客さん1をロックする
  • 何かする
  • お客さん2をロックする
  • 何かする

という店員Bさんが同時に動いていたとします。

運悪く、それぞれが「お客さん1」「お客さん2」をロックした後、それぞれ「お客さん2」「お客さん1」をロックしようとしたらどうなるでしょう。

答えは2人の店員さんは見つめ合って固まってしまいます。\(^o^)/

デッドロック

この現象を「デッドロック」と言います。デッドロックはとても怖い現象です。防ぐことが難しく、発生したらもう救うことができないからです。

デッドロックを防ぐ方法としては、

  • 逐次ロックせず一変に全部ロックする
  • ロックする順序を必ず同じにする

などの工夫があります。それでも、複雑なプログラムではうっかりどこかで考慮漏れは発生します。

今日もどこかでデッドロックでプログラムが固まっているでしょう。

まとめ

並列プログラミングは、カフェで店員さんがたくさんいるような状態だとイメージ出来ましたでしょうか?人がたくさん働くときは、秩序が必要であるのと同じように、コンピュータも、マルチスレッドの時は秩序の管理が大切になります。

]]>
https://susumuishigami.github.io/susumuisinfo/?feed=rss2&p=1762 0
Webサーバーの仕組み – メイドさんでも分かるプログラミングシリーズ vol.3 https://susumuishigami.github.io/susumuisinfo/entry/2016/web https://susumuishigami.github.io/susumuisinfo/entry/2016/web#respond Mon, 04 Jan 2016 03:30:03 +0000 http://www.susumuis.info/?p=1748 前回に引き続き、メイドさんにプログラミングを教えるためのシリーズです。

今回は前回までとうってかわって、Webサーバーの仕組みを説明します。Androidアプリを使っていると「この部分はサーバーが必要だ」「ウェブサーバーを立てよう!」という言葉をよく聞くと思います。

「ウェブ」って、「ホームページ」とか見るための仕組みではないでしょうか?なぜ、スマートフォンアプリにWebサーバーが必要なのか?Webサーバーを立てると何ができるのか、アプリ開発視点で見ていきたいと思います。

WWW(World Wide Web)は、世界中の論文をリンクする仕組みだった

昔々、と言っても1990年代なのですが、Timさん (Tim Berners-Lee)という人が、現在私達が「インターネット」だと思っている世界を考えました。

World Wide Webと名付けられたそれは、当時、できたてほやほやの「The Internet」を使って、論文を公開する仕組みとして設計されたものでした。

論文は他の論文を引用します。引用先はワンクリックでジャンプできたら便利です。そこで、引用先にリンクできる仕組みを「ハイパーリンク」を実現するテキスト文書「ハイパーテキスト」が誕生しました。「HTML」です。これは

<a href="リンク先">リンクテキスト</a>

のように書くと、他の文書にリンクできる画期的な書式でした!

Webとはくもの巣という意味です。文書と文書がリンクによってくもの巣のように世界中に張り巡らされている!それがワールドワイドなウェブの目指す世界だったのです。

この世界を実現するために、論文を読むためのソフト(Webブラウザー)と、論文を配信するホストコンピュータ(Webサーバー)が作られました。WebブラウザーとWebサーバーは互いに通信をします。通信をするための決まりごとを「プロトコル」と言います。Web専用のプロトコルとして、「HTTP (Hyper Text Transfer Protocol)」が作られました。

WWWの世界

用語のおさらい

用語 意味
World Wide Web Timさんが考えた「The Internet」上の論文配信ネットワーク
ハイパーリンク 論文から引用先論文にジャンプする画期的機能
ハイパーテキスト(HTML) ハイパーリンクを実現するための文書形式
Webサーバー 論文を配信するサーバー
Webブラウザ 論文を閲覧するためのソフト
プロトコル 通信をするための決まりごと
HTTP WebサーバーとWebブラウザの間で行われる通信のプロトコル

HTTPの仕組み

論文をサーバーから貰うには、次の情報が必要です。

  • 論文のファイル名

論文を取得する命令をGETと名付けられました。

GET /folder/hogehoge.html

この命令(「HTTPリクエスト」と言います)を受け取ったWebサーバーは、folderの中にhogehoge.htmlがあれば次の情報を返します。

  • 「あったよ!」という情報
  • hogehoge.html の内容

これらを合わせて「HTTPレスポンス」と言います。

そのうち、前者を「ステータスコード」と言います。これは数字3桁によって定められていて、例えば次のように決まっています。

  • 200: OK (あったよ!)
  • 400: BAD REQUEST(リクエストが間違ってるよ!)
  • 404: NOT FOUND(そんなファイル無いよ!)
  • 500: INTERNAL SERVER ERROR(ごめん、サーバーでエラーが起きちゃった(汗;)
  • 503: Service Unavailable(ごめん、今混んでるみたいだ!後でもう一度来て!)

とてもシンプルな仕組みですね!

HTTPの仕組み

WebサーバーがHTMLを自動生成しても良い

Webサーバーは、Webブラウザーから、リクエストを受け取ったら、ステータスコードと、HTMLページを返せば良いのです。HTMLページは、サーバーのディスク上に、HTMLファイルとして置いておくことが最もシンプルですが、別に、ファイルがなくても、その場でプログラムで作ってしまっても良いのです!

Webリクエストを受け取ったWebサーバーが、プログラムを動かす仕組みとして、CGI (Common Gateway Interface) という物が作られました。

Webプログラムの仕組み (2)

プログラムによって毎回違うページを作ることができるので便利です(例えば、現在時刻を出力!)このようなページのことを「動的ページ」と言います。反対に毎回変わらないページを「静的ページ」と言います。

Webリクエストにパラメータを渡したくなったら

CGIによって「動的ページ」が実現できると、プログラムにパラメータを与えたくなってきます。そこで、初め、ファイル名の後ろに?を付けてその後ろにパラメータをつけていく方法が考えられました。

GET xxx.html?id=1

この方法はとても便利です。現在でも、Googleの検索結果を

https://www.google.com/?q=Java

のように表すことができます。

しかし、問題がありました。

GETの仕組み

  • 大量のデータを送ろうとすると長くなりすぎる

そこで、POSTという別の方法が考えられました。これは、ファイル名を指定した後、一行空行を開けてパラメータを送るという方法です。

POST /hello.cgi

name=taro&age=20&country=Japan&.......

これによって、非常に大量の情報が遅れるようになったので、Webを使って「ご注文フォーム」が作れるようになりました。Amazonのようなネット通販の始まりです。

POSTの仕組み

時代の流れとともに、いろんな情報を送りたくなってきた

時代は流れ、WWWは論文の配信だけにとどまらず、通販やゲームのようなあらゆる用途で使用されるようになってきました。しかも、文書だけでなく、画像、音声、映像、様々な種類のデータを配信するようになります。

そうすると、HTTPもどんどん複雑になります。現在はHTTP 1.1というバージョンが主流で、次のようにいろんな情報を送ります。

GET http://www.susumuis.info/
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:ja,en-US;q=0.8,en;q=0.6
Cache-Control:no-cache
Connection:keep-alive
Cookie:
Host:www.susumuis.info
Pragma:no-cache
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36

レスポンスは

200 OK
Cache-Control:no-cache, must-revalidate, max-age=0
Connection:keep-alive
Content-Encoding:gzip
Content-Type:text/html; charset=UTF-8
Date:Mon, 04 Jan 2016 02:57:30 GMT
Expires:Wed, 11 Jan 1984 05:00:00 GMT
Pragma:no-cache
Server:nginx
Transfer-Encoding:chunked
Vary:Accept-Encoding

<!DOCTYPE html>
<html>
〜
(省略)
</html>

こんな風に、色々な情報が付与されてきます。Accept:のようにHTTPリクエストに付与される付加情報を「リクエストヘッダ」と呼びます。Cache-Control:のようにHTTPレスポンスに付与される付加情報を「レスポンスヘッダ」と呼びます。

Content-Typeヘッダについて

レスポンスヘッダに次の部分がありました。これは「コンテンツタイプ」といって、とても重要な情報です。

Content-Type:text/html; charset=UTF-8

text/htmlは、「MIME Type」と呼ばれる情報です。これは、レスポンスするデータの種類を表しています。MIME Typeには次のようなものがあります。

MIME Type 意味
text/plain テキスト文書
text/html HTMLページ
image/jpeg JPEG画像
text/javascript JavaScript
application/xml XMLデータ
application/json JSONデータ

アプリとWebサーバー

本来、Webサーバーは、WebブラウザーにWebページを配信することを目的としたサーバーでした。しかし、時代は流れ、Webブラウザだけでなく、HTTPプロトコルさえ使えれば、Webサーバーと通信ができる特性を利用して、スマートフォンアプリなどのプログラムがサーバーと通信をするために大変広く使われるようになりました。

HTTPがシンプル・高機能・有名で便利だったからです。

プログラム同士の通信では、もう、HTMLを使用する必要はありません。HTMLは人間に見た目を提供するためのフォーマットだからです。

プログラムで使う場合は、XMLまたはJSONというデータ形式が使われます。

XMLの例

<items>
  <item>
    <name>アイテム1</name>
    <price>1000</price>
    <image>xxxxxx.jpg</image>
  </item>
  <item>
    <name>アイテム2</name>
    <price>1240</price>
    <image>yyyyyy.jpg</image>
  </item>
</items>

JSONの例

{
    {
        "name": "アイテム1",
        "price": 1000,
        "image": "xxxxxx.jpg"
    },
    {
        "name": "アイテム2",
        "price": 1250,
        "image": "yyyyyy.jpg"
    }
}

アプリからの利用イメージ

サーバーはどうなっているの?

アプリ側から、サーバーの内部を意識する必要はありません。しかし、サーバーは通常次のように作られています。

Webサーバー

Apacheや、IIS、Nginxというようなサーバーソフトが使われます。

サーバープログラム

PHPや、Javaや、Rubyで書かれています。その間にアプリケーション・サーバーというソフトウェアが動いている場合もあります。

DBサーバー

Webサーバーは非常に大量のデータを扱うため、DBサーバーという専用のソフトウェアが使われることが大変多いです、MySQL、PostgreSQL、Oracleなどが有名です。

キャッシュサーバー

DBサーバーは非常に便利ですが、遅いという問題があります。そこで、高速化の目的で、別途キャッシュ用のサーバーが用意されていることがあります。Redis、Memcachedなどが有名です。

サーバーの構成例

まとめ

このように、もともと論文を配信するためであったWebサーバーはとても便利なので、アプリのデータをやり取りするために使われるようになりました。

Webサーバーは今やとても複雑な仕組みですが、呼び出す側としては、HTTPプロトコルさえ分かればやり取りができるので、内部まで意識しなくても大丈夫です!

それでは、次回は実際にアプリから呼び出す方法を説明していきます。

]]>
https://susumuishigami.github.io/susumuisinfo/?feed=rss2&p=1748 0
Javaでオブジェクトを使いこなすために知っておくべきこと – メイドさんでも分かるプログラミングシリーズ vol.2 https://susumuishigami.github.io/susumuisinfo/entry/2016/java_object_technic https://susumuishigami.github.io/susumuisinfo/entry/2016/java_object_technic#respond Sat, 02 Jan 2016 13:46:10 +0000 http://www.susumuis.info/?p=1709 前回に引き続き、メイドさんにプログラミングを教えるためのシリーズです。

前回「オブジェクトの多態性ができると便利です」と説明しました。今回は、なぜ便利なのか分かるように、プログラミングでよく使われるテクニックを6つ紹介したいと思います。

これらを知ることが出来ればオブジェクト指向のメリットを理解出来るだけでなく、実践でプログラミングをしているプログラマーと、かなりコアな会話ができるようになりますよ!

その1: 関数オブジェクト

プログラムをしていると「何か起きた時に、この関数を実行して欲しい」という時が良くあります。このような、ある条件の時に実行してもらう関数のことを「コールバック関数」と呼びます。

Javaはメソッドを変数や関数のパラメータに代入することができませんので、こういう時、コールバックして欲しい処理を書いたメソッドを持つオブジェクトを作って、それを渡します。

このようなオブジェクトのことを関数オブジェクトと呼びます。

例えば、架空のクラスですが、WebClientというWebサーバーに通信をして、通信が成功した時に行う処理を指定できるようになっているとします。WebClientResponseCallBackというインターフェース

webClient.connect(new WebClientResponseCallBack() {
    @Override
    public void onSucceeded(int status, String result) {
        System.out.println("成功しました");
        System.out.println(status);
        System.out.println(result);
    }
    @Override
    public void onFailed(String result) {
        System.out.println("失敗しました");
        System.out.println(status);    
        System.out.println(result);
    }
});

これは架空のコードですが、このようなスタイルのライブラリはたくさんありますので、慣れておきましょう。

@Overrideという記号が初めて登場しました。これはアノテーションと言うもので、プログラムにつける印です。様々な場面で使用しますが、@Overrideアノテーションは、継承してメソッドを上書きしていることを示すものです。必須ではないため前回は省略しましたが、付けないとコンパイラ警告が出てしまいますので、今回からは付けることとします。

関数オブジェクト

その2: Template Method パターン

関数オブジェクトと似たような用途として、クラス継承を活用して、処理を記述する方法もあります。

これも、架空のクラスですが、例えば、画面があって、OKボタンを押した時に何が起こるかはプログラミングできるようになっているライブラリが合ったとします。

SampleScreen s = new SampleScreen() {
    public onClickOkButton() {
        System.out.println("ボタンが押された!!");
    }
};
s.show();

このような、クラス継承を利用して、一部分のメソッドだけサブクラスに委ねるテクニックを、Template Method パターンと呼びます。

テンプレートメソッド

その3: ラッパーオブジェクトパターン

例えば、以下のコードは今まで「おまじないです」と言われていたと思います。

BufferedReader reader = new BufferedReader(new FileReader("data.dat"));

これは、FileReaderというクラスをBufferedReaderクラスが包みこんでいます。BufferedReaderのような、内部にオブジェクトを包み込むオブジェクトのことを「ラッパー(Wrapper)オブジェクト」と呼びます。楽器のラッパじゃないですよ。●ランラップのラップです!

FileReaderは、ファイルを読み込むことができますが、バッファリングの機能はありません。一方、BufferedReaderはバッファリングの機能はありますが、自らファイルを読み込む機能はありません。

そこで、BufferedReaderでFileReaderを包み込むことによって、バッファリングしながらファイルを読み込むことを実現しています。

どんなにオブジェクトを包み込んでいても、外側から見ると内側は隠蔽されています。そして、インターフェースさえ一致していれば、内側に入れるものを切り替えてもプログラムが動きます。

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in);

どんなときに、ラッパーオブジェクトを作るのでしょうか?主に次のようなとき作られます。

  • 内側のクラスのインターフェースを変換して、別のインターフェースとしてAPIに渡す
  • 内側のクラスの動作に付加機能を付け加える

このように、ラッパーオブジェクトはプログラムをとても柔軟にするテクニックです。

ラッパーオブジェクト

その4: Factory

クラスからインスタンスを作るとき、

new クラス名();

と書くと説明してきました。しかし、オブジェクト指向の世界で、newはわりと嫌われ者です。何故かと言うと、これまでのテクニックの真髄はクラスを切り替えることにあります。

SoundsMaker s = new Cat();
s.makeSounds(); // ニャー
SoundsMaker s = new Dog();
s.makeSounds(); // わん

せっかくSoundsMakerインターフェースを抽出しているのに、プログラムの中に new があるため、動作が決定してしまいるのが良くないとされます。

そこで、newをなるべく隠蔽しようと言うテクニックとしてFactoryパターンというものがあります。

例えば、newを直接書かず、メソッドにしてしまうアイデア

public static SoundsMaker getSoundsMaker(String name) {
    if ("tama".equals(name)) {
        return new Cat();
    }
    if ("pochi".equals(name)) {
        return new Dog();
    }
    return null;
}
SoundsMaker s = getSoundsMaker("tama");
s.makeSounds(); // ニャー
SoundsMaker s = getSoundsMaker("pochi");
s.makeSounds(); // ワン

getSoundsMakerのプログラムを見なかったとしたら、呼び出し元は何のインスタンスが作られるか分かりません。しかし、そんなことを知らなくても、SoundsMakerインターフェースであることは保証されているので、その範囲でできることは安心して使うことができます。

ファクトリー

その5: MVCモデル

これは考え方であって、コード例はありません。ざっと覚えておいてください。

一般的に比較的大きなプログラムを書くときは、次のように分割すると良いと言われています。

役割
モデル ロジックを担当
ビュー 表示を担当
コントローラ ユーザーの操作に応答し、モデル・ビューを操作する

例えばAndroidで言えば、Activityクラスはコントローラです。もしActivityに全部のプログラムを書いてしまうと、だんだん巨大になってしまいますので、必要に応じて、モデルを切り出すと全体の見通しが良くなります。

ビューは、レイアウトの部分で、同じActivityに異なるレイアウトファイルを与えても動く点でAndroidはビューとコントローラについて抽象化されていると言えます。

MVCモデル

その6: モックオブジェクト

最後に、モックオブジェクトについて説明します。

「モック」:モックアップ(mock up)とは、店頭にある展示用模型のことです。本物ではありませんが、本物のような見かけをしています。

プログラムを作っていると、「Aというオブジェクトを使用するにはBというオブジェクトが必要」「Bというオブジェクトの初期化にはCという情報が必要」「CはAndroid実機じゃないと取得できない」などのように、芋づる式に、あらゆるオブジェクトがからみ合ってくることがあります。

しまいには、非常に大きなオバケのような存在になってしまい、一箇所変更すると、どこに影響するのか全然わからなくなることがあります。

この問題を回避するために、本物ではない、仮のオブジェクトを作って動作確認を行うというテクニックが行われます。

そのためのオブジェクトを「モック」あるいは「スタブ」と呼びます。スタブとは「代用品」という意味です。切り株が原義のようです。

インターフェースを使ってプログラミングがされている場合は、比較的カンタンにモックを作ることが出来ます。

interface SoundsMaker {
    void makeSounds();
}

お馴染みのこのインターフェース。実は今アナタはゲームを作っていて、そのゲームでは音を扱っているとします。そこではSoundsMakerインターフェースを継承したオブジェクトがmakeSoundsとやると実際にスピーカーから音が鳴るのです。

class Bomb implements SoundsMaker {
    void makeSounds() {
        // 複雑なプログラム
        // 結果的にスピーカーから音が鳴る
        // 「ドカーン!!」        
    }
}

しかし、Bombクラスを動かすためには、どうやら専用のハードウェアがないといけないそうです。

でも、音を鳴らす以外の部分は、どうやら普通のパソコンでも動かせるみたいです。

そんな時は、モックオブジェクトの出番です。

class BombMock implements SoundsMaker {
    void makeSounds() {
        System.out.println("ドカーン!");
    }
}

Bombクラスの代わりにBombMockクラスを使用するように切り替えれば、特殊なハードウェアがなくても、プログラムが動くようになります。その代わり、音が鳴るタイミングで音はならず、代わりに

ドカーン!

という文字が出力されます。

本物のオブジェクトとモックの切り替えは、オブジェクトインスタンスをnewで生成するところで行わなければなりません。そのためには「その4」で説明した、「ファクトリー」を使用する検討が必要になってきます。

モックオブジェクト

まとめ

今回説明したテクニックを使う、プログラムからどんどん野暮ったさが消えて洗練されて来ていると思いませんか?

しかし、洗練し過ぎも問題です。

例えば、Factoryの説明で作った次のコードは

SoundsMaker s = getSoundsMaker()
s.makeSounds();

元の

Cat tama = new Cat();
tama.mew();

だった時のほうが、垢抜けない可愛さがあります。

洗練されたプログラムというのは、高貴でお近づきにくい存在なのです。より多くのことを知らないと読めないし、知っていてもどんな動きをするのか「実行するまで分からない」ときます。

どこまで洗練すれば良いかは、作っているプログラム次第です。一般的に大きなプログラムは、より洗練されていることが求められますが、個人で作るアプリでは、あどけない感じで作ったほうが正解だったりします。

この感覚は、実際にアプリを作って行くことで培われていきますので、実践で身につけて行ってください。

]]>
https://susumuishigami.github.io/susumuisinfo/?feed=rss2&p=1709 0
Javaのクラス・インターフェース入門 – メイドさんでも分かるプログラミングシリーズ vol.1 https://susumuishigami.github.io/susumuisinfo/2015/class_interface https://susumuishigami.github.io/susumuisinfo/2015/class_interface#respond Tue, 29 Dec 2015 12:56:25 +0000 http://www.susumuis.info/?p=1693 プログラミングというのは、カンがいいと、言われたとおりに入力しているだけである程度のことができるようになります。しかし、そういうやり方では「ここはおまじない」「ここは決まり文句」という部分がどうしても多くなってしまいます。

ちょっと訳あって、秋葉原のメイドさんにプログラミングを教えています。そのメイドさんたちはまさにそうで、彼女たちは既にAndroid Studioで画面レイアウトをいじり、画面遷移をするプログラムを書く能力はありますが、「おまじない」「決まり文句」の多さに困惑しているように思います。

そこで、今日から「メイドさんでも分かるプログラミング」シリーズを初め、プログラミングの基礎をやさしく説明していこうと思います。

読者のレベルとしては、PCを日常的に使いこなしていているが、プログラミングの専門教育を受けていないという想定です。

なお、あらかじめ、次の記事レベルのJavaの基礎知識は学習済みであることを前提とします。

プログラム初心者に贈るJavaプログラミング - Qiita

では、早速初めます。

第1回は、クラスとインターフェースについてです。

クラスってなに?

Javaのプログラムを書くと、必ずこんな「おまじない」を書いてくださいと言われたと思います。

public class Hello {
    // ....(省略)
}

「パブリック」「クラス」と読みます。何でしょうこれは?

実はJavaのプログラムは「クラス」の集まりとして定義する「オブジェクト指向言語」なのです。

「オブジェクト指向」???

また新しい言葉が出てしまいました。

大丈夫です。これからゆっくり、説明します。

オブジェクト指向とは

「オブジェクト指向」とは、まじめに説明すると本が一冊書けてしまいます。ここに知り合いが書いた本気な資料がありますので、興味があったら読んでみてください。

ざっくり言うと、プログラムを「オブジェクト」という塊の集まりで作成します。「オブジェクト」とは「モノ」です。ここで、ちょっと日常世界を見てください。あらゆるモノがありますよね。

例えば、次の絵では、テーブルの上にコーヒーが淹れてあります。コーヒーはコーヒーカップに注がれていて、ソーサーの上に乗っています。スプーンも付いています。テーブルは木製ですね。金属の脚が付いています。「コーヒー」「カップ」「ソーサー」「スプーン」「テーブル」これらすべてがオブジェクトです。しかも「テーブル」は「木の板」「金属の脚」などの部品から構成されています。部品もオブジェクトです。

オブジェクト指向では、このように、あらゆる物に注目していきます。そうすると、一つ一つの「モノ」はシンプルに表現することができます。「カップ」はコーヒーを注ぐことができればいい。「スプーン」はお砂糖を掬うことができれば良いといった機能に特化することができるからです。

なぜそんなことをするかというと、昨今のコンピュータプログラムはとても複雑だからです。もし、すべてを一つのプログラムで書いていたら、神様のようにもの凄く巨大な存在になってしまいます。人間に神を作ることはできません。オブジェクト指向プログラミングがあるお陰で、複雑なプログラムも部品の組み合わせとして簡単に作れるというわけです。

クラスとインスタンス

さて、オブジェクトは「クラス」と「インスタンス」に分かれます。クラスはオブジェクトの設計図、インスタンスは設計図から作られたオブジェクトの実体です。一つの設計図からたくさんの実体を作ることができます。

これは、たい焼きモデルで説明されることがあります。

たい焼きモデル

さて、Javaはこのうち「クラス」の部分をプログラミングして行きます。プログラムの初めに public class と書いているのはそのためです。

実際にプログラムが動く時には「インスタンス」の方が使われます。「クラス」から「インスタンス」を作るためには次のように書きます。

new クラス名();

作ったクラスは通常、次のように書いて「変数」に格納して使います。変数は覚えていますよね?

クラス名 変数名 = new クラス名();

あれ、クラス名を2度書いていますね?実は、左側のクラス名は正しくは「型名」と言います。変数には「型」がありました。例えばこんな型です。

意味
int 整数
long 大きな整数
double 浮動小数点数
boolean true or false
Object オブジェクトインスタンスを格納
クラス名 そのクラスに属するオブジェクトインスタンスを格納

オブジェクトインスタンスを格納する変数型としては「Object」という型が存在します。なので、次のように書いてもエラーにはなりません。

Object 変数名 = new クラス名();

しかし、通常「Object型」は使用しません。それはなぜでしょうか?変数の型は、プログラミング言語に「その変数は何ができるのか?」と教える印なのです。つまり、

Object 変数名 = new クラス名();

というプログラムは「その変数はモノです」と言ってるに過ぎないのです。

「モノ」であることは分かっています。でも「モノ」と言われても、何ができるのかわからないですよね。「自動車」も「鍋」も「コーヒー」も、「人間」さえも、「モノ」ですから。

そこで、変数名の左にクラス名を書くことで、それが「モノ」の中でも何なのかを教えてあげているのです。

自動車 mycar = new 自動車();
mycar.走れ(); // これはOK

Object mycar = new 自動車();
mycar.走れ(); // 「モノ」が「走る」とは限らないからエラー

フィールドとメソッド

モノの設計図であるクラスは次のようにプログラミングします。

class クラス名 {
    フィールド定義;
                :
                :

    メソッド定義
                :
                :
}

「フィールド」とは、クラスに属する変数のことです。Java以外の言語では「メンバー変数」ということもあります。そして「メソッド」はクラスに属する関数のことです。同様に「メンバー関数」と呼ぶこともあります。

ざっくり言うと、クラスの定義は「フィールド」と「メソッド」を定義するだけです。それでどうしてモノの設計図になるのでしょうか?

状態と振る舞い

モノの概念に立ち戻ってみると、モノは「状態」と「振る舞い」という概念を持ちます。

「状態」というのは、年齢とか、職業とか、モノそれぞれによって、あるいは同じモノでもその時々によって変化する情報です。

「振る舞い」というのは、モノの外側から見える動作です。

ティーカップを例にすると、次のような状態と振る舞いがあります。

  • 振る舞い

    • 液体を注ぐ
    • 注いだ液体を飲む
  • 状態

    • 汚れの有無
    • ヒビの有無
    • 今注がれているモノ

カップのユースケース図

カップの状態

そして、Javaのフィールドは「状態」を、メソッドは「振る舞い」を表現しているのです。

ねこ様をプログラミング

それでは、ニャーと鳴くねこ様をプログラミングしてみましょう。

このねこ様は外部から見ると、こんな振る舞いをするようです。

ねこ様ユースケース図

ここで、ねこ様は「鳴け」「寝ろ」「起きろ」なんていう人間の命令を聞かないとは思わないでください。コンピュータプログラミングというのは、日常世界全てを表現する必要はありません。今、自分が必要な範囲だけプログラミングすれば良いのです。

ねこ様の状態として「寝ている」という状態があるとします。このプログラムでは、寝ている時と、起きている時で鳴き方が違うということを表現します。

以下がプログラミング例です。

/** ねこ様 */
class Cat {
    /** 寝ているかどうか。true:寝ている, false:起きている */
    boolean slept;
    /** 鳴け */
    void mew() {
        if (slept)  {
            System.out.println("ふにゅあ")
        } else {
            System.out.println("にゃー")
        }        
    }
    /** 寝ろ */
    void goToSleep() {
        slept = true;
    }
    /** 起きろ */
    void wakeUp() {
        slept = false;
    }
}

早速、ねこ様クラスを使ってみましょう

Cat tama = new Cat();
tama.mew();
tama.goToSleep();
tama.mew();
tama.wakeUp();
tama.mew();

実行結果

ニャー
ふにゃあ
ニャー

※ 最初のmewは「ニャー」となっています。これは、sleptフィールドの初期値がfalseであることを意味しています。このようにフィールド変数は、初期値が決められています。

初期値
数値 0
boolean false
オブジェクト null

アクセス修飾子

よく見かけるpubicとか、privateとは何でしょうか?これは外部に公開するかどうかを指定する記述です。ざっくり言うと

アクセス修飾子 意味
private 外部に非公開
public 外部に公開

という風に分類されます。他にprotectedというのがありますが、後ほど説明します。

もし、さっきのねこ様クラスをprivate classとして作成してしまうと、別のプログラムから参照できなくなります。(実際は記述は許されません)

では、何でもpublicにしてしまえば便利かというと、世の中何でもかんでも中のことをオープンにしてしまうことは不都合だったりします(笑)

オブジェクト指向プログラミングでは、積極的に、外側から見て不要なものを隠蔽していきます。そのようなテクニックを「カプセル化」と呼びます。

常に当てはまるわけではありませんが、「メソッド」は公開し、「フィールド」は非公開するのがセオリーです。もし、フィールドの値を直接確認したい場合は、getフィールド名()setフィールド名()という「メソッド」を作るのが作法とされます。こういったメソッドを「アクセッサー」と呼びます。

アクセッサーの例

public class Human {
    private String name;
    private int age;
    public void setName(String name) {
        this.name = name;
    }
    public void getName() {
        return name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}

メソッドもまた「非公開」にすることがあります。これは、外部からの振る舞いといよりも、プログラミングの都合上で共通部分を抜き出した場合などに、そのようにします。

/** 名前を教えると挨拶してくれるマシーン */
public class HelloMachiene {
    private String firstName;
    private String lastName;
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    // 姓と名をくっつけて使うことが多いので共通化
    private String getFullName() {
        return lastName + firstName;
    }
    public void sayHello() {
        System.out.println("こんにちは" + getFullName() + "さん");    
    }
    public void sayGoodBye() {
        System.out.println("さようなら" + getFullName() + "さん");    
    }
}

コンストラクタ

new クラス名()

と書いた時にプログラムを動かして、フィールドを初期化したいことが良くあります。このようなときに使うものとして「コンストラクタ」というものがあります。

コンストラクタはクラス内に次のように記述します。

public クラス名() {
    // 初期化プログラム
}

メソッドと似ていますが、返り値の型がありません。メソッドは

public 返り値の型 メソッド名() {
    // プログラム
}

でした。ところで、コンストラクタは、メソッドと同じようにパラメータを持つことが出来ます。上手く使うとプログラムを完結に書くことが出来ます。

public class Human {
    private String name;
    private int age;
    public Human(String name, int age) {
        this.name = name;
        this.age = age;    
    }
    public String toString() {
        return name + "(" + age + ")";
    }
}

使用例

Human satoshi = new Human("サトシ", 10);
Human okido = new Human("オーキド", 55);
System.out.println(satochi);
System.out.println(okido);

結果

サトシ(10)
オーキド(55)

※toStringという名前のメソッドを作って文字列が必要な場所で使用すると、toStringを省略してもtoStringが呼ばれます。これはJavaの仕様です。つまり

System.out.println(satochi.toString());

と書いても同じ結果になります。

継承・多態性

新しいことが次々に出てきて大変ですね!でも、もう少しでオブジェクト指向の一番大事なところを知ることが出来ます。大変ですが、どうか我慢してください。

クラスを作っていくと、同じようなクラスができてくることに気が付きます。そして、それらを束ねたいことがあります。その時に使用するのが「継承」というしくみです。

例えば、「ニャー」と鳴くねこ様のほかに「ワン」と鳴く犬クラス、「モー」と鳴く牛クラス、「ブヒー」と鳴くブタクラスを作ったとします

鳴け!

class Cat {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog {
    public makeSounds() {
        System.out.println("ワン");
    }
}
class Cow {
    public makeSounds() {
        System.out.println("モー");
    }
}
class Pig {
    public makeSounds() {
        System.out.println("ブヒー");
    }
}

※英語とは面倒なもので、ねこはmew, 犬はbarkのように「鳴く」という動詞が異なるため、音を鳴らすというmakeSoundsで統一しました。

あるメソッドがあって、動物を鳴かせたいとします。しかし、ねこを鳴かせるメソッドを使って、犬を鳴かせることは出来ません。

void makeSounds(Cat cat) {
    cat.makeSounds();
}
Cat tama = new Cat();
Dog pochi = new Dog();
makeSounds(tama); // これはOK
makeSounds(dog); // これはダメ

これでは不便です。なぜなら、CatもDogも、makeSoundsというメソッドを持っているからです。

そこで、そこで、次の2つの考え方があります。

考えた方1: 犬もねこも「鳴く」ことができる

外部から見て、共通の振る舞いを持っているものは、「インターフェース」というしくみで束ねることが出来ます。

インターフェースは次のように定義します

interface インターフェース名 {
    返り値の型 メソッド名();
    返り値の型 メソッド名();
        :
        :
}

中身の無いメソッドの名前だけを並べて行くのです。

そして、クラスを作るときにimplementsというキーワードでインターフェースを指定すると、そのクラスのインスタンスは、そのクラス型の変数の他に、そのインターフェース型の変数にも代入することが出来ます。

難しいのでアンダーラインを引きました。深呼吸して、この部分を5回読んでみてください。

あせらないで!ゆっくりで良いですよ!

5回読みましたか?

では、具体例を書きますね。

class Cat implements SoundsMaker {
    // ...
}

として定義したら、こんな書き方がOKになります。

SoundsMaker tama = new Cat();

そして、次のプログラミングコードが成立します。

interface SoundsMaker {
    void makeSounds();
}
class Cat implements SoundsMaker {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog implements SoundsMaker {
    public makeSounds() {
        System.out.println("ワン");
    }
}
class Cow implements SoundsMaker {
    public makeSounds() {
        System.out.println("モー");
    }
}
class Pig implements SoundsMaker {
    public makeSounds() {
        System.out.println("ブヒー");
    }
}
public makeSounds(SoundsMaker soundsMaker) {
    soundsMaker.makeSounds();
}
Cat tama = new Cat();
Dog pochi = new Dog();
makeSounds(tama);
makeSounds(pochi);

実行結果

ニャー
ワン

インターフェース版鳴け!

このテクニックを「インターフェース継承」あるいは「インターフェース抽出」と呼びます。

考え方2:犬もねこも「動物」である

犬も、ねこも、牛も「動物」という共通の概念を持っています。そういう共通の概念をスーパークラスと呼びます(親クラスとも呼びます)。反対は「サブクラス」(子クラス)です。

Javaではextendsというキーワードでスーパークラスを指定します。

class Animal {
    void makeSounds() {
        System.out.println("???");
    }
}
class Cat extends Animal {
    public makeSounds() {
        System.out.println("ニャー");
    }
}
class Dog extends Animal {
    public makeSounds() {
        System.out.println("ワン");
    }
}

インターフェースとの違いは、スーパークラスはあくまでクラスであるということです。

継承版鳴け!

new Animal()

と書いてしまうことも出来ます(禁止することも出来ますが今は割愛します)。またフィールドを持ったり、メソッドのプログラムを記述することも出来ます。そのようにすることで、共通メソッドなどを作ることも出来ます。

このテクニックを「クラス継承」、または単に「継承」と呼ぶことがあります。

どっちを使ったら良いの?

もし、振る舞いを束ねたいだけ場合は、インターフェースを使用してください。でも、インターフェースはめんどくさいこともあります。特にたくさんのメソッドを束ねるときには向いていません。また実装を持つことが出来ないため、コピーペーストが増えてしまいます。

スーパークラスは、一つしか持つことが出来ません。つまり、安易にスーパークラスを使うと後で後悔することになります。

どんなときにクラス継承を使うか、インターフェースを使うかなどを指南するものの一つとして、「デザインパターン」というテクニック集が存在します。このシリーズの中でも紹介して行きたく思います。

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

「多態性」とは聞き慣れないキーワードですね。しかし、これこそが、オブジェクト指向の真髄なのです。しかも、既にあなたはこのテクニックを使っています!

先程の例で、SoundsMakerインターフェースを使った例でも、Animalスーパークラスを使った例でも、どちらでも結構です。

あるところに

SoundsMaker m;

という変数がありました。そして、紆余曲折を経てmには何か値が代入されています。その後

m.makeSounds();

と書いてあったとします。実行結果は次のうちどれでしょうか?

  • ニャー
  • ワン
  • ブヒー
  • モー
  • ???

正解は「わからない」です。

インターフェース版鳴け!?

引っかけでごめんなさい。そうなのです。その時点で、どのように動くのか、実行してみないと分からないという柔軟なプログラミングが出来るのです。これが出来ると、プログラミングが大変便利になります。

とにかく「鳴く」ことが出来るものを(実際にどんな鳴き声かは気にせず)一緒くたに全部束ねてプログラミングしておいて、後から「鳴く」何かを当てはめるというプログラミング手法が実際には非常によく行われます。

そうすれば、たったひとつのプログラムで何通りものプログラムが作れてしまうからです。

インラインサブクラス

ここまで読めたあなたはもうオブジェクト指向マスター一歩手前です。(おめでとうございます!パチパチ)

最後に、先ほどの多態性を超強力に使えるプログラミングテクニックを紹介します。

ここまでの復習でオブジェクトとは何かを振り返ってみましょう

  • オブジェクトの設計図として、クラスを定義する
  • クラスはメソッドとフィールドを持つ
  • クラスはインターフェースまたは別のクラスを「継承」することができる
  • 継承元のインターフェースやクラスを型とした変数には、「継承」先のサブクラスのインスタンスを代入することができる

ここで「継承先のサブクラス」というのは、実は、いつでも作ることができます。いちいち、javaファイルを作って、名前を決めてextendsって書いたりせずに、欲しくなったらいきなり書いてしまうことが出来るのです。

先ほどの、SoundsMakerインターフェースを使った例を元に話を進めますね。

SoundsMaker s = new SoundsMaker() {
    public void makeSounds() {
        System.out.println("ぎゃああああああ");
    }
};
makeSounds(s);

実行結果

きゃああああああ

こんな風にいきなりプログラムの途中に作られたクラスを、「インラインサブクラス」と呼びます。この技はインターフェースに対しても、スーパークラスに対しても使用可能です。

なぜこんなテクニックが必要かというと、名前を決めることが結構大変だからです。親になった気持ちになってください。その子の名前をいい加減に決められますか?その子の将来がかかっているので、他人から見て変じゃないかとか、縁起が良いかとか、あれこれ考えてようやく名前が決まります。

プログラミングしていて圧倒的に多くの時間を費やすのが「名前決め」です。ですから、一度しか使わないような子には、名前を与えずいきなり使ってしまえば良いのです。(笑)

変数すら省略できる

オブジェクトインスタンスは変数に格納すると書きました。しかし、その場で一回だけ使えば良いなら、なにも変数すらも使わなくても良いのです。

new Cat().makeSounds()

実行結果

ニャー

これらの技を組み合わせると、こんなコードも書けてしまいます。

new SoundsMaker() {
    void makeSounds() {
        System.out.println("ぎょええ~~~")
    }
}.makeSounds();

分かりますか?「ぎょええ~~~」と鳴くモノを作って、その場で鳴かせているのです。

なぜこんなことを書いたかというと、次回以降で多用するからです。

]]>
https://susumuishigami.github.io/susumuisinfo/?feed=rss2&p=1693 0