オブジェクト指向
多義語過ぎてわけがわからないが自分の解釈をだらだら書く。アランケイやメッセージパッシング、Smalltalk、Simulaには一切触れない。
オブジェクト指向とは foo.bar() のような(特殊な)関数呼び出しの記法を実現するやり方である。まず、なぜこの記法が多くの言語で使えるのか、つまり、この記法のメリットを説明する。
一般的な関数呼び出しは bar(foo) のようなものだ。そしてfoo.bar()を並べると違いが目につく。
fooとbarの順番が逆
fooとbar、つまり操作と操作対象がドットで繋がれている
関数呼び出しを連鎖させるとメソッド記法の優位性が明らかになる。
code:chain.js
f(g(h(x)));
x.h().g().f();
関数呼び出しが括弧のネスト、呼び出される順番の逆転によって見づらいコードになるなか、メソッド記法はフラットを保ったまま整然と順番通りに並ぶ。この美しさが人々をオブジェクト指向に駆り立てた。世はまさに大OOP時代。
関数適用の順序を逆にするという目的を達成できる言語機能を2つ紹介しておく。
UFCSは.を使う。詳しくないので省略。
パイプライン演算子は(一般的には)|>を使い、左辺の値を右辺の関数に突っ込む。ここで重要なのは右辺の関数には任意の式を書けるいうことだ。
code:fs
let add2 n = n + 2
// スコープにある変数を使っている
40 |> add2
let add x y = x + y
// その場で部分適用した関数を使っている
40 |> add 2
// 無名関数
40 |> \x -> x + 2
このように関数が任意に書けることは、部分適用など関数に評価される式を自由に組み合わせできるということだ。これは自由度が高い一方で、右辺に来うる値の候補が多すぎて補完が難しいという難点を抱えている。
クラスベースOOP
オブジェクト指向言語の殆どは クラス を搭載している。「F#を知ってほしい」の表現があまりに秀逸だったので紹介しておくと、クラスとは「型に密結合したモジュール」である。クラスを定義すると新しい型が生まれる。そしてクラス定義の中に、その型を使った関数(インスタンスメソッド)が定義されている。 JavaScriptとオブジェクト指向
JavaScriptはオブジェクト指向言語の中でも特殊な気がする。
JavaScriptにおいてオブジェクトとはラベルのついた直積型である。動的型付けという性質もあって、事前に型として宣言する必要のあるRustの構造体などと異なり、JavaScriptのオブジェクトはオブジェクトリテラルという形でアドホックに自由なプロパティを持ったオブジェクトを生成することができる。 code:obj.js
const obj = {
name: "John",
age: 14,
};
JavaScriptは第一級関数をもつ言語であり、オブジェクトのプロパティに関数を持たせることができる。
code:o.js
const o = {
foo: 0,
printFoo: function() { // プロパティに関数式を割り当て
// この関数の中ではthisはoを指す
console.log(this.foo);
},
};
つまり o.printFoo というものが関数であり、その後ろにカッコをつけて呼び出している。(ちゃんと構文規則を調べていないので、嘘かもしれない。というかthisのバインドから考えると絶対嘘)
同じようなプロパティをもつオブジェクトを複数作りたくなることがあるだろう。そのような場合にはクラスを使うことができる。
code:class.js
class Klass {
printFoo() {
console.log(this.foo);
}
constructor(foo) {
this.foo = foo;
}
}
const a = new Klass(1);
const b = new Klass(3);
a.printFoo();
b.printFoo();
ところで、{}のような空オブジェクトでも toString のようなメソッドはよぶことができる。
code:proto.js
const o = {};
このtoStringはプロトタイプから供給されている。プロトタイプは何らかのオブジェクトであり、o.toString() は実際には o のプロトタイプの toString() を呼んでいる。オブジェクトリテラルのプロトタイプは Object.prototype と同じである。
code:op.js
const o = {};
Object.getPrototypeOf(o) === Object.prototype; // true
カプセル化?
カプセル化はいいことであり、オブジェクト指向でも実現できるが、しかしオブジェクト指向だけのものでもない。 クラスベースオブジェクト指向では状態(プロパティ/メンバ変数)と、それに対する操作(メソッド)を同時に定義できる。
code:kt
class Klass {
var count = 0
fun increment() {
this.count++
}
fun printCount() {
println (this.count)
}
}
fun main () {
val k = Klass()
k.printCount() // 0
k.increment()
k.printCount() // 1
}