Hi, I am Dm-On

K. Onda

Software Engineer at Example Co.

I am a passionate software engineer with x years of working experience. I built OSS tools for Kubernetes using GO. My tools help people to deploy their workloads in Kubernetes. Sometimes, I work on some fun projects such as writing a theme, etc.

Leadership
Team Work

Skills

Experiences

1
Senior Software Engineer
Example Co.

Nov 2019 - Present, Dhaka Branch

Example Co. is a widely recognized company for cloud-native development. It builds tools for Kubernetes.

Responsibilities:
  • Design and develop XYZ tool for ABC task
  • Design, develop and manage disaster recovery tool Xtool that backup Kubernetes volumes, databases, and cluster’s resource definition.
  • Lead backend team.

Software Engineer
PreExample Co.

March 2016 - May 2017, Nowhere

PreExample Co. is a gateway company to enter into Example co. So, nothing special here.

Responsibilities:
  • Write lots of example codes.
  • Read lots of examples.
  • See lots of example videos.
2

Projects

Kubernetes
Kubernetes
Contributor March 2018 - Present

Production-Grade Container Scheduling and Management.

Star
Tensorflow
Tensorflow
Developer Jun 2018 - Present

An Open Source Machine Learning Framework for Everyone.

Star
Toha
Toha
Owner Jun 2019 - Present

A Hugo theme for personal portfolio.

Star

Recent Posts

Card image cap
Solid原則 - OCP

Open Closed Principle (開放閉鎖原則)  “クラスは拡張に対して開いていなければならず 修正に対して閉じていなければならない” 「このマシンは CPUもメモリもディスクも拡張可能にできているのさ。 それに変更がケースの内側だけなら、ユーザーや外部デバイスも気づかないだろ?」 OCPは クラスの拡張性を最大化し、デグレードのリスクを最小化するための原則です。修正が外部に影響しないようにクラス外との窓口を限定し(Close)、修正自体を阻害しないようにクラス内の拡張ポイントを意識して設計します(Open)。 概要 冒頭の例では、マシン(コンピュータ)を引き合いに出して説明しました。マシンは内部に多くの拡張ポイントを用意している一方で、外部との接触はPCケースによって限定され一部のボタンや端子からしかやり取りできません。 例えば、マシンのケースを開ければ HDDやメモリのスロットによりパーツの追加・交換を簡単に行うことができます。これが「拡張に対して開かれている」部分です。逆に、専用メモリが基盤に埋め込まれており追加・交換ができなければ「拡張に対して開かれていない」ことになります。 一方で、マシンのケース自体は外部との接触を限定する役割を持ちます。ケースはマザーボード等の脆弱な内部基盤を隠蔽し、最小限のインターフェイスを外部に公開します(カプセル化)。具体的には「電源ボタン・ディスクドライブ・USB端子・電源ソケット」という最小限の窓口のみ露出され残りはケースで覆われます。露出部分の挙動さえ担保できれば、それを利用する側に修正の影響は及びません。これが「修正に対して閉じられている」部分です。修正に対して閉じられていれば、デグレードの危険性や再テストの必要性を最小化できます。 修正に対して閉じられている構造は、何もマシンに限った構造ではありません。自動車や船舶等の機械製品はもちろんのこと、人間や動物の体だって修正に対しては閉じられています。ですから運転手が直接エンジンを触って始動することは不可能ですし、動物が食べ物を口を介さずに胃に押し込めることも不可能です。イグニッションキーによる始動や口で咀嚼してからの嚥下で問題ありませんし、むしろその方が便利で安全にできています。例えば、ブレーキが踏まれていなければ始動できませんし、毒草を口にした際には味覚が異常を検知してくれます。なお、これをプログラムで表現すると Setter内に Validation機能を付加した状態に相当します。 特徴 OCPの特徴は、スコープ視点では最適化の範囲がクラスに特化している点です。また、この最適化が、クラスには最適な責務が割り当てられていることが前提で、その責務における変更には継承や委譲を用いて開放し、他クラスからの干渉にはカプセル化を用いて閉鎖するという手段をとります。 コードサンプル 実装に話を移します。コード上での Open-Closed-Principle(開放閉鎖原則) は継承と委譲による縦方向(自身の関連)の開放と、カプセル化による横方向(他者との関連)の閉鎖によって実現します。縦方向と横方向と表現しているのは、一般的にクラス図上では自身の継承関係は親クラスを上方に、子クラスを下方に配置して表現するからです。 閉鎖 閉鎖部分は変数やメソッドの可視性で制御します。これらの可視性を private (Javaなら protectedも可) にすることで外部アクセスを遮断します。こうすれば外部のクラスから変数やメソッドにアクセスされることはありません。 public class Machine { private boolean powered; private CPU cpu; private Memory memory; private HardDisk hardDisk; private Set<USBDevice> usbDevices = new HashSet(); private Set<HDMIDevice> hdmiDevices = new HashSet(); // 振る舞いを省略 } (Javaによる閉鎖) 外部から変数へのアクセスが必要な場合は、Getter / Setter等の publicなアクセッサメソッドを通じてアクセスを許可します。結局はアクセスさせてしまうのであれば最初から変数を publicにするのと同じだと思われるかも知れませんが、以下の点が異なります。

Card image cap
Factory Method

目的 FactoryMethod はインスタンスの生成に関するパターンです。 このパターンの目的は、インターフェイスに対する実装の生成・代入局面を隠し、疎結合を完成させることです。 概要 インターフェイスを用いてポリモーフィズム(多様性)を実現したいとします。また、利用側にインターフェイスだけを見せることで疎結合も実現したいとします。こうすることで、多様な実装の切り替えに対応することができ保守性を高められるからです。さて、この時気をつけなければならないのが、ポリモーフィズム適用時の隙(仮称)です。 ポリモーフィズム適用時の隙 あるサスペンスドラマで犯人が怪人の仮面をかぶっていたとします。犯人はAさんかも知れない。しかしBさんかも知れない。AさんにもBさんにも第1の犯行が可能だ。そして難解なことに第2の犯行も可能だ。 ポリモーフィズムとは「多様性」です。仕事を決めてやり方を決めない「インターフェイス」によってその多様性を担保します。多様性はインターフェイスを満足させられる実装であれば、どれでも実現可能…という仕組みで担保します。この例で説明するならば、犯人はAさんでもあり得るしBさんでもあり得るのという部分です。そして「疎結合」というのは、利用者は何を使っているのかを感知していないという構造で担保します。この例で説明するならば、目撃者は仮面をつけて行った2つの犯行しか見ていないという部分です しかし、AさんかBさんの何れかが仮面を被るその瞬間を見られていたら? 多様性はいとも簡単に崩れ去ります。ですから犯人は仮面を被る瞬間を見られてはいけません。仮面を付けた状態で物陰から登場するのです。この、物陰の役割を果たす存在こそが Factory Method、物語や設計をミステリアスにさせるエッセンスです。 以上の例からもわかるように、Factoryと聞くと生成プロセスに注目してしまいがちですが、FactoryMethodの真の目的は生成物をインターフェイスの型で提供することで、その実体を隠しきることなのです。 例えば、良く見かける生成プロセスを隠しきれていないコードを提示します。このコードは生成のプロセスを完全に隠蔽してはいません。そのため当該スコープにおける多様性は担保出来ていません。これが Factoryを使わないコードの限界です。 var e tech.Engineer = &impl.Onda{} e.Program() e.Test() e.Publish()  ではこのコードが問題であるかというと、一概にそうとも言えません。何故なら代入の瞬間以外はインターフェイスに依存していないコードであることは担保できているので、本当にポリモーフィズムが必要になったその後に、簡単にポリモーフィズムを完成させることができるからです。 [余談] ポリモーフィズムとインターフェイス分離の原則 先の例に関してですが、名探偵が、様々な状況から推測して犯人を絞り込んでゆく様は、インターフェイスの責務を増やしてゆく過程に相当します。犯行日の深夜に動けた人、凶器を調達できた人、そして被害者の行動を知っていた人。最終的にインターフェイスを満足させ続けた人が犯人です。この様にして重すぎるインターフェイスの責務は実現クラスの枯渇を招きます。謎を解き明かしたい探偵の技術としては良いものですが、抽象化の力を振るいたい設計者として悪しきものです。この件に関する詳細は「インターフェイス分離の原則」で説明したいと思います 実現 次のシナリオでは、"Hello"という文字列を格納するため、Storageを生成しています。最初のテストケースでは factoryを用いてその実体を隠蔽できていますが、二番目のテストケースでは factoryを用いないため、その実体を隠しきれていません。この瞬間に疎結合という価値は失われてしまいます。 package factory_method_pattern import ( "testing" "github.com/stretchr/testify/assert" "github.com/koooyooo/go-design-pattern/factory_method/factory" ) // FactoryMethodを適用した場合 // Storageの実体を知ることなく利用できる (疎結合) func TestFactoryMethod(t *testing.T) { // Factory経由で実体を隠蔽 s := factory.CreateStorage() err := s.Store([]byte("Hello")) assert.NoError(t, err) } // FactoryMethodを適用しない場合 // Storageの実体を知ってしまう (密結合) func TestNonFactory(t *testing.

Card image cap
Singleton

目的 Singletonの目的は一つだけインスタンスを生成し、それを使い回すことです。 パターンの実装としては「一つしか生成できなくする」ための制約を作り込む形となります。 OOPとインスタンス数 オブジェクト指向(OOP)の世界では、特別な理由がない限り現実世界のモノの数と、インスタンスの数を一致させます。豚が3匹いるなら Pigインスタンスを 3つ生成します。同様に車が1000台なら Carインスタンスを1000個生成します。この理由は、複製したインスタンスの数だけ固有の情報を格納できるからです。現実世界のものが増えたのならば、それを管理するオブジェクトも同じだけ増やすのが基本です。オブジェクト指向言語が現実の世界や概念の構造を雄弁に表現できるのは、この状態の複製が簡単に(new Car() or Car{})実現できるからという点が大きいのです。 さて、オブジェクトと言えば状態と振る舞いを持ちます。この両者で特に大きな進化をもたらしたのはどちらでしょうか?実は、インスタンスの増加で増えるのは状態だけです。状態はクラス(struct)定義時のメソッドの宣言から変わることはありません。インスタンスが複製されてもその振る舞いが同じ様にコピーされるだけです。唯一の振る舞いの変化が許される部分は、そのインスタンスの状態に依って振る舞いが変わるロジックが定義されている時です。しかし、オブジェクト指向言語によって主役の座に位置するのが状態であることに変わりはありません。 この常識を根底から覆すのが Singletonです。何故ならその状態を1つで良い…と言い張るのですから。しかし Singletonについて深く考察してゆくと、そこにもまた OOPの本質に迫る側面があることに気付くはずです。 一つだけインスタンスを生成してそれを使い回すと何が良いのでしょうか。すぐには思い浮かばないかも知れませんが、これが活きる局面は数多くあります。 1. 数の限定 最初に紹介するのは、本当に数が限られているものをそのとおりに表現するものです。1つしかない物を厳格に表現するなら Singletonは良い選択です。例えば日本に政府は一つしかありません。つまり以下のコードが成立します。 gov := jpgov.Instance() ディズニーランド内にミッキーマウスは複数存在しないという話を聞いたことがあります。それが本当なら次のコードも成立します。 m := disney.MickyInstance() 次にシステム的な面で考えてみます。データベースを管理するマネージャが1つしかないのであれば、以下の表現が可能です。インスタンスは関数経由で取得しても良いですし、初期ロード時に生成しても構わないなら変数として用意しておいても良いでしょう。 // 関数で取得(実装上の選択肢は広がる) dbMgr := database.ManagerInstance() // 変数で取得 dbMgr := database.ManagerInstance 2. 状態の共有 Singletonが公開されるということは、単一の状態も公開されるということです。一般的にSingletonの参照はグローバルに提供されることが多いため、どこからでもアクセス可能な単一の状態という便利なものが手に入ります。例えば、Util系のクラス(package)は振る舞いという観点で単一のものを提供しますが、Singletonでは状態という観点で単一のものが提供出来るわけです。 次の例は、Observerパターンで通知を行う局面です。システムの各所からこのObserverに通知が入ります。このObserverは Singletonとして常に単一のインスタンスが参照されるので情報を集中的に管理できています。各所で別々のインスタンスを生成してしまったのでは集中管理になりません。 func (s Service) report() { o := observer.Instance() o.reportAccess(1) } 一つしか無いというのは、一箇所で情報を管理できるという強みがあるのです。総理大臣や大統領はいくら忙しくても2名以上にしません。責務あるインスタンスに情報を集約させるための手段としても Singletonは有効なのです。 3. 参照の取得 上の例をもう少し使います。通常、Observerのインスタンスが必要なら上位の生成元から延々と引数で引き渡す必要があります。しかし、Singletonではその必要がありません。Singletonの生成部分はグローバルな関数(Javaなら staticメソッド, Golangなら グローバル関数)ですので、調達は簡単です。引数をシンプルに保てますのでその点では設計上の長所となります。 一方で、グローバルな関数に触れるというのは、その処理が副作用を持つということです。単純な長所だけではないので、この部分には気をつける必要があります。余談ですが、この解決策としてDI(Dependency Injection: 依存性の注入)という仕組みが存在し、DIが導入の際には Singletonは DIコンテナ側で実現されます。これがSingletonの完成形かもしれません。 type Service struct { // 注入される依存性 o *observer.