はてなキーワード: Abstractとは
これは http://anond.hatelabo.jp/20110316202255 の続編です。
GTをやる前に改を書いてくれている人がいてとてもしっかりした内容なのでちゃんと勉強したい人はそっちを見てね!
d:id:ryoasai:20110317 - ドラゴンボールで学ぶオブジェクト指向 改 | 達人プログラマーを目指して
またオブジェクト指向については
d:id:m-hiyama:20080109 いまさらながらだけど、オブジェクトとクラスの関係を究めてみようよ | 檜山正幸のキマイラ飼育記
がとても詳しいです。合わせて読むとかなりしっかりと理解出来ると思います。
ホットエントリに行くとは思っておらず、皆様ありがとうございます。
「ドラゴンボールをオブジェクト指向にする」というコンセプトではなく、「オブジェクト指向を(無理矢理)ドラゴンボールで説明する」という遊びだったので
プログラマーの方々にはツッコミを受けてしまいましたがここは遊びだと思って楽しんで下さい…。
ドラゴンボールは小さい頃から大好きでしたが流石にうるおぼえ過ぎました。
それはさておき「説明する題材を決める→好きな漫画から無理矢理当てはまりそうな例を考える」という思考実験なので、気が向いた方は色々考えてみると楽しいと思います。僕は楽しかったです。
これは難易度が高そうです。
やっぱりドラゴンボールで例えると分かりやすいな!
無理がある!
デザインパターンとはドクターゲロが考えた「こうやって設計すれば色々捗るぞ」という例のことです。実際はGoFという人たちが考えたもので23個のよくあるパターンに名前を付けて整理してくれたわけですね。
23個の中にはブルマさんですらわからないものが多いので(さすがドクターゲロですね)良く使う、代表的な物をいくつか紹介します
Singletonは世界に一つだけしか存在出来ないようにする方法です。
balls = new DragonBalls(); //これでは誰でもドラゴンボールを作れてしまう! balls.callShenron();
クラスの中にはいくつかのメソッドがありますが、簡単に言うと外から呼べるもの、外からでは呼べないものの
二種類があります。そうやってメソッドを保護することで世界の崩壊を防ぐわけですね。
基本的な戦闘力をアップさせるには本人の努力が必要になり、外から簡単に挙げられてしまうとジャンプの三本柱が外れてしまいます。
ただナメック星の最長老や界王神などはかなり偉いので、本人の才能を引き出すことが可能でした。
現実には思いつきのような仕様を後から言われることが多々あります。困ります。
//地球上にひとつだけ存在するドラゴンボールをつくろう class DragonBalls{ private DragonBalls(){ //ドラゴンボールを作れないように生成メソッドを保護します。 } static function sagasouze(){ static singleton_dragonball; //ドラゴンボールを生成。 //DragonBallsクラスの中なので、保護してある「new DragonBalls()」を呼べます。 if(!singleton_dragonball)singleton_dragonball = new DragonBalls(); return singleton_dragonball; } }
地球のみんなは地球語しか話せませんが、ナメック星にいるクリリンを通して願いを叶える必要があります。
クリリンももちろん地球語しか話せませんが、ナメック語を話せるデンデがいるため、地球のみんなは願いを叶えることが出来ます。
class Kuririn{ private dende = new Dende(); function request( wish1, wish2, wisth3){ this.dende.request(wish1); this.dende.request(wish2); this.dende.request(wish3); } } kuririn.request( "ピッコロを生き返らせてくれ", "ピッコロをナメック星へとワープさせてくれ", "ナメック星にいる孫悟空とフリーザ以外を全員地球へとワープさせる" );
この場合のメリットはデンデが何をやっているかクリリンをプログラミングした人が知る必要が無いということです。
地球の人はナメック星にいるナメック星人が「デンデ」であることを知る必要もありません。
それでも願いは叶うんです。
本来であればデンデやクリリンは願いが叶うのを待つ必要がありましたが、地球の人は一気に伝えることが可能なように設計しました。
//デンデクラス。ナメック星人は英語でNamekianらしいです。 class Dende extends Namekian{ function translate(word){ namekian = *****//ナメック語に翻訳します。 return namekian; } function request(wish){ static polunga; if(!polunga){ polunga = DragonBalls.spell("タッカラプト ポッポルンガ プリピット パロ"); } polunga.ask(this.trasnlate(wish)); } }
大まかなアルゴリズムだけ決めておいて、実装はサブクラスに任せる設計がTemplate Methodです。
ナメック星に行く方法を考えた時いくつかの方法がありました。古い宇宙船を探してきて直して載せて…っていちいち書くより同じメソッドでナメック星に行けたほうが便利ですね。
abstract class WayToNamek{ abstract function prepareSpaceShip(); abstract function launchSpaceShip( ship ) ; function gotoSU839045YX( people ){ ship = prepareSpaceShip( ); //船を修理します ship.load(people); //人を載せます this.launchSpaceShip(ship); //船を出発します。 } }
ナメック星に行く方法を定義したので「ブルマ、クリリン、悟飯」組と「悟空」をそれぞれナメック星に連れて行きましょう。
way = new WayWithKamisamaShip(); way.gotoSU839045YX( buruma, kuririn, gohan ); way = new WayWithSaiyajinShip(); way.gotoSU839045YX( goku );
と簡単に方位SU83、距離9045YXまで乗員を連れて行くことが出来ます。
二つの方法を実装します。神様の船を修理して行く方法と、サイヤ人の船(悟空が乗ってきた船)で行く方法の二つです。
//神様の船で行きます。 class WayWithKamisamaShip extends WayToNamek{ function prepareSpaceShip(){ return new KamisamaShip(); //船を準備します。神様の船を使います。 } function launchSpaceShip(ship){ ship.inputByVoice("ナメック星に出発"); // } } class WayWithSaiyajinShip extends WayToNamek{ function prepareSpaceShip(){ return new SaiyajinShip(); //船を準備します。サイヤ人の船(フリーザの船?)を使います。 } function launchSpaceShip(ship){ //audio = new HighSpecAudio(); //ship.setAudio(audio); ship.turnOnCenterButton(); //真ん中のボタンを押すだけ } }
元になる船も違いますし、発射の仕方も違いますが同じ呼び出し方が出来ます。
オーディオの位置が決まりませんでしたが、今回の運用では不要とのクライアントからのご意見でしたのでだったので
せっかく用意したオーディオも無駄になりましたが、コメントアウトしてあります。
渡米して数年のポスドク。最近やや行き詰まり気味なので、現実逃避しつつ現状把握および打開策を見つけるためにパソコン内を再構築中。時期的に興味を持たれる人もあろうかと思ったので、恐る恐る一応公開してみることに。
([]はフォルダ)
public class Main { public static void main( String[] args ) { // ( 5 - 3 ) + 1 Exp exp = new Add(new Sub(new Num(5), new Num(3)), new Num(1)); System.out.println(exp.eval()); } } /** * 抽象クラス */ abstract class Exp { public abstract int eval(); } /** * 足し算 */ class Sub extends Exp { /** 左辺 */ private Exp hidari; /** 右辺 */ private Exp migi; /** * コンストラクタ */ public Sub(Exp hidari, Exp migi){ this.hidari = hidari; this.migi = migi; } /** * 評価 */ @Override public int eval() { return hidari.eval() - migi.eval(); } } /** * 引き算 */ class Add extends Exp { /** 左辺 */ private Exp hidari; /** 右辺 */ private Exp migi; /** * コンストラクタ */ public Add(Exp hidari, Exp migi){ this.hidari = hidari; this.migi = migi; } /** * 評価 */ @Override public int eval() { return hidari.eval() + migi.eval(); } } /** * 数 */ class Num extends Exp { /** * 数 */ private int self; /** * コンストラクタ */ public Num(int self) { this.self = self; } /** * 評価 */ @Override public int eval() { return self; } }