コントラクトの継承と連携(他のコントラクトのメソッド実行)
どうも、ぺろりんです。
「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」をテキストとして使った勉強のつづきです。(前回の記事はこちら)
実を言うと今回はChapter 4の「会員管理」(これは次の記事へ)をやりたかったんですが、以前サラッと流し読みしたところがソースコードに出てきて詰まりましたw
というわけで、今回はChapter 3の「コントラクトの継承」と「他のコントラクトのメソッドを実行」というセクションに戻って理解を深めようと思います。
ここで私が理解したいのは「(1つのソースコードに)2つ以上のコントラクトが書いてあるときの動き」です。
これを「コントラクトの継承」と、「他のコントラクトのメソッドを実行」という2つの切り口から調べていきます。(テキストを参照しつつ)
前提知識
お恥ずかしながらオブジェクト指向言語の理解が浅くて、まず単語でつまずきましたw
なので、このへんからちゃんと理解しておこうかと思います。
「クラス」、「インスタンス」とは?
「クラス」は“鋳型”とか“設計図”にあたるもので、これをもとに作られた“実体”が「インスタンス」です。
「メソッド」とは?
実質的には関数だったりしますが、関数とは別の概念です。
オブジェクト指向では
・属性(どんなもの?)…a,b,cという性質を持つ
・操作(何ができる?)…X,Yという動作ができる
をひとまとめにした(a,b,c,X,Y)を1つの「オブジェクト」と考えます。
オブジェクトができる「操作」のことを「メソッド」と呼びます。
ちなみに、
と書くことで、〈オブジェクト〉内で定義された属性や操作を呼び出せます。
「継承」、「オーバーライド」とは?
オブジェクト指向では、親子関係をつけたクラスを定義することができます。
何がうれしいかと言うと、たとえば2つのクラスAとBを定義するときに、AとBには共通の性質Pがあったとします。
「性質Pを持った親クラスX」を作ってAとBをXの子クラスとすれば、子クラスは親クラスの性質を引き継ぐため、AとBの中で共通の性質Pを定義する必要がなくなります。
この「子クラスは親クラスの性質を引き継ぐ」性質を「継承」と呼びます。
子クラスの中で、継承した親クラスの変数などを上書きするのが「オーバーライド」です。
ここまでがオブジェクト指向についての前提知識です。
このへんの理解をちゃんとしておかないと分からない!(私だけか……?w)
「コントラクト」、「デプロイ」とは?
オブジェクト指向の言葉はすこし分かった気がしますが、今度はイーサリアムの言葉の理解も浅いことに気付きましたw
まだいまいち「コントラクト」と「デプロイ」のイメージがつかめてなかったので、これらも調べてみました。
ここまでオブジェクト指向の言葉を復習しておくと、以下のような理解ができるようです。
(ブロックチェーンEthereum入門 3(NTTデータ先端技術株式会社)より)
(参考)
・“たい焼き”であま~く理解するJava文法と言語仕様 (1/2)(@IT)
・【PHP超入門】クラス~例外処理~PDOの基礎(Qiita)
・Javaでインスタンスを使う方法【初心者向け】(TECHACADEMYmagazine)
・メソッド (method)とは(「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典)
・ブロックチェーンEthereum入門 3(NTTデータ先端技術株式会社)
コントラクトの継承
さて、ここまでの前提知識をもとに、「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」へ戻ってきます。
コンパイラのバージョンをソースに合わせつつ、サンプルコードをコンパイルします。


このサンプルコードの概要としては以下のようなものです。
・1つのソースコードの中に
①コントラクトA
②AのサブコントラクトB
③コントラクトC
という3つのコントラクトが書いてある。
・コントラクトCの中で、AとBをデプロイしている(「new」コマンドを使うことでデプロイできる)。
このサンプルコードで、コントラクトの継承について動きを見ていきます。
初心者の私としては、はじめは素朴に「上に書いてるAから順にSolidityでデプロイしていくべきなのかな?」と考えました。
しかしよく考えてみると、コントラクトCの中でAとBのインスタンスを作っている(デプロイしている)ので、コントラクトCだけデプロイすれば事足りるのでは?
……と思って、コントラクトCのみデプロイすることを試してみました。
Cだけ選んで、EnvironmentをJavaScript VMにしつつデプロイしてみます。


できたのはCだけです。

この状態で「c」と「getData」を押してみても初期状態という感じです。


ソースコードの中で、コントラクトAとBのインスタンスを作っている(中でnewしている)関数「makeContract」を押してみます。
この後ふただび「c」と「getData」を押してみると、値を拾っています。

今度は、はじめに考えていた通り、A、B、Cの順にすべてデプロイしてみました。


この状態で、青いボタンだけ押してみます。
特に何も値は入っていません。


ちなみにここで、BはAを継承しているため、Bの中では定義されていない「setA」と「a」のボタンがBのところにもあります。
ためしに、Aのところで
a=15
と設定して「setA」を実行してみます。

この直後にA、B、Cで青いボタンを押してみます。
すると、Aの「a」と「getData」の値のみ変わりました。


次に、Bのところで
a=20
と設定して「setA」を実行してみます。

この直後にもA、B、Cで青いボタンを押してみると、今度はBの「a」と「getData」の値のみ変わりました。


Aの中に定義された「a」をBの中でも定義していますが、これは別に、BがAの中で定義した「a」の値を参照する動きにはなっていないようです。
この時点でCの「makeContract」をするとどうなるでしょうか?
これを実行しつつ、Cの中の「c」と「getData」を押してみます。

Cの中ではあくまで
a=1
としてAとBのインスタンスを作成して計算しているので、先ほどAやBの中で設定した「a」とは関係なくCの中で計算されていることがわかります。
ちなみに、Cで「makeContract」した後も、AとBの中にある「a」と「getData」は変わっていませんでした。

どうやら、以下のようなことが言えるようです。
・AとBのインスタンスを中で作っているCだけデプロイすれば、AとBをデプロイしなくてもCは結果を出せる。
・A、B、Cをそれぞれデプロイすると、それぞれは独立に動く(設定した「a」の値は、あくまで各A、B、C内でのみ使われる)。
ちなみに、「new」コマンドを消して、Cの中でインスタンスを作らずAやBを呼んでみる……みたいなことを試したかったのですが、そもそもコンパイルできるコードにできず断念しましたw
他のコントラクトのメソッドを実行
次に、他のコントラクトのメソッドを実行するサンプルコードをコンパイルしてみます。

コードの概要としては、以下のようなものです。
・1つのコードに
①コントラクトA
②コントラクトB
の2つが書いてある。
・コントラクトBの中で「コントラクトA」や「Aの中で定義した変数」を呼ぶけど、Aの“インスタンス化”(newコマンド)はやらない。
先ほどの「継承」ではコントラクトCの中でAとBをインスタンス化していたので、CだけデプロイすればAやBの中にあるデータを拾ってきました。
しかし今回はコントラクトBの中でAのインスタンス化はしていないので、おそらくBだけデプロイしても「Bで呼んでいる、Aの中で定義した変数」の値は拾えないでしょう……という想定のもと、まずはBだけデプロイしてみます。


ためしにBだけデプロイした状態で青いボタンだけ押してみると、どれも初期状態です。
(というかコントラクトAをデプロイしていないので、そもそもコントラクトAのアドレスを入れなさいという場所に何も入れられない……)

次に、Aもデプロイしてみます。


さて、Aのアドレスを入れて「setA」を押してから、Bの青いボタンを押してみます。


めでたく値を拾うことができました。
コントラクトAをデプロイしたときのアドレスを使うことでAとBが紐づいて、BからAの中で定義された変数の値を呼び出すことができました。
コントラクトBで定義されているsetA(A _a)の中では、setA(A _a)の引数として与えられたAのアドレス_aを
というようにセットします。
aNum()はa.num()を返すことで(numはコントラクトAの中で定義された状態変数)、Aで定義されたnumの値を返します。
とすることで〈オブジェクト〉内で定義された変数や関数が呼び出せることを思い出すと、「a = A(_a)」は「オブジェクト」になっているようです。
つまり、コントラクト名の引数にそのコントラクトのデプロイアドレスを指定したら「オブジェクト」化(もしくはインスタンス化)されるようです。
(参考)
・インスタンスの考え方・クラス型変数・コンストラクタ・静的メンバ(Qiita)
・【基本の基本】Javaのデータ型とは?と簡単な使い方まとめ(エンジニアの入り口)
・Ethereum 外部コントラクトの呼び出し方法(Remix, MetaMask連携)(Qiita)
まとめ
オブジェクト指向言語に慣れている方だとそんなに困らないのかも知れませんが、私は慣れておらず詰まりながらいくつか試してみましたw
オブジェクト指向言語の本も手元に置いておく方が良さそうだなぁ。。
今回触ってみて、個人的には以下のような理解が得られました。
・1つのコード内に複数のコントラクトが含まれるとき、
①あるコントラクトXの中で別のコントラクトYをデプロイしているならば、XをデプロイすればYのデプロイは不要。
②それぞれのコントラクトを別々にデプロイすると、各々が独立して動く。
・コントラクトZの引数にそのコントラクトZのデプロイアドレスWを指定したZ(W)は「オブジェクト」化(インスタンス化)される。
デプロイアドレスがJavaなどで何にあたるのか分からないのですが、デプロイアドレスを引数に指定するとオブジェクト化するというのが分かったような分からないような……w
次ははりきって「会員管理」のところをやりますw
では今回はこのへんで~(´・ω・`)
「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」をテキストとして使った勉強のつづきです。(前回の記事はこちら)
実を言うと今回はChapter 4の「会員管理」(これは次の記事へ)をやりたかったんですが、以前サラッと流し読みしたところがソースコードに出てきて詰まりましたw
というわけで、今回はChapter 3の「コントラクトの継承」と「他のコントラクトのメソッドを実行」というセクションに戻って理解を深めようと思います。
ここで私が理解したいのは「(1つのソースコードに)2つ以上のコントラクトが書いてあるときの動き」です。
これを「コントラクトの継承」と、「他のコントラクトのメソッドを実行」という2つの切り口から調べていきます。(テキストを参照しつつ)
前提知識
お恥ずかしながらオブジェクト指向言語の理解が浅くて、まず単語でつまずきましたw
なので、このへんからちゃんと理解しておこうかと思います。
「クラス」、「インスタンス」とは?
「クラス」は“鋳型”とか“設計図”にあたるもので、これをもとに作られた“実体”が「インスタンス」です。
「メソッド」とは?
実質的には関数だったりしますが、関数とは別の概念です。
オブジェクト指向では
・属性(どんなもの?)…a,b,cという性質を持つ
・操作(何ができる?)…X,Yという動作ができる
をひとまとめにした(a,b,c,X,Y)を1つの「オブジェクト」と考えます。
オブジェクトができる「操作」のことを「メソッド」と呼びます。
ちなみに、
〈オブジェクト〉.〈オブジェクト内の変数や関数名〉
と書くことで、〈オブジェクト〉内で定義された属性や操作を呼び出せます。
「継承」、「オーバーライド」とは?
オブジェクト指向では、親子関係をつけたクラスを定義することができます。
何がうれしいかと言うと、たとえば2つのクラスAとBを定義するときに、AとBには共通の性質Pがあったとします。
「性質Pを持った親クラスX」を作ってAとBをXの子クラスとすれば、子クラスは親クラスの性質を引き継ぐため、AとBの中で共通の性質Pを定義する必要がなくなります。
この「子クラスは親クラスの性質を引き継ぐ」性質を「継承」と呼びます。
子クラスの中で、継承した親クラスの変数などを上書きするのが「オーバーライド」です。
ここまでがオブジェクト指向についての前提知識です。
このへんの理解をちゃんとしておかないと分からない!(私だけか……?w)
「コントラクト」、「デプロイ」とは?
オブジェクト指向の言葉はすこし分かった気がしますが、今度はイーサリアムの言葉の理解も浅いことに気付きましたw
まだいまいち「コントラクト」と「デプロイ」のイメージがつかめてなかったので、これらも調べてみました。
ここまでオブジェクト指向の言葉を復習しておくと、以下のような理解ができるようです。
(ブロックチェーンEthereum入門 3(NTTデータ先端技術株式会社)より)
オブジェクト指向言語との対比ではコントラクトのソースコードはクラスであり、デプロイはインスタンス化すること
(参考)
・“たい焼き”であま~く理解するJava文法と言語仕様 (1/2)(@IT)
・【PHP超入門】クラス~例外処理~PDOの基礎(Qiita)
・Javaでインスタンスを使う方法【初心者向け】(TECHACADEMYmagazine)
・メソッド (method)とは(「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典)
・ブロックチェーンEthereum入門 3(NTTデータ先端技術株式会社)
コントラクトの継承
さて、ここまでの前提知識をもとに、「はじめてのブロックチェーン・アプリケーション Ethereumによるスマートコントラクト開発入門 (DEV Engineer's Books)」へ戻ってきます。
コンパイラのバージョンをソースに合わせつつ、サンプルコードをコンパイルします。


このサンプルコードの概要としては以下のようなものです。
・1つのソースコードの中に
①コントラクトA
②AのサブコントラクトB
③コントラクトC
という3つのコントラクトが書いてある。
・コントラクトCの中で、AとBをデプロイしている(「new」コマンドを使うことでデプロイできる)。
このサンプルコードで、コントラクトの継承について動きを見ていきます。
初心者の私としては、はじめは素朴に「上に書いてるAから順にSolidityでデプロイしていくべきなのかな?」と考えました。
しかしよく考えてみると、コントラクトCの中でAとBのインスタンスを作っている(デプロイしている)ので、コントラクトCだけデプロイすれば事足りるのでは?
……と思って、コントラクトCのみデプロイすることを試してみました。
Cだけ選んで、EnvironmentをJavaScript VMにしつつデプロイしてみます。


できたのはCだけです。

この状態で「c」と「getData」を押してみても初期状態という感じです。


ソースコードの中で、コントラクトAとBのインスタンスを作っている(中でnewしている)関数「makeContract」を押してみます。
この後ふただび「c」と「getData」を押してみると、値を拾っています。

今度は、はじめに考えていた通り、A、B、Cの順にすべてデプロイしてみました。


この状態で、青いボタンだけ押してみます。
特に何も値は入っていません。


ちなみにここで、BはAを継承しているため、Bの中では定義されていない「setA」と「a」のボタンがBのところにもあります。
ためしに、Aのところで
a=15
と設定して「setA」を実行してみます。

この直後にA、B、Cで青いボタンを押してみます。
すると、Aの「a」と「getData」の値のみ変わりました。


次に、Bのところで
a=20
と設定して「setA」を実行してみます。

この直後にもA、B、Cで青いボタンを押してみると、今度はBの「a」と「getData」の値のみ変わりました。


Aの中に定義された「a」をBの中でも定義していますが、これは別に、BがAの中で定義した「a」の値を参照する動きにはなっていないようです。
この時点でCの「makeContract」をするとどうなるでしょうか?
これを実行しつつ、Cの中の「c」と「getData」を押してみます。

Cの中ではあくまで
a=1
としてAとBのインスタンスを作成して計算しているので、先ほどAやBの中で設定した「a」とは関係なくCの中で計算されていることがわかります。
ちなみに、Cで「makeContract」した後も、AとBの中にある「a」と「getData」は変わっていませんでした。

どうやら、以下のようなことが言えるようです。
・AとBのインスタンスを中で作っているCだけデプロイすれば、AとBをデプロイしなくてもCは結果を出せる。
・A、B、Cをそれぞれデプロイすると、それぞれは独立に動く(設定した「a」の値は、あくまで各A、B、C内でのみ使われる)。
ちなみに、「new」コマンドを消して、Cの中でインスタンスを作らずAやBを呼んでみる……みたいなことを試したかったのですが、そもそもコンパイルできるコードにできず断念しましたw
他のコントラクトのメソッドを実行
次に、他のコントラクトのメソッドを実行するサンプルコードをコンパイルしてみます。

コードの概要としては、以下のようなものです。
・1つのコードに
①コントラクトA
②コントラクトB
の2つが書いてある。
・コントラクトBの中で「コントラクトA」や「Aの中で定義した変数」を呼ぶけど、Aの“インスタンス化”(newコマンド)はやらない。
先ほどの「継承」ではコントラクトCの中でAとBをインスタンス化していたので、CだけデプロイすればAやBの中にあるデータを拾ってきました。
しかし今回はコントラクトBの中でAのインスタンス化はしていないので、おそらくBだけデプロイしても「Bで呼んでいる、Aの中で定義した変数」の値は拾えないでしょう……という想定のもと、まずはBだけデプロイしてみます。


ためしにBだけデプロイした状態で青いボタンだけ押してみると、どれも初期状態です。
(というかコントラクトAをデプロイしていないので、そもそもコントラクトAのアドレスを入れなさいという場所に何も入れられない……)

次に、Aもデプロイしてみます。


さて、Aのアドレスを入れて「setA」を押してから、Bの青いボタンを押してみます。


めでたく値を拾うことができました。
コントラクトAをデプロイしたときのアドレスを使うことでAとBが紐づいて、BからAの中で定義された変数の値を呼び出すことができました。
コントラクトBで定義されているsetA(A _a)の中では、setA(A _a)の引数として与えられたAのアドレス_aを
a = A(_a)
というようにセットします。
aNum()はa.num()を返すことで(numはコントラクトAの中で定義された状態変数)、Aで定義されたnumの値を返します。
〈オブジェクト〉.〈オブジェクト内の変数や関数名〉
とすることで〈オブジェクト〉内で定義された変数や関数が呼び出せることを思い出すと、「a = A(_a)」は「オブジェクト」になっているようです。
つまり、コントラクト名の引数にそのコントラクトのデプロイアドレスを指定したら「オブジェクト」化(もしくはインスタンス化)されるようです。
(参考)
・インスタンスの考え方・クラス型変数・コンストラクタ・静的メンバ(Qiita)
・【基本の基本】Javaのデータ型とは?と簡単な使い方まとめ(エンジニアの入り口)
・Ethereum 外部コントラクトの呼び出し方法(Remix, MetaMask連携)(Qiita)
まとめ
オブジェクト指向言語に慣れている方だとそんなに困らないのかも知れませんが、私は慣れておらず詰まりながらいくつか試してみましたw
オブジェクト指向言語の本も手元に置いておく方が良さそうだなぁ。。
今回触ってみて、個人的には以下のような理解が得られました。
・1つのコード内に複数のコントラクトが含まれるとき、
①あるコントラクトXの中で別のコントラクトYをデプロイしているならば、XをデプロイすればYのデプロイは不要。
②それぞれのコントラクトを別々にデプロイすると、各々が独立して動く。
・コントラクトZの引数にそのコントラクトZのデプロイアドレスWを指定したZ(W)は「オブジェクト」化(インスタンス化)される。
デプロイアドレスがJavaなどで何にあたるのか分からないのですが、デプロイアドレスを引数に指定するとオブジェクト化するというのが分かったような分からないような……w
次ははりきって「会員管理」のところをやりますw
では今回はこのへんで~(´・ω・`)