第4章 クラス

クラスの作り方

オブジェクト指向言語と言えば、新たなクラスを作成する構文が何よりも重要と 言えましょう。Smalltalkでこれをするための方法を説明します。まずは、例か ら。

   1     Object subclass: #TheSubclass
   2            instanceVariableNames: 'instVar1 instVar2'
   3            classVariableNames: 'classVar1 classVar2'
   4            poolDictionaries: ''
   5            category: 'TheCategory'.
   6     !

ここでは、Objectクラス(Smalltalkの全てのクラスの基底クラス) の派生クラス TheSubclass を作っています。インスタンス変数 (C++で言う「データメンバ」)instVar1instVar2、クラス変数(C++で言う「静的データメンバ」) classVar1classVar2 です。また、Smalltalkの クラスはカテゴリ(category)に分類するのですが、ここでは、 TheCategory というカテゴリに TheSubclass を分類しています。

ここで何をやっているかと言うと、簡単な話、Object のクラスメソッドである、 subclass:instanceVariableNames:classVariableNames:poolDictionaries:category: (長い名前…)を実行しているのです。つまり、派生クラスを作 るのも、メソッドを実行するのです。

構文をまとめると、以下のような感じです。

   1     基底クラス subclass: 派生クラス名のシンボル(つまり # を先行させる)
   2                instanceVariableNames: インスタンス変数の並びの文字列
   3                classVariableNames: クラス変数の並びの文字列
   4                poolDictionaries: プールディクショナリの並びの文字列
   5                category: カテゴリ名の文字列.
   6     !

え? 「プールディクショナリ」とは何かって? ええと…よく知りません。(;) まあ、良いじゃないですか。ね。使ったこと無いですし…。だめですか?

よくある例ですが、猫を表すクラスでも作ってしまいましょう。インスタンス変 数は猫の名前です。

   1     Object subclass: #Cat
   2            instanceVariableNames: 'name'
   3            classVariableNames: ''
   4            poolDictionaries: ''
   5            category: 'AnimalClasses'.
   6     !


メソッドの作り方

メソッド(C++で言うところの「メンバ関数」)の作り方は少しト リッキーです。上で作った Catクラスのメンバ関数を作りましょう。まず、猫に 鳴いていただきましょう。引数をとらないメンバ関数 mew を作ります。

   1     !Cat methodsFor: 'mewing'!
   2     mew
   3         Transcript showCR: 'mew'.
   4     ! !

これがC++では、

    // mewing
    void Cat::mew()
    {
        cout << "mew" << end;
    }

に相当すると言えばよいでしょうか? (厳密に言えば、void じゃないん ですよね。Smalltalkのメソッドはデフォルトでself~-(C++ 等のthisに相当)を返しますので)-~ 最初のチャンクで、 methodsFor: メソッドを実行して、つづくチャンクでメソッドの実装を 書きます。メソッドの定義の終わりは、空のチャンク(! ! の部分です)を書きます。(ちなみに、methodsFor: の引数には メソッドの説明を書きます) ともかく、これを書いた上 で、

   1     |cat|
   2     cat := Cat new.
   3     cat mew.
   4     !

とすると、ちゃんとTranscriptに mew と表示されるはずです。 (new クラスメソッドは自動的に定義されます)

つづいて、名前を設定したり、所得したりするメソッドを作りましょう。C++風 に書けば、

    // accessing
    void Cat::setName(string newName)
    {
        name = newName;
    }
    string Cat::getName()
    {
        return name;
    }

にあたるようなメソッドです。Smalltalkでは、これらのメソッドは、setName のかわりに name: 、getName のかわりに name という名前をつける習慣になっ ています。

   1     !Cat methodsFor: 'accessing'!
   2     name: newName
   3         name := newName.
   4     !
   5     name
   6         ^name.
   7     ! !

同じ説明(accessing)の元で複数のメソッドを定義するに は、! で区切ればよいだけです。また、^ がC++等 の return に相当します。新たに作った猫に tama という名前をつけて、それを表示するには、

   1     |tama|
   2     tama := Cat new.
   3     tama name: 'tama'.
   4     Transcript showCR: tama name.
   5     !

とします。

ついでに演算子型のメソッドの作り方も紹介しましょう。今までと全く同じです。

   1     !Cat methodsFor: 'bearing'!
   2     + anotherCat
   3         ^Cat new.
   4     ! !

ええ、つまりですね、例えば、tama と nyanko という二匹の猫が子供を作りま して、chibi という猫を産んだとします。(笑) 以下のように書いて下さい。無 理矢理ですが。

   1     |tama nyanko chibi|
   2     tama := Cat new name: 'tama'.
   3     nyanko := Cat new name: 'nyanko'.
   4     chibi := tama + nyanko.
   5     chibi name: 'chibi'.
   6     !

name: のように、返値を明示的に返さないメソッドは、デフォルトでレシーバ自 身、つまり self (C++でいう this ) を返すので、

   1 tama := Cat new name: 'tama'.


クラスメソッド

クラスメソッド(C++でいうところの「静的メンバ関数」)を作り ましょう。今までは、new: で作った猫は最初は名前がついていなかったので、 新たな猫を作ると同時に名前をつけるようにしましょう。

   1     !Cat class methodsFor: 'creating'!
   2     new!WithName: newName
   3         |newCat|
   4         newCat := self new.
   5         newCat name: newName.
   6         ^newCat.
   7     ! !

methodsFor: メソッドの前に、class メソッドで、クラスメソッドを作ることを 明記すれば良いんですね。(そう、Smalltalk では、new もクラスメソッドでし かないということを思い出しましょう) self は何度も言ってきたように、C++等の this に相当します。ではこ の場合の self は一体どのオブジェクトでしょうか? この newWithName: クラス メソッドに Transcript showCR: self. とでも書けば分かります が、self にはオブジェクト Cat が束縛されています。C++等では考えられない ことですが、全てのクラスもオブジェクトである、純粋なオブジェクト 指向言語であるSmalltalkでは、このように変数にクラスが束縛されていること もあるのです。

使い方は以下のとおり。

   1     |tama|
   2     tama := Cat newWithName: 'tama'.
   3     Transcript showCR: tama name.
   4     !


   1 "animal.st"
   2 Object subclass: #Animal
   3        instanceVariableNames: 'name'
   4        classVariableNames: ''
   5        poolDictionaries: ''
   6        category: 'Animals'.
   7 !
   8 !Animal class methodsFor: 'creating'!
   9 new!WithName: newName
  10     |newAnimal|
  11     newAnimal := self new name: newName.
  12     ^newAnimal.
  13 ! !
  14 
  15 !Animal methodsFor: 'accessing'!
  16 name
  17     ^name.
  18 !
  19 name: newName
  20     name := newName.
  21 ! !
  22 
  23 Animal subclass: #Cat
  24        instanceVariableNames: ''
  25        classVariableNames: ''
  26        poolDictionaries: ''
  27        category: 'Animals'.
  28 !
  29 !Cat methodsFor: 'mewing'!
  30 mew
  31     Transcript showCR: name, ': mew!!'.
  32 ! !
  33 
  34 Animal subclass: #Dog
  35        instanceVariableNames: ''
  36        classVariableNames: ''
  37        poolDictionaries: ''
  38        category: 'Animals'.
  39 !
  40 !Dog methodsFor: 'barking'!
  41 bark
  42     Transcript showCR: name, ': bowwow!!'.
  43 ! !
  44 
  45 "main routine"
  46 |aCat aDog|
  47 aCat := Cat newWithName: 'tama'.
  48 aDog := Dog newWithName: 'kuro'.
  49 aCat mew.
  50 aDog bark.
  51 !

そうそう、言い忘れていましたが、Smalltalkの全てのメソッドは原則、C++でい う public 、インスタンス変数は原則 protected です。

wiredBeep/topics/Programming/Smalltalk/ForProgrammers/04-classes (last edited 2008-01-26 14:30:59 by beeplex)