オブジェクト指向プログラミングの教え方?

技術者仲間で話していたら、4月入社の新人に、オブジェクト指向プログラミングをどうやって教えたらよいか?、という話になった。

想定している言語は Java。

■動物・犬・猫モデルの説明から
■基本用語の説明から:「カプセル化とは」「継承とは」...
■サンプルコードから: System.out.println( "hello world" )
...

どのパターンでもうまくいかなかったので、今度の新人研修では何か工夫したいね、という話。
結論から言うと「これだ」というアイデアがでたわけではないが、話の内容は、いろいろ興味深かったのでメモ書き。

Java はオブジェクト指向の言語なの?


Java は、ある意味 C言語の仲間。ある側面はほとんど同じ言語。

・int, long (プリミティブなデータ型)
・配列操作
・if/for/return

ここだけ見れば、C言語のまま。つまり命令型、手続き型の言語。

public static void main( String[] args) なんかもオブジェクト指向というより C 言語風。

どうも、いきなり Java = オブジェクト指向 という教え方は間違いだろう、というのが一つの結論。

オブジェクト指向の前に基本が大切なのでは


たとえば、こんなアイデアがでた。

  1. まず命令的、手続き的な書き方に振り切って、プログラムを書いて動かす味を覚える。

  2. すこし大きめの課題に挑戦しながら、構造化プログラミング、特に手続き呼び出しの基本を学ぶ。メソッドの抽出リファクタリングは、この段階で教えてしまう。

  3. 次の段階で「複数」のクラスに分割してプログラムを作成することを教える。
    クラスの抽出のリファクタリングを実際にやって覚える。

  4. この段階では(本来のオブジェクト指向ではなく) getter/setter だけのデータ入れ物オブジェクトと、そのオブジェクトからデータを get したり set するという、データと処理を分離したスタイルで教える。

  5. その後で、データを get する側のオブジェクト(クラス)に書いてあるロジックを、データの入れ物オブジェクトに移動することを覚えてもらう。


この 5 を教えるときに、これが「カプセル化」であり「ロジックの移動」という「リファクタリング」である、という「言葉」もいっしょに教える。

この段階でも「これがオブジェクト指向である」という言い方(教え方)はしない。

あくまでも、

◎プログラムはメソッドやクラスに分割すること
◎データと関連するロジックは同じクラスにまとめたほうが良いこと

の2点を強調する。

あと、どんな簡単なプログラムでも「パッケージ」宣言は必須であると教えたほうが良い、という意見もでた。
なんでも public ではなく、基本はパッケージスコープということを最初から覚えたほうが良い。
複数のパッケージに分ける発想も早めに覚えたほうが良さそう。

基本的なリファクタリング

◎名前の変更
◎メソッドの抽出
◎クラスの抽出
◎ロジックの移動
◎データの移動
◎パッケージ間のクラスの移動

は手をどんどん動かしながら早い段階から覚えてもらうべき、という意見もでた。

「動いたら次の課題」を繰り返すと「動いた、OK」だけの技術者になってしまいそう。
そうではなく「動いた後」で、コード(設計)を改善するためにリファクタリングをすることまでがプログラミングである、と覚えてほしい。

カプセル化


教え方までは議論できなかったけど「カプセル化」は徹底的に体で覚えるべき、で意見が一致。

◎関連するデータとロジックをひとかたまりにすること。
◎データ構造は外には見せないこと(単純な getter/setter は書かないこと)

そうするのが「良いこと」という感覚をなんとか会得してもらう。

オブジェクト指向のプログラミングでは、プログラム全体のどこも見てもカプセル化が徹底しているのは、まちがいなく良いソフトウェア。

だから「カプセル化」=「良いこと」=「いつでもやるべきこと」を徹底的に教えておきたい。

「カプセル化」という言葉を使って説明すべきかは、賛否が別れた。

継承


Java は言語の仕組みとして、型継承(implements)と実装継承(extends)があり、この使い分けも含めて「継承」はもちろん重要な教育テーマ。

問題は「継承」をどう位置づけて、どのタイミングで、どう教えるか?

「カプセル化」は、プログラムのどこでもやるべきこと(良いこと)。

でも「継承」はそうではない。

プログラムのありとあらゆる場所が、型継承/実装継承だらけのソフトウェアは、どうみても良いソフトウェアではない。

少なくとも「オブジェクト指向らしいプログラミング」=「継承を使いまくる」=「よいプログラミング」という、刷り込みは絶対にすべきではない。

むしろ「継承」は「使いどころが難しいよ」ということを最初から強調したほうがよさそう。

だから研修の初期では「継承」はさらっと「そういう仕組みもあるけど、具体的には後で」という先送り。

業務アプリケーションでドメイン層を実装することに限定すれば、実装継承(extends)を使うべきケースはほとんどないと思っている。
共通のロジックは共通部品として「コンポジション」として実装するほうが業務の概念に合っていることが多い。

型継承(implements)も、あちこちで積極的に使うべき仕組みではない、と思っている。

開発の初期段階で発見した(つもり)の継承関係は、要件を深く掘り下げていくと、まちがった理解であることが多い。

経験上、ドメインの理解が表面的なプロジェクトの初期に、継承関係をあちこち設計・実装してしまうと、ドメインを深く理解できるようになったあとで、ドメインの本来あるべき構造と、実装してしまった継承構造が大きく捻じれていて苦労する失敗パターンばかりの気がする。

継承構造は、実装したクラスが増えてきて、ロジックやコードブロックで具体的な共通性を把握できてから、必要に応じて、段階的に、リファクタリングをしながら導入していくのが、実践的な良いやり方だと思う。

「継承」は「リファクタリング」の学習の中で覚えていくべき技術かもしれない。

ポリモーフィズム


大切であることは全員一致。
ただし、そのために「継承」を教える、というのは「なんか違う」というのが一つの結論。

そもそも「ポリモーフィズム」という「言葉」から教えたり、その実現手段として「継承」を強調するのは、ダメパターンだった、というのがいままでの経験。

もっと業務アプリケーションを書く上での具体例、必然的な例で「実装のやり方」という振り切り方で教えるのが良いのでは?

「ポリモーフィズム」という「言葉から入る」のではなく「if 文をなくすテクニック」という切り口で教えてみる。

if( value == null ) ...

という null チェック をプログラミングの基本のお作法として教えるのではなく、null object パターンあるいは簡単な strategy パターン を、かなり早い段階の研修内容に含める。
(パターンの名前そのものは使わない。コード例でのみ教える。簡単なクラス図は書いたほうがよさそう)

購入日オブジェクトがあるとして、

・正しい日付
・正しくない日付( 2月30日と入力された場合とか )
・不明の日付

を表示する場合、表示ロジックを if 文で書きわけるのではなく、

・購入日インタフェースでasText()メソッドを宣言

・有効購入日 (「年月日」を表示)
・無効購入日 (「年月日」を表示。「不正」の警告も表示)
・不明購入日 (「未記入」を表示)

の実装クラスごとに、個別の asText() メソッドを実装する。

こういう書き方を、if 文 を覚えた直後に教えてもいいくらいなのでは?

「ポリモーフィズム」という言葉を一切語らず、「if 文を減らすテクニック」をいろいろ教えることで、結果的に「ポリモーフィズム」を習得する、というのがいいんじゃないかと。

null オブジェクトパターン以外にも、

・boolean を enum に置き換える
・enum に振舞を持たせる
・Map を使った場合分け
・Set を使った場合分け

など、if 文の代替手段は、いろいろある。

「ポリモーフィズム」とは何か、なぜ必要か、という教え方でなく、「 if 文以外の条件分岐の書き方」という振り切り方で教えたほうが、実践的なんではないかと。

すくなくとも「これでわかった Java 入門」みたいな本をテキストとして使うより、はるかに効果的な教え方だと思うんだけどなあ。

設計力の向上

今年は設計力を向上する。
そのために設計の基本をちゃんと勉強しなおそうと思う。

ソフトウェア開発を生業にしている以上、日々なんらかの設計作業はしている。
しかし、経験と勘と度胸で乗り切っているが、一流のプロの設計とは言い難い。それは自分がいちばん分かっている。

ある日突然、スーパーエンジニアに変身できるわけはないが、設計力を向上するためにプロとして最低限の努力は続けたいものだ。

今年の最初の記事なので「決意表明」みたいな力みがあって、読みやすい文章にはならないだろうけど、設計力の向上について、自分のキーワードを書きだしておこうと思う。

心技体


設計力も、心技体だと思う。
心を練り、技を磨き、体を鍛えることがたいせつ。

■体を鍛える


文字通り体力づくりですね。

納期は迫る、バグは消えない、性能は出ない、この期に及んで仕様変更の追い討ち、...

こういう状況で、プロとして設計の質を維持するには、ぜったい体力が必要。
設計とは、頭を使って考えてなんらかの決断をすること。
厳しい状況、いろいろなプレッシャーな中で、へろへろになった状態で、どこまで良い決断ができるかは、体力しだい。それが現実だと思っている。

そういう状況にならないように、プロジェクトのやり方を工夫することはもちろんたいせつ。
まずいプロジェクト運営を体力と根性で乗り切れ、と主張する気はありません。

ただ、良い設計をするためには、健康な体・基礎体力が基本になりますよね、ということ。

自分は、完全に運動不足。
設計力の「体を鍛える」ために今年は意識的に体力づくりをしようと思う。
まずはエスカレータ使わず階段使う、くらいから。

■技を磨く


現場で格闘を繰り返してきているので、経験と勘と度胸は、それなりに身についてきた。
本やネット上で、ソフトウェア設計についての情報にはそれなりに触れていると思う。

でも基本的な設計の考え方が、きちんと身についているか?とあらためて考えると、答えは、はっきりノー。
結果的にソフトウェアは動いてはいるが、それが良い設計である証明にはなっていないことは、自分でもよく分かっている。

もともと「原理原則より実践じゃあ」「設計用語を覚えただけで設計できるなら苦労はしない」「現場を知らずにえらそうなこと言うな」という超現場主義の体質が、ここにきて、やはり限界にきている気がする。

今年は、ちゃんとソフトウェア設計の原則を、基本から勉強しなおそうと思う。

デザインパターンなんかも「知っている」「使っている」というレベルではなく、その背景にある「考え方」を理解し、いつ、どう使うか/使わないかを適切に「判断する力」を身に着ける、という視点で学びなおそうと思う。

自分は、業務アプリケーションの開発が専門で、設計の流儀は、ドメイン駆動設計にこだわっている。だから、ドメイン駆動設計を実践するための基本を学び直そうと思う。

コードを書きながら体で基本を覚えるには、

◎オブジェクト指向エクササイズ 9つのルール
◎リファクタリング by マーチンファウラー
◎実装パターン by ケントベック
◎Effective Java by ジョシュアブロック

を地道に実践してみる。身体で覚える。

設計の考え方は「オブジェクトへ責任の割り当て方」を軸に本の読み直しと、実際の自分たちのコードで検証してみようと思う。

◎オブジェクトデザイン by ワーフスブラック
◎実践UML、特に GRASP by クレーグラーマン
◎アジャイルソフトウェア開発の奥義、特にクラスとパッケージの設計原則

いずれも、今まで、何回か読んだことはあるし、知識として知らないわけではない。
ただ知識や理解が断片的。一番、問題なのは、日々の設計活動と、これらの知識がほとんど関係づいていないこと。

おそらく、自分たちなりに良い設計だと思っている箇所は、これらの書籍で語られている設計原則をうまく実践できているんだと思う。

設計でぎこちなさや違和感を感じている箇所は、設計原則に反していることが具体例になっているんだと思う。

実際のコード(設計内容)と、これらの原則を突き合わせながら、設計の基本の考え方を、勉強しなおしてみようと思う。

■心を練る


設計力とは判断力だと思っている。

論理的に考えたり、いろいろな手法を適用すれば、それなりのレベルの設計はできると思う。
でも、プロとしてもっと上のレベルを目指したい。

「心の思うままに設計すると、それが最高の設計になる」

このレベルに実際に到達できるとは思わないが、そういう境地にあこがれる。

技を練り、体を鍛え、現場での実践を正しく積み重ねていくことで、こういう境地に近づけるんだと思う。

as a whole:全体力


システムの設計は、as a whole つまり全体としての質の問題なんだと思う。
しかもその質とは、一言で言い表せるようなものではないし、数値化して比較できるようなものでもない。

しかもシステムの全体は、常に変化を続けるもの。その中で「いきいきたとした全体」をどうやって生み出していくか?

この考え方は、もちろん「環境設計の手引き パタン・ランゲージ」クリストファーアレグザンダーから影響を受けている。

ソフトウェアシステムの設計について、全体としての質、成長を続ける全体を生むための設計、という視点で、もう一度、この本も読み直してみようと思う。

ソフトウェア設計の技術書、それから現場での実践は、どうしても局所での判断をしがち。また、目先のリリース日が最終ゴールであるかのように行動してしまう。

良い設計は「全体」を、そして全体が「成長を続ける」ことを、もっと意識することが大切なんだと思う。

こういう「全体力」が設計力の重要な要素。
問題は「全体力」をどうやって勉強して身につけるか。

in the context:文脈力


全体力とならんで重要なのが「文脈力」。
ソフトウェアの設計判断は「文脈」(取り巻く環境/関係者の都合や事情/前後関係)に依存する。

書籍で語られる「原理・原則」や「パターン」は、個々の文脈を捨象したり、高度に一般化した結果ともいえる。

「パターン」は「ベストプラクティス」である、という説明もあるが「汎化した」ベストプラクティスという考え方には正直なじめない。

設計でたいせつなのは、現在の特定の文脈においてのベストプラクティスを見つけることなんだと思う。

自分は、特定の文脈の経験、その中で身につけてきた勘と度胸を重視しすぎていることは自覚しているつもり。

だからこそ、一般的な原理とか基本原則を学び直そうとは思う。
でも、設計は、最後は、今の特定の文脈の中でのベストプラクティスを求める、ことだと思う。

問題は、全体力と同じ。
「文脈力」をどうやって勉強し身につけるか。

全体力/文脈力の学び方


難しすぎるテーマではあるが、今年は、いくつかチャレンジしてみたい。

■学習パターン


慶応SFCの井庭先生の研究室のパターンランゲージ研究の成果の一部として

学習パターン

が公開されている。これを題材に技術者仲間で「技術者の学び方」をテーマに勉強会を続けていた。

それがひとつのきっかけになって、DevLove のイベントで、井庭先生に講演いただいたり、対談までさせていただいたいのは、貴重な経験だった。

「全体力」や「文脈力」を磨くには、この学習パターンが参考になると思っている。

たとえば「鳥の眼、虫の眼」「広がりと掘り下げのT字」「隠れた関係性に学ぶ」とかは、そのまま「全体力」や「文脈力」の学び方になっていると思う。

あまり読み込めてないけど、同じく井庭研プロジェクトの、

コラボレーションパターン

も「全体力」「文脈力」の学び方の手がかりになりそう。

あたりまえだけど、井庭先生のパタンランゲージの研究はクリストファーアレグザンダーの業績を深く研究された上での活動。

これらの研究成果を「ソフトウェアの設計力」とくに「全体力」「文脈力」の能力アップの手がかりとして生かしていきたい。

国語力


最近、感じているのは「国語力」。

「設計力」と「国語力」は、基本的に同じものなのかもしれないとさえ思う。

自分自身の「本を読む力」「文章を書く力」は貧弱だと思う。さらにまずいことに、年々、劣化している気がする。

私の文章を書く力は、このブログでご覧の通りです。
もともと「自分のメモ書き」とか居直って書き始めているので、自分で読み返してもひどい文章が多い。

今年は、もうちょっと、ちゃんとした文章を書こうと言いながら、結局、こうやって、思いつくままにメモ書きモードから抜け切れない。

本を読む力は「ななめ読み」と「勝手読み」は得意。
その分、読み落としや、読み違えは日常茶飯事。

これだと思った本は、何度も読み返すほうなので、それなりに理解は進んでいる気もするけど、何度、読み返しても「こんなこと書いてあったんだ」という再発見の繰り返し。

そういう再発見があるのは、読み直す価値のある「良書」だから、と勝手な理屈で、自分のずさんな読み方を正当化してきたのが実態。

こういう読み方・書き方が、自分の設計のスタイルや設計品質に直結しているように思う。

設計力を向上するには、本の読み方、文章の書き方の基本をもう一度勉強しなおすのも、今年のテーマだと思っている。



calendar
  12345
6789101112
13141516171819
20212223242526
2728293031  
<< January 2013 >>
システム設計日記を検索
プロフィール
リンク
システム開発日記(実装編)
有限会社 システム設計
twitter @masuda220
selected entries
recent comment
recent trackback
categories
archives
others
mobile
qrcode
powered
無料ブログ作成サービス JUGEM