オブジェクト指向プログラミング (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