VisualWorksに、自分で新しいクラスを作成してみましょう。ここでは、人間(HumanBeing)というクラスを作り、名前と誕生日を指定してインスタンスを生成すると、満年齢・現在まで生きてきた日数・平均寿命までの残りの日数を答えられるようにします。
プログラムの作成は、システムブラウザで行います。VisualWorksランチャーに並んでいるアイコンの左から4番目にあるアイコンを押すと、ウィンドウが開きます。VisualWorksランチャーの「Browse」メニューで「System」を選択する、もしくはVisualWorksランチャーを選択している時に「F5」キーをたたいても同様にシステムブラウザが開きます。
|
システムブラウザは、大きく上下に分かれていて、さらに上部は4つに分かれています。上部の左から、パッケージ(package)、クラス(class)、プロトコル(protocol)、メソッド(method)を表示するペイン(pane:小窓)です。下部は、基本的に上部で選んだもののソースコードが表示されます。
|
パッケージとプロトコルは、それぞれの隣に表示されるクラスとメソッドをわかりやすいように束ねておくものです。
まず、パッケージを作成しましょう。システムブラウザの「Package」メニューから「New Package...」を選択します。パッケージペインで右ボタンメニューを出し、「New Package...」を選んでも同様に、新パッケージ名を尋ねるダイアログが表示されます。
|
新パッケージ名を入力するダイアログが開きますので、「Foo-Example」と入力し、「OK」ボタンを押しましょう。すると、システムブラウザ上部のパッケージペインに入力したパッケージが追加されます。
|
次に、新しいクラス「HumanBeing」を作成します。パッケージペインで「Foo-Example」を選択し、クラスペインで右ボタンメニューを出して「New Class...」を選択すると、新クラスの情報を入力するダイアログが出ます。
|
「Name」はクラス名、「Instance Variables」はインスタンス変数名です。ここでは、クラス名「HumanBeing」、インスタンス変数に「name」「birthday」を作成します。図のように、ダイアログの「Name」に「HumanBeing」を、「Instance Variables」に「name birthday」を入力します。インスタンス変数はスペースで区切ることで複数指定することができます。また、「Create methods」ではメソッドの自動生成を行えます。今回は、「Accessors」(インスタンス変数にアクセスする)、「Initializer」(初期化する)、「Subclass responsibilities」(子クラスで実装しなければならないメソッド)のすべてにチェックを入れた状態で「OK」ボタンを押しましょう。クラスが生成されると、クラスペインに「HumanBeing」クラスが、プロトコルペイン、メソッドペインに自動生成されたメソッドたちが表示されます。
|
ではここから、各メソッドの編集を行います。まずは、自動生成されたメソッドを修正しましょう。
プロトコルペインでInstanceタブを選び、「initialize-release」→「initialize」を選択します。初期化をするメソッドの修正です。
|
コメント(ダブルクォートで括られた箇所)と、最後に「self」を返している箇所を削除します。
initialize name := nil. birthday := nil
編集しおわったら、フォーマット (プログラムの整形) をします。プログラムソースの枠内で、右ボタンメニューから「Format」を選択します。メソッド名が太字になり、ソースのインデント等がそろいます。
|
そして、プログラムをアクセプトします。プログラムソースの枠内で、右ボタンメニューから、「Accept」を選択します。アクセプトすると、プログラムが再登録されます。
|
次に、プロトコルペインでClassタブを選び、「instance creation」→「new」を選択します。インスタンスを生成するメソッドの修正です。
|
new ^(super new) initialize; yourself
編集しおわったら、フォーマットとアクセプトを必ずしましょう。
|
自動生成したメソッドは、引数がある場合その名前の意味がアバウトになっています。わかりやすくするために、引数名を変更しておきましょう。プロトコルペインでInstancesタブを選び、「accessing」→「birthday:」を選択します。
|
引数名が「anObject」になっていますが、誕生日は日付ですので、「aDate」に変更しましょう。
birthday: aDate birthday := aDate
くどいようですが、フォーマットとアクセプトを忘れずに!
|
同様に、「name:」メソッドの引数も修正しましょう。名前ですから、必ず文字列を受け取りますね。よって、「anObject」を「aString」に変更します。
name: aString name := aString
では京都産業大学が創立されて何年経ったかを調べる例題プログラムを作成してみましょう。まず、プロトコルペインでClassタブを選び、「examples」というカテゴリを作成します。例題プログラムは、クラスメソッドとして定義しましょう。プロトコルペインで右ボタンメニューを出し、「New...」を選択すると、新カテゴリ名を入力するダイアログが開きます。
|
ここでは「examples」というカテゴリ名で登録します。「OK」ボタンを押すと、プロトコルペインに「examples」が表示されます。
|
「HumanBeing」クラスのインスタンスを生成し、名前に「京都産業大学」、誕生日に「1965/05/04」をセットして年齢(創立されてからの年数)を取得します。そして、名前、誕生日、年齢をトランスクリプトに出力するメソッド「example1」を作成しましょう。以下のようなプログラムになります。examplesカテゴリを選択し、下部のソースコードペインにコピー&ペーストしてください。
example1 "HumanBeing example1." | aBeing | aBeing := HumanBeing new. aBeing name: '京都産業大学'. aBeing birthday: (Date newDay: 4 monthNumber: 5 year: 1965). Transcript clear; nextPutAll: aBeing name; cr; nextPutAll: aBeing birthday printString; cr; nextPutAll: aBeing age printString; flush. ^aBeing
2行目のコメントは、このメソッド自身を実行するプログラムです。こうしておくと便利ですよ。
フォーマットおよびアクセプトすると、メソッドペインにexample1が表示されます。
|
では、コメントにある実行プログラムを選択し、右ボタンメニューから「Do it」してみましょう。エラーダイアログが表示されます。
|
エラーには「Message not understood: #age」と書かれていますね。まだ「age」メソッドを作成していないので、「ageメソッドなんて知らないですよ」と言われているわけです。
では、年齢を計算する「age」メソッドを作成しましょう。インスタンスが持っている誕生日と実行した現在年月日を元に計算するので、今度はインスタンスメソッドになります。プロトコルペインで「Instance」タブを選び「accessing」を選択します。ソースペインに以下のプログラムをコピー&ペーストし、フォーマットおよびアクセプトをして登録してください。
age | today age birthdayInThisYear | today := Date today. age := today year - self birthday year. birthdayInThisYear := Date newDay: self birthday dayOfMonth monthNumber: self birthday monthIndex year: today year. today < birthdayInThisYear ifTrue: [age := age - 1]. ^age
|
まず、誕生年と現在年の差分を取得し「age」というテンポラリ変数にゲットします。また、今年の誕生日の年月日を取得し「birthdayInThisYear」にゲットします。今日の年月日「today」が「birthdayInThisYear」より小さければ、今年の誕生日をまだ迎えていないことになるので、「age」から1を引きます。これが年齢になるわけですね。読めるでしょうか?
では、先ほど作成したクラスメソッド「example1」をもう一度実行してみましょう。どうでしょうか。トランスクリプトに、名前「京都産業大学」、誕生日「May 4, 1965」、年齢(創立?年目)が出力されましたか?
次に、皆さんが今日まで何日生きてきたかを答えるプログラムを作ってみましょう。「example1」と同じようにクラスメソッドとして「example2」を作成します。以下のプログラムで名前と誕生日を自分のものに変えてみましょう。また、「age」と同じようにインスタンスメソッドとして「livingDays」を作成してください。
example2 "HumanBeing example2." | aBeing | aBeing := HumanBeing new. aBeing name: '京都産業大学'. aBeing birthday: (Date newDay: 4 monthNumber: 5 year: 1965). Transcript clear; nextPutAll: aBeing name; cr; nextPutAll: aBeing birthday printString; cr; nextPutAll: aBeing age printString; cr; nextPutAll: aBeing livingDays printString; flush. ^aBeing
|
livingDays | days | days := Date today asDays - self birthday asDays. ^days
|
クラスメソッド「example2」を実行してみましょう。トランスクリプトに、名前、誕生日、年齢、今まで生きてきた日数が出力されると思います。10,000日くらいになりましたか?
平均寿命までの残り日数を得るプログラムを作りましょう。
日本における平均寿命を「男性が79歳」「女性が86歳」とします。すでにあるプログラムを利用して、自分の名前、誕生日、年齢、今まで生きてきた日数、平均寿命までの残りの日数をトランスクリプトに出力するプログラムを完成させてください。
以下のプログラムを実行すれば、上記のことを踏まえて、問題を解くための用意が調います。
| aString aURL aFilename aBrowser aWindow aPackage aNavigator aClass aProtocol aSelector | aString := 'http://www.cc.kyoto-su.ac.jp/~atsushi/Programs/VisualWorks/Exercises/Exercise_HumanBeing/Exercise-HumanBeing.st'. aURL := JunURL named: aString. aURL exists ifFalse: [^nil]. aFilename := Filename defaultDirectory construct: aURL asURI tail. aURL downloadTo: aFilename. aFilename exists ifFalse: [^nil]. aFilename fileIn. aBrowser := #{Refactory.Browser.RefactoringBrowser} value open. aWindow := aBrowser builder ifNil: [^nil] ifNotNil: [:aBuilder | aBuilder window]. aWindow displayBox: (JunGeometry alignedBoxWithScreenCenter: 800 @ 700). aPackage := Store.Registry packageNamed: 'Foo-Example'. aPackage ifNil: [^nil]. aNavigator := aBrowser navigator. aNavigator selectPundle: aPackage. aClass := #{HumanBeing} value class. aProtocol := #examples. aSelector := #example3. (aNavigator state) classesAndNameSpaces: (Array with: aClass); protocols: (Array with: aProtocol); selectors: (Array with: aSelector). aNavigator setState: aNavigator state; changed
|
Dateクラスのメソッドで、必要そうなものを紹介しておきます。詳細は「System Browser」を開いて調べてみましょう。
プログラム例1:
age | today age birthdayInThisYear | today := Date today. age := today year - self birthday year. birthdayInThisYear := Date newDay: self birthday dayOfMonth monthNumber: self birthday monthIndex year: today year. today < birthdayInThisYear ifTrue: [age := age - 1]. ^age
プログラム例2:
livingDays | days | days := Date today asDays - self birthday asDays. ^days