技術書典8は中止になってしまいましたが、オンラインで開催される技術書典 応援祭にgolang.tokyoも参加します。
私は、今回の新刊である「Gopherの休日2020冬」に「GoにおけるSOLID原則」という内容で寄稿しました。
また、その冒頭部分も公開します。気になる方はどうぞ次のリンク先よりご購入ください。
golang.tokyo 技術書典 応援祭 新刊「Gopherの休日2020冬」について
今回の新刊は私以外にも6名以上の方々が執筆しており、以下のような内容になっています(敬称略)。
- GoにおけるSOLIDの原則 / @budougumi0617
- Go本体にコントリビュートする方法 / @hajimehoshi
- TUIツールを作ろう / @gorilla0513
- GoとコンセンサスアルゴリズムRaftによる分散システム構築入門 / @po3rin
- Goにおける初期化処理 / @kaneshin0120
- text/template の linter を作ろう / @knsh14
- Goによるコンテナランタイム自作入門 /
@_moricho_
- and more…
今回もすべての内容が技術書典 応援祭のための書き下ろしになっています。
Go本体の解説やコントリビューション方法、分散システム、linter・TUI、コンテナランタイムの自作まで多種多様です。
ページ数は現時点で計150ページ以上になっており、販売価格1,000円となります。 (なお、売上はすべてgolang.tokyoのノベルティ作成などに使われます)
また、前回同様、@tottie_designerさんに表紙・裏表紙を作成していただきました。
寄稿内容について
私はオブジェクト指向設計の重要な原則であるSOLIDの原則を話題に「GoにおけるSOLID原則」という章を書きました。
抽象クラス・具象クラスなどが存在しないGoにおいてどのようにSOLIDの原則の思想を生かしていくか、を(自分なりに解釈して)まとめています。
以下、試し読み代わりに冒頭を転載します(脚注などはブログ用に編集)。
内容に興味がでた方はぜひ応援祭でご購入ください。冒頭に記載したとおり、この他にも様々なテーマで寄稿されています。
GoにおけるSOLIDの原則
@budougumi0617
です。
オブジェクト指向設計の原則を5つまとめたSOLIDの原則(the SOLID principles
)という設計指針があることはみなさんご存じでしょう。
本章ではSOLIDの原則にのっとったGo
の実装について考えます。
SOLIDの原則
(the SOLID principles
)とは
SOLIDの原則は次の用語リストに挙げた5つのソフトウェア設計の原則の頭文字をまとめたものです(アジャイルソフトウェア開発の奥義 第2版より引用)。
単一責任の原則
(SRP
,Single responsibility principle
)- クラスを変更する理由は1つ以上存在してはならない。
オープン・クローズドの原則
(OCP
,Open–closed principle
)- ソフトウェアの構成要素構成要素(クラス、モジュール、関数など)は拡張に対して開いて(オープン: Open)いて、修正に対して閉じて(クローズド: Closed)いなければならない。
リスコフの置換原則
(LSP
,Liskov substitution principle
)S
型のオブジェクトo1
の各々に、対応するT
型のオブジェクトo2
が1つ存在し、T
を使って定義されたプログラムP
に対してo2
の代わりにo1
を使ってもP
の振る舞いが変わらない場合、S
はT
の派生型であると言える。
- インターフェイス分離の原則(
ISP
,Interface segregation principle
)- クライアントに、クライアントが利用しないメソッドへの依存を強制してはならない。
依存関係逆転の原則
(DIP
,Dependency inversion principle
)- 上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも「抽象」に依存すべきである。「抽象」は実装の詳細に依存してはならない。実装の詳細が「抽象」に依存すべきである。
これらの原則は、ソフトウェアをより理解しやすく、より柔軟でメンテナンス性の高いものにする目的で考案されました。
各々の原則の原案者や発表時期は異なりますが、この5つの原則から頭文字のアルファベットを1つずつ取りSOLIDの原則
としてまとめたのがRobert C. Martin
氏です。
- Getting a SOLID start.
メーリングリストに投稿されたRobert C. Martin
氏のコメントを見ると、1995年時点でオープンクローズドの原則(The open/closed principle
)などの命名がされていることがわかります。
- The Ten Commandments of OO Programming
また、SOLIDの原則として5つの原則がまとめて掲載された日本語書籍は、「アジャイルソフトウェア開発の奥義」になります。
SOLIDの原則の成り立ち自体についてもっと知りたい場合は、@nazonohito51
さんの「「SOLIDの原則って何ですか?」って質問に答えたかった
」を読んでみるとよいでしょう。
- 「SOLIDの原則って何ですか?」って質問に答えたかった
Goとオブジェクト指向プログラミング
本題へ入る前に、Go
とオブジェクト指向プログラミングの関係を考えてみます。
そもそもオブジェクト指向に準拠したプログラミング言語であることの条件とは何でしょうか。
さまざまな主張はありますが、本章では次の3大要素を備えることが「オブジェクト指向に準拠したプログラミング言語であること」とします。
- カプセル化(
Encapsulation
) - 多態性(ポリモフィズム)(
Polymorphism
) - 継承(
Inheritance
)
ではGo
はオブジェクト指向言語なのでしょうか。Go
公式サイトにはFrequently Asked Questions (FAQ)
という「よくある質問と答え」ページがあります。
この中のIs Go an object-oriented language?
(Go
はオブジェクト指向言語ですか?)という質問に対する答えとして、次の公式見解が記載されています。
- Is Go an object-oriented language? | Frequently Asked Questions (FAQ)
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.
あいまいな回答にはなっていますが、「Yesであり、Noでもある。」という回答です。
Go
はオブジェクト指向の3大要素を一部しか取り入れていないため、このような回答になっています。
Go
はサブクラシング(subclassing
)に対応していない
多くの方がオブジェクト指向言語に期待する仕組みの1つとして、先ほど引用した回答内にもあるサブクラシング
(subclassing
)が挙げられるでしょう。
もっと平易な言葉で言い直すと、クラス(型)の階層構造(親子関係)による継承
です。
代表的なオブジェクト指向言語であるJava
でサブクラシングの例を書いたコードが次のコードです。
このコードは、親となるPerson
クラスと子となるJapanese
クラスの定義と、Person
クラスを引数に取るメソッドを含んでいます。
class Person {
String name;
int age;
}
// Personクラスを継承したJapaneseクラス
class Japanese extends Person {
int myNumber;
}
class Main {
// Personクラスを引数にとるメソッド
public static void Hello(Person p) {
System.out.println("Hello " + p.name);
}
}
Person
クラスを継承したJapanese
クラスのオブジェクトは、ポリモフィズムによってPerson
変数に代入できます。
また、同様にPerson
クラスのオブジェクトを引数にとるメソッドに対して代入することもできます。
Japanese japanese = new Japanese();
person.name = "budougumi0617";
Person person = japanese;
Main.Hello(japanese);
このようなポリモフィズムを目的とした継承関係を表現するとき、Go
ではインターフェースを使うでしょう。
しかし、Go
は上記のような具象クラス(あるいは抽象クラス)を親とするようなサブクラシングによる継承の仕組みを言語仕様としてサポートしていません。
Go
では埋込み(Embedding
)を使って別の型に実装を埋め込むアプローチもあります。
- Embedding | Effective Go
しかし、これは多態性や共変性・反変性を満たしません。
- 共変性と反変性 (C#)
よって、埋め込みはオブジェクト指向で期待される継承ではなくコンポジションにすぎません。
次のコードは前述JavaのコードをGo
で書き直したものです。
Go
の例では、Japanese
型のオブジェクトはHello
関数に利用することはできません。
type Person struct {
Name string
Age int
}
// Personを埋め込んだJapanese型。
type Japanese struct {
Person
MyNumber int
}
func Hello(p Person) {
fmt.Println("Hello " + p.Name)
}
以上の例以外にも、リスコフの置換原則
などの一部のSOLIDの原則はそのままGo
に適用することはできません。
しかし、SOLIDの原則のベースとなる考えを取り入れることでよりシンプルで可用性の高いGo
のコードを書くことは可能です。
それでは、次節よりGo
のコードにSOLIDの原則を適用していくとどうなっていくか見ていきます。
なお、Go
とSOLIDの原則については、Dave Cheney
氏も2016年にGolangUK
でSOLID Go Design
というタイトルで発表されています。
- SOLID Go Design
[コラム]実装よりもコンポジションを選ぶ
あるクラスが他の具象クラス(抽象クラス)を拡張した場合の継承を、Java
の世界では実装継承
(implemebtation inheritance
)と呼びます。
あるクラスがインターフェースを実装した場合や、インターフェースが他のインターフェースを拡張した場合の継承をインターフェース継承
(interface inheritance
)と呼びます。
Go
がサポートしている継承はJava
の言い方を借りるならばインターフェース継承
のみです。
クラスの親子関係による実装継承
はカプセル化を破壊する危険も大きく、深い継承構造はクラスの構成把握を困難にするという欠点もあります。
このことは代表的なオブジェクト指向言語であるJava
の名著、「Effective Java
」の「項目16 継承よりコンポジションを選ぶ
」でも言及されています(筆者が所有しているEffective Java
は第2版ですが、2018年にJava 9
対応のEffective Java 第3版
が発売されています)。
Go
が実装継承
をサポートしなかった理由は明らかになっていませんが、筆者は以上の危険性があるためサポートされていないと考えています。
転載は以上です。頒布物ではこの後個別の原則をGoでどう活かすかを20ページ弱にまとめています。
おわりに
まだ私自身応援祭の具体的な形式がわかっておりませんが、ぜひご購入ください。 私自身日々学びながら設計・実装しているので、読後「これはこうじゃない?」みたいな感想などもいただけると幸いです。
2020/02/28 21:00 追記
応援際の詳細が公開されました。
「#技術書典 応援祭」を3月7日から約1ヶ月間に渡って開催します!あり得ない短期間で工数をぶち込み、無理をおしてなんとか体制を整えました。すべては皆さんが一生懸命書いた技術書を、楽しみに待つ読者の元へ届けるためです。温かい気持ちで参加いただけたらうれしいです。https://t.co/yBgQxT97Q6
— 技術書典公式アカウント (@techbookfest) February 28, 2020
2020/03/10 追記
販売ページが公開されています。
技術書典 応援祭(技術書典8)用に執筆された新刊です。
技術書典7で頒布した既刊も販売しています。