HOME Sather を試そう download 書き込む

5. クラス定義


1. はじめに

いよいよ自前のクラス定義について述べます。

Sather はオブジェクト指向言語なので、クラス定義は中心的な機能です。

Sather のクラスシステムは結構複雑で、通常のクラスの他に 部分クラス (partial class)、 抽象クラス (abstracted class)、コンテナ型クラス (parametrized class)、変更不能クラス (immutable class) があります。 今回は通常のクラス、部分クラスについて説明し、コンテナ型クラス、抽象クラス、変更不能クラスは 7, 8 章で説明します。

2. 通常のクラス

まず、通常のクラスについての述べます。 オブジェクト指向についての知識がある読者を対象にしているので、退屈な説明は抜きにして例を挙げて説明します。

今流行の携帯電話を例にとります。携帯電話は電話であり、かつメーラーとしても機能しますので、 電話とメーラーの属性を継承することになります。 Sather は多重継承をサポートしているので、電話クラス、 メーラークラス、携帯電話クラスは次のように書くことができます。

2.1. 電話クラス

まず電話クラスを定義します。 属性:電話番号とメソッド:電話をかける を持ちます。 説明はソースコード内にコメントとして付け加えておきました。
01:     class PHONE is                      -- 電話クラスです
02:     
03:        private attr phone_number:STR;   -- 電話番号です。private はプライベート変数。 attr はインスタンス変数の意味です。
04:                                         -- private をつけないとパブリック変数になります。
05:     
06:        create(pn:STR):SAME is           -- create は特殊なメソッドで、インスタンスを作るときは必ずこれを使います。
07:           return new.init(pn);          -- まず new でメモリー領域を確保して、それに init メソッドを作用させます。
08:        end;                             -- 返り値 SAME は create を呼んだクラスです。継承を考えると SAME を使うのが良いでしょう。
09:     
10:        private init(pn:STR):SAME is     -- init は初期化の関数です。create の書き方は他にもありますが、継承を考えると return new.init
11:           phone_number := pn;           -- と書くのが良いでしょう。インスタンス変数 phone_number を初期化します。
12:           return self;                  -- 自分自身 (self) を返します。
13:        end;
14:     
15:        get_number:STR is                -- 電話番号を取得するメソッドです。self.phone_number の self は省略できます。
16:           return phone_number;
17:        end;
18:     
19:        phone_call(to:SAME, msg:STR):STR is    -- 電話をかけるメソッドです。他の電話とメッセージを引数に取ります。
20:           return "From: " + phone_number +    -- 簡単のため文字列を返すだけです。
21:     	    "\nTo: " + to.phone_number +  -- to.phone_number のようにインスタンスを特定することもできます。
22:     	    "\nSubject: " + msg + "\n";   -- この関数でも SAME を使っています。可能な限り SAME を使うほうが良いでしょう。
23:        end;
24:     end;  -- end of PHONE
その他 電話のインスタンスを作るときは次のようにします。
 phone1:PHONE:=PHONE::create("012-345-6789");  -- 省略していない書き方です。省略形を使ってコンパイラーに文句を言われたらこう書く必要があります。
 phone2:PHONE:=#PHONE("023-444-5555");         -- 通常は '#PHONE' で代用できます。
 phone3:PHONE:=#("023-4545-6767");             -- さらに、作られるインスタンスの型が明らかな場合は '#' で代用できます。

2.2. メーラークラス

以下のように定義します。
自分のメールアドレス、新着メールリストをインスタンス属性として持ち、 メーリングリストをクラス属性として持ちます。 また、メソッドとして、メールの送受信、メーリングリストへの投稿、メーリングリストを読むを持ちます。
01:     class MAIL is
02:        private attr address:STR;                                 -- メールアドレス
03:        private attr newmails:LIST{STR};                          -- 新着メールリスト LIST{STR} 型
04:        private shared mailinglist:LIST{STR}:=LIST{STR}::create;  -- クラス属性 メーリングリスト (LIST{STR}) を宣言して作成。 
05:                                                                  -- constant と shared の場合は '#' を使った省略形が使えない。
06:        create(ad:STR):SAME is
07:           return new.init(ad);
08:        end;
09:     
10:        init(ad:STR):SAME is
11:           newmails := #LIST{STR};                                -- 新着メールリストを作成。
12:           address := ad;                                         -- 自分のアドレスを設定
13:           return self;                                           -- 自分自身を返す
14:        end;
15:     
16:        get_address:STR is                                        -- アドレスを取得
17:           return address;
18:        end;
19:     
20:        send(to:SAME, msg:STR) is                                 -- メールを送るメソッド。相手先の新着メールリストにメッセージを追加
21:           to.newmails.append("From: " + address + "\nSubject: " + msg + "\n");
22:        end;
23:     
24:        send2mailinglist(msg:STR) is                              -- メーリングリストに投稿。メーリングリストにメッセージを追加
25:           mailinglist.append("From: " + address + "\nSubject: " + msg + "\n");
26:        end;
27:     
28:        read_mail:STR is                                          -- メールを読む。
29:           s:STR := address + " received following new messages:\n";
30:           loop
31:     	 s := s+ newmails.elt!;                              -- 新着メールリストから新着メールを取り出す
32:           end;
33:           newmails.clear;                                        -- 新着メールをクリアする。
34:           return s + "\n";
35:        end;
36:     
37:        read_mailinglist:STR is                                   -- メーリングリストを読む。
38:           s:STR := "Messages in the mailing list are:\n";
39:           loop
40:     	 s := s+ mailinglist.elt!;
41:           end;
42:           return s + "\n";
43:        end;
44:     
45:     end; -- end of MAIL
メモ

2.3. 携帯電話クラス

携帯電話クラスは電話とメーラーを継承します。簡潔に書くことができます。
01:     class MOBILE is                                 
02:        include PHONE init -> phone_init, create ->; -- 継承は include で表します。
03:        include MAIL  init -> mail_init, create ->;  -- 親クラスの init に別名をつけ、create は継承しません。
04:     
05:        create(ph,ad:STR):SAME is
06:           return new.phone_init(ph).mail_init(ad); -- 親クラスの init 関数を使って簡潔に書けます。
07:        end;
08:     
09:     end; -- end of MOBILE

2.4. テスト

テストルーチンを以下に示します。全体のプログラムは付録につけておきますのでダウンロードして遊んでみてください。
01:     class MAIN is
02:     
03:        main(av: ARRAY{STR}) is
04:           if av.size = 5 then
05:     	 mob1:MOBILE := #(av[1], av[2]);   -- 携帯電話を 2 つ作って
06:     	 mob2:MOBILE := #(av[3], av[4]);
07:     	 mobile_test(mob1, mob2);          -- 通話テストを行う。
08:           else
09:     	 #ERR + "Usage:mobile PHONE_NUMBER_1 EMAIL_ADDRESS_1 PHONE_NUMBER_2 EMAIL_ADDRES_2 \n";
10:           end;
11:        end;
12:     
13:        mobile_test(mob1, mob2:MOBILE) is
14:           #OUT + "Phone number(1) is " + mob1.get_number + "\n";          -- 携帯電話 (1) の電話番号を表示
15:           #OUT + "E-mail address(1) is " + mob1.get_address + "\n";       -- 携帯電話 (1) のメールアドレスを表示
16:           #OUT + "\nPhone call test (1) -> (2):\n" +                      -- 携帯電話 (1) から (2) に電話をかける。
17:     	    mob1.phone_call(mob2, "Hello");
18:           #OUT + "\nE-mail transfer test (2) -> (1):\n";
19:           mob2.send(mob1, "How are you?");                                -- 携帯電話 (2) から (1) にメールを送る。
20:           mob2.send(mob1, "I want to see you.");                          -- 携帯電話 (2) から (1) にメールを送る。
21:           #OUT + mob1.read_mail;                                          -- 携帯電話 (1) が新着メールを見る。
22:           #OUT + "\nTest for mailing list:\n";
23:           mob1.send2mailinglist("Is Sather good?");                       -- 携帯電話 (1) がメーリングリストへメールを出す。
24:           mob2.send2mailinglist("Yes. Sather is good.");                  -- 携帯電話 (2) がメーリングリストへメールを出す。
25:           #OUT + mob1.read_mailinglist + "\n";                            -- メーリングリストを見る。
26:        end;
27:     end; -- end of MAIN
28:     
> sacomp mobile.sa -o mobile
> ./mobile 012-222-3333 foo@ab.ne.jp 023-444-5677 bar@cd.com
Phone number(1) is 012-222-3333
E-mail address(1) is foo@ab.ne.jp

Phone call test (1) -> (2):
From: 012-222-3333
To: 023-444-5677
Subject: Hello

E-mail transfer test (2) -> (1):
foo@ab.ne.jp received following new messages:
From: bar@cd.com
Subject: How are you?
From: bar@cd.com
Subject: I want to see you.


Test for mailing list:
Messages in the mailing list are:
From: foo@ab.ne.jp
Subject: Is Sather good?
From: bar@cd.com
Subject: Yes. Sather is good.

3. 部分クラス

部分クラスは継承されることを前提としたクラスです。 自身のインスタンスを作ることは無く、かつ stub 変数を持ちます。stub 変数は子クラスで再定義される必要があります。

今流行の無線 LAN を例にとってみます。無線 LAN は盗聴される危険があるので、 個人レベルでも WEP か WPA-PSK などの暗号化を施す必要があります。
ここでは、部分クラス WLAN とそれを継承したクラス WEP, WPA_PSK を定義します。

01:     -- an example of partical classes, WLAN
02:     
03:     partial class WLAN is
04:     
05:        stub security:STR;                -- この属性は子クラスで再定義される必要がある。
06:     
07:        get_security:STR is
08:           return security;               -- security を取得するメソッド。
09:        end;
10:     
11:     end;  -- WLAN
12:     
13:     
14:     class WEP is
15:        include WLAN;
16:     
17:        const security:STR := "wep";
18:        private attr key:STR;
19:     
20:        create(k:STR):SAME is
21:           return new.init(k);
22:        end;
23:     
24:        init(k:STR):SAME is
25:           key := k;
26:           return self;
27:        end;
28:     
29:        get_key:STR is
30:           return key;
31:        end;
32:     
33:     end; -- WEP
34:     
35:     class WPA_PSK is
36:        include WLAN;
37:     
38:        const security:STR := "wpa psk";
39:        private attr key:STR;
40:     
41:        create(k:STR):SAME is
42:           return new.init(k);
43:        end;
44:     
45:        init(k:STR):SAME is
46:           key := k;
47:           return self;
48:        end;
49:     
50:        get_key:STR is
51:           return key;
52:        end;
53:     
54:     end; -- WPA_PSK
55:     
56:     
57:     
58:     class MAIN is
59:     
60:        main is
61:           wlan_test;
62:        end;
63:     
64:        wlan_test is
65:           wep0:WEP:=#("foo");
66:           wpa0:WPA_PSK:=#("bar");
67:           
68:           #OUT + "Security and key of wep0 are: " + wep0.get_security + " and \"" + wep0.get_key + "\".\n";
69:           #OUT + "Security and key of wpa0 are: " + wpa0.get_security + " and \"" + wpa0.get_key + "\".\n";
70:           
71:        end;
72:     end; -- end of MAIN
出力は以下のようになります。
$ sacomp wlan.sa -o wlan
$ ./wlan
Security and key of wep0 are: wep and "foo".
Security and key of wpa0 are: wpa psk and "bar".

4. 終わりに

今回は普通のクラスについて例を挙げて説明しました。Sather には他にもいろいろなクラスがあるので おいおい説明していきます。一応 付録 にコードをつけておきます。

HOME Sather を試そう download 書き込む