オブジェクト指向プログラミング (OOP)

Ring でオブジェクト指向プログラミングのパラダイムを扱う方法です。

  • クラスとオブジェクト

  • 括弧を用いたオブジェクトへのアクセス

  • コンポジション

  • Setter と Getter

  • プライベート属性とメソッド

  • 演算子のオーバーロード

  • 継承

  • 動的属性

  • パッケージ

  • オブジェクトの表示

  • Find() とオブジェクトのリスト

  • Sort() とオブジェクトのリスト

  • Self.属性 および Self.メソッド() の用法

  • This.属性 および This.メソッド() の用法

クラスとオブジェクト

このシンタックスで新しいクラスを定義できます。

文法:

Class <クラス名> [From|<|: <親クラス名>]
        [属性]
        [メソッド]
        [Private
          [属性]
          [メソッド]
        ]

また、このシンタックスでオブジェクトを作成できます。

文法:

New <オブジェクト名> [ (init メソッドの仮引数) ] |
[ { オブジェクトのデータ、およびメソッドへのアクセス } ]   ---> オブジェクト

用例:

New point { x=10  y=20  z=30  print() }
Class Point x y z func print see x + nl + y + nl + z + nl

注釈

{ } でオブジェクトのデータとメソッドへアクセスできます。

ちなみに

クラス名の直後にクラスの属性を宣言できます。

実行結果:

10
20
30

別の記法で同じプログラムを書き直せます。

New point                       # point クラスで新しいオブジェクトを作成
{                               # 新しいオブジェクトの属性、およびメソッドへのアクセス
        x = 10                  # 属性 x へ 10 を設定
        y = 20                  # 属性 y へ 20 を設定
        z = 30                  # 属性 z へ 30 を設定
        print()                 # print メソッドの呼び出し
}                               # オブジェクトのアクセスを終了


Class Point                    # Point クラスの定義
        x y z                   # クラスには属性 x, y および z があります。
        func print             # print メソッドの定義
                see x + nl +   # 属性 x の表示
                y + nl +       # 属性 y の表示
                z + nl         # 属性 z の表示

また、別の方法で同じプログラムを書くことができます。

P1 = New Point
P1.x = 10
P1.y = 20
P1.z = 30
P1.Print()
Class Point x y z func print see x + nl + y + nl + z + nl

注釈

オブジェクト名の直後にドット演算子を用いるとオブジェクトのメンバへアクセスできます。

また、

new point { print() }
Class Point
        x = 10  y = 20  z = 30
        func print see x + nl + y + nl + z + nl

注釈

クラスの属性を宣言する時にクラスの属性へデフォルトの値を設定できます。

あるいは、

new point(10,20,30)
Class Point
        x y z
        func init p1,p2,p3 x=p1 y=p2 z=p3 print()
        func print see x + nl + y + nl + z + nl

注釈

新しいオブジェクトの作成時に () を使うと init メソッドを呼び出せます。

など別の方法で同じプログラムを書くことができます。

new point( [ :x = 10 , :y = 20 , :z = 30 ] )
Class Point x y z
      func init aPara x = aPara[:x] y = aPara[:y] z = aPara[:z] print()
      func print see x + nl + y + nl + z + nl

ちなみに

メソッドの仮引数を渡すためにハッシュを使用する場合は、 オプションの仮引数の作成時、およびハッシュへ追加時は仮引数の順序を変更できます。

括弧を用いたオブジェクトへのアクセス

括弧 { } で、いつでもオブジェクトへアクセスできます。

括弧内ではオブジェクトの属性とメソッドを使えます。

これは New キーワードによるオブジェクトの作成、またはこのシンタックスを必要なときに使えます。

オブジェクト名 { オブジェクトのデータ、およびメソッドへのアクセス }

用例:

See "Creating the Object" + nl
o1 = new Point
See "Using the Object" + nl
o1 {
        x=5
        y=15
        z=25
        print()
}
Class Point x y z func print see x + nl + y + nl + z

括弧は関数、またはメソッドの呼び出し時にオブジェクトへアクセスするために使えます。

用例:

o1 = new Point

print( o1 { x=10 y=20 z=30 } )

func print object
        see object.x + nl +
            object.y + nl +
            object.z

Class Point x y z

括弧とドット演算子を併用して同じ式のオブジェクトへアクセスできます。

用例:

o1 = new Point

O1 { x=10 y=20 z=30 }.print()

Class Point x y z
        func print see x + nl + y + nl + z

コンポジション

オブジェクトでは、ほかのオブジェクトの属性を所有できます。

アクセスしたいオブジェクトを多重括弧にすることで実現できます。

用例:

R1 = New Rectangle
{

        Name = "Rectangle 1"

        P1
        {
                X = 10
                Y = 20
        }

        P2
        {
                X = 200
                Y = 300
        }

        Color = "Blue"

}

see "Name : " + R1.Name + nl +
    "Color: " + R1.Color + nl +
    "P1   : (" + R1.P1.X + "," + R1.P1.Y + ")" + nl +
    "P2   : (" + R1.P2.X + "," + R1.P2.Y + ")"

Class Rectangle
        name  color
        p1 = new Point
        p2 = new Point

Class Point x y

実行結果:

Name : Rectangle 1
Color: Blue
P1   : (10,20)
P2   : (200,300)

Setter と Getter

オブジェクトの属性を設定 (Setter)、または取得 (Getter) 時に用いるメソッドを定義できます。

文法:

Class [クラス名]

        [属性名]
        ...

        Func Set[属性名]
                ...

        Func Get[属性名]
                ...

用例:

o1 = new person

o1.name = "Mahmoud"  see o1.name + nl

o1 { name = "Ahmed"  see name }

Class Person

        name family = "Fayed"

        func setname value
                see "SetName() 関数のメッセージ!" + nl
                name = value + " " + family

        func getname
                see "GetName() 関数のメッセージ!" + nl
                return "Mr. " + name

実行結果:

SetName() 関数のメッセージ!
GetName() 関数のメッセージ!
Mr. Mahmoud Fayed
SetName() 関数のメッセージ!
GetName() 関数のメッセージ!
Mr. Ahmed Fayed

プライベート属性とメソッド

クラスの本体内では private キーワードの後にプライベートな属性とメソッドを定義できます。

用例:

o1 = new person {
        name = "Test"
        age = 20
        print()
        o1.printsalary()
}

try
        see o1.salary
catch
        see cCatchError + nl
done

try
        o1.increasesalary(1000)
catch
        see cCatchError + nl
done

Class Person

        name age

        func print
                see "Name   : " + name + nl +
                    "Age    : " + age + nl

        func printsalary
                see "Salary : " + salary + nl

        private

        salary = 15000

        func increasesalary x
                salary += x

実行結果:

Name   : Test
Age    : 20
Salary : 15000
Error (R27) : Using private attribute from outside the class : salary
Error (R26) : Calling private method from outside the class : increasesalary

演算子のオーバーロード

クラスオブジェクトで演算子を使用可能にするには、クラスへ operator メソッドを追加します。

文法:

Class ClassName

        ...

        Func operator cOperator,Para

                ...

関数の演算子は二種類の仮引数を扱います。一つ目は演算子を、二つ目は演算子の後にある第二仮引数を意味します。

用例:

o1 = new point { x = 10 y = 10 print("P1    : ") }
o2 = new point { x = 20 y = 40 print("P2    : ") }

o3 = o1 + o2
o3.print("P1+P2 : ")

class point x y

        func operator cOperator,Para
                result = new point
                switch cOperator
                on "+"
                        result.x = x + Para.x
                        result.y = y + Para.y
                on "-"
                        result.x = x - Para.x
                        result.y = y - Para.y
                off
                return result

        func print cPoint
                see cPoint + "X : " + x + " Y : " + y + nl

実行結果:

P1    : X : 10 Y : 10
P2    : X : 20 Y : 40
P1+P2 : X : 30 Y : 50

この用例は stdlib.ring にある List クラスからの引用です。

Func operator cOperator,Para
        result = new list
        switch cOperator
                on "+"
                        if isobject(para)
                                for t in Para.vValue
                                        vValue + t
                                next
                        but islist(para)
                                for t in Para
                                        vValue + t
                                next
                        ok
                on "len"
                        return len( vValue )
                on "[]"
                        return &vValue[para]
        off
        return result

“len” 演算子は制御構造 (for in) で使用されます。

“[]” 演算子はリスト項目のアクセスをするときに使用されます。 この場合、 & 演算子は数値による参照で文字列などの項目の値を返すときに使用します。 よって、項目へアクセスするときに項目の更新ができます。

用例(2)

func main

See "----1"+nl
    a1 = new BigNumber( "123" )
    a2 = new BigNumber( "456" )
    a3 = new BigNumber( "789" )
See nl+"----2"+nl
        a1.print()
        a2.print()
        a3.print()
See nl+"----3"+nl
    a2 = a1 + "45"
See nl+"----4"+nl
    a2.print()
See nl+"----5"+nl
    a3 = a1 + a2
See nl+"----6"+nl
    a3.print()
See nl+"----7"+nl

###==================================
Func FuncAdd( num1, num2)
   Sum = 0 + num1 + num2    ### Para.aData isNumber
   Sum = "" +Sum            ### Para.adata isString
return Sum                  ### クラスからの返値
###===================================

class BigNumber

    ### 変数
    aData = "468"

    ### INIT 関数のデフォルト値
    func init aPara
        ? "INIT aPara: " ? aPara
        if isString(aPara)
            aData = aPara
        else
            aData = "" + aPara
        ok

    ### そのほかの関数
    func operator cOperator, Para
            whatType = Type(Para)
            ? nl+"WhatType-PARA: "+ whatType ? Para
            ? nl+"Operator: " ? cOperator  ? nl+"PARA: " ? Para ? "    ______" ? nl
                            if whatType = "STRING"
                               dataInfo = Para
                               ? "dataInfo String: " ? dataInfo
                            but whatType = "NUMBER"
                               datinfo  = "" + Para
                               ? "dataInfo Number: " ? dataInfo
                            else whatType = "OBJECT"
                               dataInfo = "" + para.aData
                               ? "dataInfo OBJECT: " ? dataInfo
                            ok
                          ? "dataInfo USING: " ? dataInfo
            ### Para.aData は初めて渡されたときには存在しません (メンバのオブジェクト)。
            ### "self" が代入されたときの結果は isObject です。
            result = self
            switch cOperator
                on "+"
                     answer = FuncAdd( aData, dataInfo )
                     ? nl+"AnswerString - FunAdd aData, dataInfo: " ? answer
                     ### result = Self はオブジェクトであるため、 aData メンバへオブジェクトを代入します
                     result.aData = answer
                off
            ### Result = Self はオブジェクトです
            return result

    func print
        ? nl+"ClassPrint aData: " ? aData

継承

From キーワードを使用したクラスの定義で別のクラスからクラスを作成できます。

文法:

Class <クラス名> [From <親クラスの名前>]

super オブジェクトで子クラスから親クラスのメソッドを呼び出せます。

文法:

func methodname
        ...
        super.methodname()
        ...

用例:

Func main
        e1 = new Employee {
                Name = "test"
                age = 20
                job = "programmer"
                salary = 20000000
                print()
        }


Class Human
        Name Age
        func print
                see "Name : " + name + nl + "Age  : " + age + nl

Class Employee from Human
        Job Salary
        func print
                super.print()
                see "Job  : " + job + nl + "Salary : " + salary + nl

実行結果:

Name : test
Age  : 20
Job  : programmer
Salary : 20000000

動的属性

クラス名末尾に命令を記述すると、新しいオブジェクトが作成されたときに実行します。

用例:

o1 = new dynamicClass
see o1.var5 + nl        # 5 を出力

Class DynamicClass
        for x = 1 to 10
                cStr = "var" + x + " = " + x
                eval(cStr)
        next

ちなみに

前述の用例では var1, var2, ..., var10 は属性として定義されています。

ちなみに

前述の用例における問題は x および cStr が同じ属性として定義されていることです!

注釈

文字列内にクラスの定義を記述できます。 また、 eval() 関数で文字列を実行するとクラスを定義できます。

パッケージ

このシンタックスでパッケージ (共通の名前によるクラスのグループ) を作成できます。

package PackageName
        Class Class1
                ...
        Class Class2
                ...
        Class Class3
                ...
        ...

用例:

o1 = new System.output.console
o1.print("Hello World")

Package System.Output
        Class Console
                Func Print cText
                        see cText + nl

注釈

パッケージ名にドット演算子を使えます。

パッケージ.クラス名 (PackageName.ClassName) という長い名前を入力する代わりに import 命令を使えます。

パッケージをインポートする場合は、このパッケージを指定のクラスで使えます。

用例:

import system.output
o1 = new console {
        print("Hello World")
}
Package System.Output
        Class Console
                Func Print cText
                        see cText + nl

オブジェクトの表示

see 命令はオブジェクトの状態 (属性と値) を表示します。

用例:

see new point { x=10 y=20 z=30 }
class point x y z

実行結果:

x: 10.000000
y: 20.000000
z: 30.000000

Find() とオブジェクトのリスト

Find() 関数はオブジェクトのリスト内部を検索します。

文法:

Find(List,ItemValue,nColumn,cAttribute) ---> 項目のインデックス

用例:

myList1 = [new Company {position=3 name="Mahmoud" symbol="MHD"},
           new Company {position=2 name="Bert" symbol="BRT"},
           new Company {position=1 name="Ring" symbol="RNG"}
          ]

see find(mylist1,"Bert",1,"name") + nl
see find(mylist1,"Ring",1,"name") + nl
see find(mylist1,"Mahmoud",1,"name") + nl
see find(mylist1,"RNG",1,"symbol") + nl
see find(mylist1,"MHD",1,"symbol") + nl
see find(mylist1,"BRT",1,"symbol") + nl
see find(mylist1,3,1,"position") + nl
see find(mylist1,1,1,"position") + nl
see "Other" + nl
see find(mylist1,"test",1,"name") + nl
see find(mylist1,"test",0,"name") + nl
see find(mylist1,"test",5,"name") + nl

class company position name symbol

実行結果:

2
3
1
3
1
2
1
3
Other
0
0
0

Sort() とオブジェクトのリスト

Sort() 関数はオブジェクトの属性に基づきオブジェクトのリストを整列します。

文法:

Sort(List,nColumn,cAttribute) ---> オブジェクトの属性に基づいて整列されたリスト

用例:

myList1 = [
                new Company {position=3 name="Mahmoud" symbol="MHD"},
                new Company {position=2 name="Bert" symbol="BRT"},
                new Company {position=8 name="Charlie" symbol="CHR"},
                new Company {position=6 name="Easy" symbol="FEAS"},
                new Company {position=7 name="Fox" symbol="EFOX"},
                new Company {position=5 name="Dog" symbol="GDOG"},
                new Company {position=4 name="George" symbol="DGRG"},
                new Company {position=1 name="Ring" symbol="RNG"}
          ]

see sort(mylist1,1,"name")
see copy("*",70) + nl
see sort(mylist1,1,"symbol")
see copy("*",70) + nl
see sort(mylist1,1,"position")

class company position name symbol

実行結果:

position: 2.000000
name: Bert
symbol: BRT
position: 8.000000
name: Charlie
symbol: CHR
position: 5.000000
name: Dog
symbol: GDOG
position: 6.000000
name: Easy
symbol: FEAS
position: 7.000000
name: Fox
symbol: EFOX
position: 4.000000
name: George
symbol: DGRG
position: 3.000000
name: Mahmoud
symbol: MHD
position: 1.000000
name: Ring
symbol: RNG
**********************************************************************
position: 2.000000
name: Bert
symbol: BRT
position: 8.000000
name: Charlie
symbol: CHR
position: 4.000000
name: George
symbol: DGRG
position: 7.000000
name: Fox
symbol: EFOX
position: 6.000000
name: Easy
symbol: FEAS
position: 5.000000
name: Dog
symbol: GDOG
position: 3.000000
name: Mahmoud
symbol: MHD
position: 1.000000
name: Ring
symbol: RNG
**********************************************************************
position: 1.000000
name: Ring
symbol: RNG
position: 2.000000
name: Bert
symbol: BRT
position: 3.000000
name: Mahmoud
symbol: MHD
position: 4.000000
name: George
symbol: DGRG
position: 5.000000
name: Dog
symbol: GDOG
position: 6.000000
name: Easy
symbol: FEAS
position: 7.000000
name: Fox
symbol: EFOX
position: 8.000000
name: Charlie
symbol: CHR

Self.属性 と Self.メソッド() の用法

クラスの範囲内 (クラス名末尾、およびメソッドの前) ならびに、クラスのメソッドは Self.属性 と Self.メソッド() を使えます。

Class Point
        self.x = 10
        self.y = 20
        self.z = 30
        func print
                see self.x + nl + self.y + nl + self.z + nl

注釈

クラスの属性を定義するために、クラスの範囲内で Self.属性 を使うとクラスの属性はグローバル変数との名前衝突から保護されます。

ちなみに

Self.属性 でクラスの属性を定義するときに、同名のグローバル変数が存在する場合は属性の定義は行われずにグローバル変数が使用されます。

グローバル変数名と属性名の間で起こる名前衝突については「スコープの規則」を参照してください。

これは何が起きているのですか?

理由

  • クラスの範囲内ではグローバル変数へアクセスできます。

  • 変数定義前に Ring は変数の検索を行い、見つかれば使用します。

注釈

グローバル変数を避けるために Main 関数の使用、および名前を $ から始めてください。

ちなみに

大規模プログラムでは Self.属性 でクラスの保護、およびメンバの定義してください。

This.属性 と This.メソッド() の用法

クラス内にあるメソッドはオブジェクトのスコープへアクセスできます。 属性の読み書き、またはメソッドの呼び出しでは Self.属性 あるいは Self.メソッド() の使用は不要です。

また、括弧 { } でメソッドの内側から別のオブジェクトへアクセスできます。 この場合は、現在のオブジェクトの有効範囲は括弧内へ変更されます。

括弧内のクラスの属性、およびメソッドへのアクセスする方法はありますか?

これは This.属性 と This.メソッド() で実現できます。

用例:

new point

class point
        x=10 y=20 z=30
        print()
        func print
                new UI {
                        display(this.x,this.y,this.z)
                }

Class UI
        func display x,y,z
                see x + nl + y + nl + z + nl

クラス範囲で This を Self として使用

クラス範囲とはクラス名の後、およびすべてのメソッドの前に出現する範囲のことです。

クラス範囲で This を Self として使えます。

用例:

func main

        o1 = new program {
                test()
        }

        ? o1

class program

        this.name = "My Application"
        this.version = "1.0"
        ? name ? version

        func test
                ? "Name    = " + name
                ? "Version = " + version

実行結果:

My Application
1.0
Name    = My Application
Version = 1.0
name: My Application
version: 1.0

注釈

弓括弧は現在の有効なオブジェクトを変更しますが、This はクラスを指したままにできます。

ちなみに

This と Self には違いがあります。 Self が指している現在の有効なオブジェクトは弓括弧で変更できます。

ほとんどの場合、クラス範囲で This あるいは Self を使う必要はないことを覚えていてください。

このように記述することもできます。

class program name version

または、

class program name="My Application" version="1.0"

注釈

同名で定義されたグローバル変数との衝突を回避するために、クラス範囲で This あるいは Self を使用します。

オブジェクト属性のデフォルト値

オブジェクト属性のデフォルト値は NULL です。

Ring の NULL 値は空文字列、または "NULL" 文字列です。

isNULL() 関数で NULL 値を確認できます。

用例:

oProgram = new Program
? oProgram.name
? oProgram.version
? isNULL(oProgram.name)
? isNULL(oProgram.version)
oProgram { name="My Application" version="1.0" }
? isNULL(oProgram.name)
? isNULL(oProgram.version)
? oProgram

class program
        name
        version

実行結果:

NULL
NULL
1
1
0
0
name: My Application
version: 1.0