Lumped tweets

Just marks

Better python code, better naming, better programming, better productivity

はじめに

この文章は社内の新人 Data Scientist (likewise a Machine Learning Researcher/Engineer) 向けに書いた文章をもう少し清書し、また社内向けの内容を削ったものです。

以下の区切り線以降がその内容です。


このドキュメントの目的

もしあなたがPythonのコードを「最低限読み書きできる」状態であれば、そこから「よりよいPythonを記述することを考えられる」ようになることです。

実際にできるかどうかはともかく、 よりベターなコードとは一体どういうものなのかを朧気ながら知り、考えることで、「今自分が書くべきコード」を意思と意図をもって書き下せるようになるのが目的です。

雑なコードであっても、「今は雑に書く」ことを「選択」しているのか「そうとしか書けない」のかでは雲泥の差と私は思っているからです。

このドキュメントの対象者

自分、他人の書いたソフトウェアを継続的にメンテナンスをしたことがない、あるいはその経験に乏しい人

1年もチーム開発していれば身につくようなことしか基本的には取り上げません。

エクスキューズ

間違っていることも在ると思います。にんげんだもの

タイトルについて (読み飛ばして良い)

なぜ Best code,... best productivity ではないのかというと、 ソフトウェアエンジニアリングにおいてBest wayは本当に限られたものしか無いためである。

全ての状態、全ての条件において 「たったひとつの冴えたやりかた」 は実際にはほとんど存在しない。Zen of Python ではそういっているが、そんなPythonも複数のやり方があるし、様々なProject, VirtualEnv環境がありもはや幻想である。

PEPが揃い、現在ではデファクトスタンダードがあるが

「この場合は、こういった理由により、こうすることがBetterなため、この選択をする」ということを繰り返していくことで、より高度な、よりBetterな、より複雑なことができるようになっていくと私は思う。

しかしそれは「この時の場合はこれがBest」という選択も事実上ある。Betterしかないというのもまた時と場合によるのだ。

しかしそれでも常により良い判断をするため、という目的のために Better code... というタイトルにした

PEP について

Python Enhancement Proposal の略称ことPEP

https://peps.python.org/pep-0001/#what-is-a-pep

Python Enhancement Proposals(PEP)は、Pythonに対する新しい機能の提案や、システムの変更、あるいはプロセスの改善など、Python言語に対する様々な側面の提案書である。

事実上RFCのようなもので、仕様書ではないにせよ、その仕様を提案するものである。

AcceptされたPEPはPythonにとって正式なDocumentとなり、そこに書いてあるものは「Pythonとして」のDocumentであり、事実上オフィシャルのアナウンスメントとして扱って良いものだと思う。

Example of PEP517

例えば https://peps.python.org/pep-0517Pythonプロジェクトにおけるビルドシステムアーキテクチャを提案している。

よって、現在では「プロジェクトのビルド」方法においては、このPEP517に従った方法で行うのが事実上のスタンダードになっているが、一方でこのPEP517は「仕様」を策定しているに過ぎないため、その方法に従ったツールは含まれておらず、またそのScope外のことについては異なるPEPで提案されるのが普通である。

具体的に言えば、PEP517はビルドシステムアーキテクチャを提案しており、その中身として例えば

  • pyproject.toml
    • このファイルは、パッケージのビルドに必要な設定や依存関係を定義する新しい標準的な方法として導入されました。setup.pyやsetup.cfgの代わりとして、より明確で簡潔な設定が可能になりました
  • ビルドバックエンドの指定
    • pyproject.toml内で、どのビルドバックエンドを使用するかを指定することができます。これにより、従来のsetuptools以外のビルドツールも選択できるようになりました
  • ビルドフロントエンドとバックエンドの分離
    • ビルドフロントエンド(例:pip)は、pyproject.tomlを参照して、適切なビルドバックエンドを呼び出してパッケージのビルドを行います。これにより、ビルドプロセスがより柔軟になりました

上記等が含まれている。

ビルドバックエンドとビルドフロントエンドはお互いに「お互いがPEP517に従って動いている」ことを前提にして構築されれば、そのどちらかを可変にでき、プログラマーが任意にそのツールを利用できる。

一方で、例えば python の version 管理(venvとか)についてはこのPEPに含まれておらず、PEP内部においてもそれは

We do not require that any particular “virtual environment” mechanism be used; a build frontend might use virtualenv, or venv, or no special mechanism at all. But whatever mechanism is used MUST meet the following criteria:

特定の“仮想環境”メカニズムを使用することを要求していません。ビルドフロントエンドはvirtualenvやvenvを使用するか、特別なメカニズムをまったく使用しないかもしれません。しかし、どのメカニズムが使用されるにしても、以下の基準を満たす必要があります

と、その要求を示すに過ぎない。

このように、ある要素において、Pythonとして、一定の品質とReviewをもって提示されるのがPEPであり、できるだけPEPにおいて推奨されていることは実施していくべき内容である。

他にも、標準Formatを指定する PEP8 があり、これがもっとも知られているPEPであると言っても過言ではないだろう。

コーディングスタイル, PEP8

全Pythonistaが読んだことがあるはずの、The Zen of python の中にはいくつかStyleに関係するようなこともある。一応列挙する

Beautiful is better than ugly.
醜いより美しいほうがいい。
Explicit is better than implicit.
暗示するより明示するほうがいい。
Simple is better than complex.
複雑であるよりは平易であるほうがいい。
Complex is better than complicated.
それでも、込み入っているよりは複雑であるほうがまし。
Flat is better than nested.
ネストは浅いほうがいい。
Sparse is better than dense.
密集しているよりは隙間があるほうがいい。
Readability counts.
読みやすいことは善である。
Special cases aren't special enough to break the rules.
特殊であることはルールを破る理由にならない。
Although practicality beats purity.
しかし、実用性を求めると純粋さが失われることがある。
Errors should never pass silently.
エラーは隠すな、無視するな。
Unless explicitly silenced.
ただし、わざと隠されているのなら見逃せ。
In the face of ambiguity, refuse the temptation to guess.
曖昧なものに出逢ったら、その意味を適当に推測してはいけない。
There should be one-- and preferably only one --obvious way to do it.
何かいいやり方があるはずだ。誰が見ても明らかな、たったひとつのやり方が。
Although that way may not be obvious at first unless you're Dutch.
そのやり方は一目見ただけではわかりにくいかもしれない。オランダ人にだけわかりやすいなんてこともあるかもしれない。
Now is better than never.
ずっとやらないでいるよりは、今やれ。
Although never is often better than right now.
でも、今"すぐ"にやるよりはやらないほうがマシなことが多い。
If the implementation is hard to explain, it's a bad idea.
コードの内容を説明するのが難しいのなら、それは悪い実装である。
If the implementation is easy to explain, it may be a good idea.
コードの内容を容易に説明できるのなら、おそらくそれはよい実装である。
Namespaces are one honking great idea -- let's do more of those!
名前空間は優れたアイデアであるため、積極的に利用すべきである。

実はこのThe Zen of Python もPEP20である。このPEP20は具体的なものもあるが抽象的なこともあるし、なによりジョークまで入っている。

しかし、PEP8はより具体的に、「Python のコードはこう書く」といったようなガイドが記述されている。

一部を引用すると

1レベルインデントするごとに、スペースを4つ使いましょう

https://pep8-ja.readthedocs.io/ja/latest/

とかである。 if 式の書き方や 関数の定義の方法、 class の書き方や前後の改行数等も結構細かく定義されている。

「いや別に好きに書いたらいいじゃん」

と思うこともあるだろう。しかし、コードは「書かれる時間」よりも「読まれる時間」のほうが長い。

書くのは一瞬であっても、そのコードが数時間、数日、数年と残っていくと考えれば、「一定のルール」に従って記述されていることで、その読み手へのコストを下げることができる。

たとえ発注者やPMが「いや、PoCだから書き捨てでさくっと作ってよ」と言っても、実際にそれが本当に書き捨てであるかどうかは捨てられるまでわからない。

なにより、お昼休みにご飯を食べて返ってきた1時間後の自分でさえ、乱雑に書いた自分コードを読んで理解できないこともあるだろう。その可能性を減らすための方法である。

さらにはコードを書く時も「どのように書いたらいいか」と考えるコストを減らすことで、よりロジックやデザイン/設計へ脳内リソースのフォーカスを与えることができる。

PEP8 以外のスタイルガイド

pep8 とは異なるが大きな企業だとStyleGuideを公開していることもある。有名なところで言えば

Google Python Style Guide だろう https://google.github.io/styleguide/pyguide.html

詳細は省くが、PEP8よりもぶっちゃけより思想が強い。好みで採用するべきだろう。

いずれにせよ

こういったコード規約は エディターやIDE にLinter/Formatterを組み込んだりして「考えずに適応される」状態であると良いだろう。

例えば、 pep8 であれば autopep8 というツールが存在し、 代表的なエディターである VSCode などでもそれを利用するプラグインなどがある。

これはpep8に準拠するコードへ保存時に自動的に整形したり、準拠していないコードへ警告を出したりする。

こういったツールを使うことで、怒られたら治す、というか勝手に治ってる、という状況が作れるようになり、より生産的になるだろう。

google のStyle Guide へ従う場合は https://github.com/google/pyink などが利用できる。

命名、名前、型Hint

私の尊敬する先輩の言葉がある。

「名前が決まればプログラミングの8割は終わったようなもの」

さて、本ドキュメントの本懐である。ここまでは全部素振りである。

このドキュメントのタイトルが Better python code, better naming, better programming, better productivity となっているが、実際には Better Name, Better Name, Better Name, Better type eating the better Name. である。

ディレクトリ名、ファイル名、クラス名、関数名、メソッド名、変数名……

これらに対して一貫して必要なことが名前付けであり、名前付けの本質はそのモノへの理解と表現である。

ある要素に名前がつけられたとき、人間はその名前でその要素を認識する。

例えば、以下のようなコードがあるとする。

class Employee:
    name: str
    id: int
    def __init__(self, name: str, id: int)
      self.name = name
      self.id = id

class Company:
    employee_list: []Employee = []

    def __init__(self, employee_list: []Employee):
      self.employee_list = employee_list


company = Company([Employee('Taro', 150)])
print(company.employee_list) #=> [Employee(name="Taro", id=150)]

良さそうである。

が、 Employee.id は一体なんだろうか? Employeeの Identifier として int 型の値は、例えばなんだろうか?

DB上の Auto Increment されるレコードの作成順番も表す PrimaryKey だろうか? それとも従業員番号だろうか? あるいはそれとは関係ない何らかの一意な数字だろうか?

一般に、ベターな答えは、一番最後の「何ら関係のない一意な数字」であるだろう。 Ruby on Rails の標準が AutoIncrement-edなPrimaryKeyだったが、実際には特定の関係ない一意な数字が振られるのがベターな場合が多いだろう。

ただしこれはMySQLがAutoIncrement-edなPrimaryKeyに対して強力に作用するため、場合によってはそれがBestになる場合もある たとえばこれがGoogle Cloud Spanner のようなデータベースの場合はAuto Increment-ted なInt型のIDではパフォーマンスが出ない。 よって、IDは ID として抽象化してモデリングを行ったほうが良い事がおおい。

しかし例えば、 150 は Taro の従業員番号であるとする。なので、IDと言いながら実際には従業員番号である。

なので、このインスタンス変数名は id よりも employee_number のほうが適切だろう。

class Employee:
    employee_number: int
    # 省略

おっと、ここだけではない! よく見てみると name は “Taro“ と入力されている。

これは名、あるいはFirstNameを表現しているが、では果たして「firstname」という名前にインスタンス変数名を変えるべきだろうか?

名前は大変難しい。少なくとも、「last name」と「family name」だけで区切られた名前は「ミドルネームはどうするねん」となる。

そして fullname とする場合、氏名が入ってくることを期待しそうだが、システムにおいて本名が必要だろうか?

この場合のシステムではおそらく従業員を管理する必要がある関係上、本名が必要だろう。

翻って、では Employee クラスの name 変数は一体何が入ってくるべきだろうか? 日本においては法律上利用される氏名が入ってくるべきだろう。

であるならば、 「氏名」だが、氏名は name である。ならば name で良い可能性が高いが、例えば法律的にValidな名前、例えば戸籍上の名前、とするならば official_name というのはどうだろう。

それに、近年では通称を利用するケースもある。通称は a.k.a (as known as) という言葉を使う言葉がある。よし、では nameofficial_name として、ついでに aka_name も追加すれば本名と通称を登録できるようになるだろう。

しかし戸籍上の名前は機密度が高い可能性がある。そもそも「戸籍上の名前」という「戸籍上」とPrefixがつくあたり、そもそも戸籍を表現する必要があるのではないか? と考える事ができる。

class FamilyRegistration:
  uuid: str
  fullname: str

class Employee:
  aka_name: str # 通称
  family_registration: FamilyRegistration #正式名称は別クラス
  # 省略

このように戸籍情報を別のクラスとして、そこで法律上Validな情報を保存し、Employee単位では通称だけを利用するようにするのはどうだろうか?

いやしかし、日本の「戸籍」は「一つの戸籍の中に複数の人間の情報」が入っているため、「戸籍の中の特定の誰か」を示す必要があるのでないか?

そうであるならば

class FamilyRegistration:
  class Status(enum):
    ALIVE = 'alive'
    DEATH = 'death'
  class PersonRegistration:
    id: str
    state: Status
    fullname: str
    dob: datetime

A family registration has many person registration のような状態にし、戸籍上死亡しているかどうかや date of birthday を取れるようにして……

いや、dob は Data of Birth の略だ。わかりにくので data_of_birthborn_datetime にしようとする。

ここで考えるのはまたしても名前、そして型だ。

日本の戸籍上では「日時」は永続化されない。よって、 time の情報は不要のはずだ。よって

class PersonRegistration:
    born_date: date

生年月日、というふうにして、「時」はいらんだろう、ということで born_date として型も date 型にして「日付」までしか持っていないことを表現するとしよう……

いかがだろうか。「この名前で良いのだろうか」と考えることは「こう理解しているが正しいだろうか」と考えることである。

つまり名前付けを終える、ということは「この理解とする」と現時点において確定させることである。

特にシステムのユースケースやコンテクストなどを決まっていないため↑の話は無限に深掘って無限に複雑にしていくことができる。

例えば戸籍情報はとてもセンシティブであり、取り扱うのは難しいだろう。よって、雇用に当たって必要な情報を考え切り出して、その属する境界(Boundary)に名前をつけていくのが良いだろう。

つまり、 Employee には名前は持たず、 PersonalInformationPaymentInformation あたりに分けて、用途に応じて名前を保存しておくとかである。

このように、「今このシステムにおいての目的、制約において、あるデータ、操作、情報がどんな性質を示す、持つ、表すのかを表現する」ことが「名前を決めること」である。

よって、たとえ間違っても、 list[Person()] を受け取る変数(Personの配列)が plist にならないことがこれでわかるだろう。

ここまで読んだあなたであるならば、その変数名は person_list, persons, or people になるはずだ。(ただし、 people はより「人々」といった抽象概念を表現することがあるので、 日本人がよく読むコードなら、 person_list か persons が良いだろう。実際には persons は……ここで英語の授業は終了とする)

型の入口

何気なく「型」の話をした。Python3では変数にTypeHintをつけることができる。これはHintであり、実行時エラーにならない。例えば以下のようなコードは実行できる。

>>> def plus(i: list[int]) -> list[int]:
...     return [] + i
...
arg: list[str] = ['hoge']
>>> plus(arg)
['hoge']

plus 関数の 引数 iint の配列であることを期待するが、実行時には 文字列の配列(list[str]) を投入できる。

例えばエディタ上で書いてみると(私のこれはIntellIJというIDEですが、VSCodeでも大体ちゃんとやってくれるはず)以下のように警告してくれる

ide

エディタやIDE上にこういった機能がなくても、例えば以下のように mypy などを使うと警告を抽出してくれる

$ poetry run mypy testing.py
testing.py:6: error: Argument 1 to "plus" has incompatible type "list[str]"; expected "list[int]"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

(ポエム) 型、という言葉について

型といったときに、様々なコンテクストがある。プログラミングにおける型はおよそ計算機数学における型と近しいが、実際にはそれを更に蒸留したものと認識できる。

殆どの場合、特に私のような平凡なプログラマーが型といったときにはデータ型を指しており、ラムダ計算におけるKindを指しているわけではない。

型システムというものそのものが一つの研究分野として成立している世界であり、それはとても超大な理論体系を持っている。

これまでは HaskellOCaml、F# といった一部のプログラミング言語においては超強力な型システムがあった関係で、この一部のプログラマー言語を活用したソフトウェア開発においては生産性へ寄与していた。さらに近年ではTypeScriptやなどの登場により高度ながらもHaskellなどと比較すれば簡便化され、より広く普及した型システムが登場したことにより、その深淵を除く機会が増えてきた。

もちろん、近年のC++(over C++11) などでも型推論などが使えるため、日夜その重要性は増しているといっても過言ではないだろう。

一方で、未だ殆どのプログラマーにとって 型 はただのデータ型であり、そのデータとその性質を内包する境界を宣言するものに過ぎない。が、たったそれだけでも我々プログラマーに対して与える影響は大きく、またその価値は絶大である。

もし計算機数学、計算機における型そのものへ興味があるのであれば、「型システム入門」を読むのが良いだろう。え? 僕? 諦めました。むずかしすぎるよぅ……

型システム入門 −プログラミング言語と型の理論− | Benjamin C. Pierce, 住井 英二郎, 遠藤 侑介, 酒井 政裕, 今井 敬吾, 黒木 裕介, 今井 宜洋, 才川 隆文, 今井 健男 |本 | 通販 | Amazon

型と仲良くする

簡潔に、そしてわかりやすい範囲で述べれば、型は 「ある値(データ)がどのような値を取りうる範囲を決め、それがどのような性質であり、どのように振る舞うか」でる。

Python3 には組み込み型と User定義型 ≒ class がある。

例えば int 型と float 型を考えてみよう。

integer_value: int = 1
str_value: string = "My name is HaiTo"

例えば、 integer_valueint 型であり、これは 符号付き整数 を表現している。同様に str_value は文字列を示す

つまり、

  • integer_value
    • どのような値の範囲か(※事実上無限長拡張できるが、64bit整数値とする)
      • 63bit 整数 〜 63bit整数 (-9,223,372,036,854,775,807 ~ 9,223,372,036,854,775,807)
    • どのような性質か
      • 符号(正負)付きの整数値であり、小数の値は存在せず、また int はPython3においてはメモリが許すだけの桁を維持できる
    • どのように振る舞うか
      • +, -, *, - といったオペレーターに反応できる
  • str_value
    • どのような値の範囲か
    • どのような性質か
      • 文字の列(シーケンス)であり、またリテラルでありつまりImmutableである。内部的にはUTF-16,32のいずれかだが事実上任意の文字を表現できる。byte等にも等価変換できる
    • どのように振る舞うか
      • split, strip, replace, …. といった振る舞いを持っている

当たり前のことを当たり前にいっているが、これは PrimitivePythonの組み込みクラスだからだ。例えば以下のようなデータクラスを宣言したとしよう。

@dataclass は説明のため利用しません

class Temperature:
    kelvin_value: float
    def __init__(self, kelvin_value: float):
        self.kelvin_value = kelvin_value

kelvin 、つまり絶対零度を0としたSI単位系のうち温度を表すもので、それを Temperature 、つまり「気温」として表現しているに過ぎないコードである。

このClassがあろうが無かろうが、以下のコードでもよいはずだ。

temprature = 309

さすがに 309 は意味がわからないかもしれないので、もう少し丁寧にする

temprature_kelvin = 309

これでおよそ人肌(36度くらい)の気温であることがわかった。

例えば、二日間の気温の差をとるとしよう。

yesterday_temprature_kelvin = 309
today_temprature_kelvin = 300
print(yesterday_temprature_kelvin - today_temprature_kelvin) #=> 9

素晴らしい。ではこれをファーレンハイトで表示してほしいという話があったとする。

ファーレンハイトを計算するには {摂氏}×1.8+32 だ。その定義より便宜上 9 kelvin の差は摂氏9度の差と ほぼ 等価なので、流用できる。

yesterday_temprature_kelvin = 309
today_temprature_kelvin = 300
diff_temprature_kelvin = yesterday_temprature_kelvin - today_temprature_kelvin
diff_temprature_fahrenheit = diff_temprature_kelvin * 1.8 + 32
print(diff_temprature_fahrenheit) #=> 48.2

素晴らしい。ファーレンハイトという単位の是非はともかく、現実には一部地域で常用されている単位なので、変換ロジックも簡単だ。

ただ、 1.8 とか 32 とか、このコードが短く、受け取る変数名がわかりやすいので、「Kelvinとファーレンハイトの変換なんだな」と理解でき、そのために必要な変数なんだなと理解できる。

ただこれが以下のようなコードならどうだろうか。

prev = 309
current = 300
diff = prev - current
diff_2 = diff * 1.8 + 32
print(diff_2) #=> 48.2

何をやっているのか はわかるが なんでこうなっているのか が全くわからないだろう。

1.832 とは一体何者だろうか? 数値に意味はあるのか? てか diff_2 ってなんだ? なんで diff を print しないんだ?

変数名がわかりやすければ、まだわかるのだ。直近の例のように変数名がわかりにくかったり、数字に名前がついていないとその意味もわからないのだ。

では元に戻り、改めてデータクラスを見てみよう。

class Temperature:
    kelvin_value: float
    def __init__(self, kelvin_value: float):
        self.kelvin_value = kelvin_value

例えば、このデータクラスを以下のように拡張する

あらかじめ言っておくが、これは間違った拡張である

class Temperature:
    BASE = 273.15

    def __init__(self, kelvin_value: float):
        self.kelvin_value = kelvin_value

    def __add__(self, other: 'Temperature') -> 'Temperature':
        if not isinstance(other, Temperature):
            raise TypeError("Unmatched type")
        return Temperature(self.kelvin_value + other.kelvin_value)

    def __sub__(self, other: 'Temperature') -> 'Temperature':
        if not isinstance(other, Temperature):
            raise TypeError("Unmatched type")
        return Temperature(self.kelvin_value - other.kelvin_value)

    def to_celsius(self) -> float:
        return self.kelvin_value - self.BASE

    def to_fahrenheit(self) -> float: 
        return self.to_celsius() * 1.8 + 32

add だとか sub だとかは何かというと、Python演算子というのは事実上メソッド呼び出しであり、たとえば

1 + 2

としたときには、 (1).__add__(2) という形に書き下せる(実際にはちょっと違うけどまぁ許して)

つまり、 __add__ を実装すれば + に、 __sub__ を実装すれば - に反応できるようになる。なので、このクラスを使うと以下のように表現できる

yesterday: Temperature = Temperature(309)
toady: Temperature = Temperature(300)
diff: Temperature = yesterday - today
print(diff.to_celsius()) #=> 9

なんとわかりやすいコードだろうか。昨日の気温、今日の気温があり、差の気温を取って、それをセルシウス度として表現する。

型情報がついきているため、変数名を短くしても十分にわかりやすいだろう。もちろん yesterday_temperature としてもよいだろう。

kelvin という情報は変数名には不要だろう。その意味は Temperature 内部に隠蔽され、四則演算のロジックはそのクラスの内部で処理されるので、Kelvinであるかどうかはインスタンス化されたあとであれば知らなくてよいだろう。

ここで条件が変わったことを知ったとしよう。見ての通り、Kelvinは我々にとって直感的な値ではない。入力値として摂氏の値が入ってくることになった。

内部的にはKelvinで扱っておこう。なにせSI単位系なので……しかしこれは直感的には正しそうだが実際には正しくない場合が多い。前述の通り、セルシウス度とKelvinの互換性は高いが完全ではない。しかし今回はこのまま内部的には引き続き Kelvin を保持するとする。

なので、以下のようにすると良いだろう。

class Temperature:    
    BASE = 273.15
    kelvin_value: float

    @classmethod
    def from_celsius(cls, celsius_value: float) -> Temperature:
        return cls(celsius_value + cls.BASE)

    def __init__(self, kelvin_value: float):
        self.kelvin_value = kelvin_value
#  .. 省略

ではこれを利用する

yesterday: Temperature = Temperature.from_celsius(24)
toady: Temperature = Temperature.from_celsius(20)
diff: Temperature = yesterday - today
print(diff.to_celsius()) #=> -269.15

このように、「気温どうし」を足したり引いたりすると、直感的に間違っているような値が出てくる。

しかし、内部計算をみればこれは正しい。 つまり 297.15 - 293.15 = 4.0 kelvin ==> -269.15℃ なのだ。

「差がしりたいんだ」となるだろう。なので、そもそも Temperature - TemperatureTemperature を返却していることがおかしいのだ。

同様に + でもそうだ。つまり、

class Temperature:
    BASE = 273.15

    @classmethod
    def from_celsius(cls, celsius_value: float) -> 'Temperature':
        return cls(celsius_value + cls.BASE)

    def __init__(self, kelvin_value: float):
        self.kelvin_value = kelvin_value

    def __add__(self, other: 'Temperature') -> float:
        if not isinstance(other, Temperature):
            raise TypeError("Unmatched type")
        return self.kelvin_value + other.kelvin_value

    def __sub__(self, other: 'Temperature') -> float:
        if not isinstance(other, Temperature):
            raise TypeError("Unmatched type")
        return self.kelvin_value - other.kelvin_value

    def to_celsius(self) -> float:
        return self.kelvin_value - Temperature.BASE

    def to_fahrenheit(self) -> float:
        return self.to_celsius() * 1.8 + 32

これで、ただ float を返すようになった。その和/差をまた気温として使いたければ、改めてそれでInstance化するとよいだろう。

素晴らしい。この拡張について考えていけば、 from_fahrenheit() クラスメソッドがあってもいいだろう。いっそ、 __init__ においては呼び出し元を限定して from... からしか呼び出されたときに動作しないようにし、 from_kelvin, from_fahrenheit, from_celsius のFactoryClassMethodを経由してのみ使えるようにしてもよいだろう。

さて、ここまでにおいてこの Temperature は事実上ただの float 型の値であるが、それを包んだ Temperature 型としている。

このTemperature 型は

  • どのような値を取るか
    • floatの値だが、Kelvin値において地球上ではおよそ200.0 kelvin - 350.0kelvin 程度のRangeである
  • どのような性質か
    • 「気温」を表している。
  • どのように振る舞うか
    • 足し引きができ、また様々な単位の値へ変換できる

といえる。

考えれば、 これはただの float よりもその表現力が「狭くなっている」ことがわかるだろう。

私はわざと割り算や掛け算を宣言しなかった。

「気温」で割り算や掛け算をするだろうか? 殆どの場合しない。しないのであれば、提供 しないことができる

つまり、事実上ただの float を扱っているのだが、 float よりもその取り合える振る舞いが狭まっているのだ。

これは興味深いことである。同じ 300.0 という値であっても、それを float として使うか、気温として使うかで性質が変わり、取り得る値や振る舞いが変わっているかのように見える。

これが型を付けることの強いモチベーションである。

名前と同じように「こいつが一体なにものなのか」を表現する方法が型である。

このように、値に型をつけて、名前をつけて、「こいつは一体なにもので、どういった振る舞いをするのか」を確定させていくのがプログラミングにおいてもっとも重要な要素の一つであり、そしてたったひとつの冴えたやりかたが存在しないものなのである。

なぜ私が Jupyter notebook の外の世界において pandas や polars, dict を消したがるのか

別記事予定

実践編

Dropped

おわりに: よりBetterなコードを目指すために

名前付けや型のセクションで何度も言っているように、「今の考えていること」を表現するのがコードである。

「こういったデータが入ってくる(input)」「こういった処理をする」「こういったデータを返却する(output)」

殆どのプログラミングというのはこの3ステップを実現するためのものである。

InputがFileなのか、API Call なのか、はたまたS3を通じたファイル連携なのか……Inputも色々あり、Outputも同様に色々ある。

それぞれにおいて、「色々」に具体的な名前をつけたり、型を付けて値の範囲を制限したり、Validationしたりしていく。

つまり、プログラミングをするのに実際に必要なのはそのプログラミング言語に対する理解と、そのプログラミング言語の外の知識や経験である。

あるビジネスや機能を表現するのに対して必要なのは、そのビジネスや機能の知識なのだ。プログラミングをうまくなるためには、プログラミングだけをしているのではなく、あなたが立ち向かうビジネスや機能について知り、自分なりにそのビジネスをプログラムとして表現し、向き合い、改善し続けていくことだ。

すべての時間軸において最高のコードはない。ビジネスは常に変化するし、環境も変化するし、我々が理解するビジネスもまた日々変わっていく。

すべての時間軸において最高のコードはないが、「今考えうるBetter than elseだと自分が思うコード」は間違いなくある。

しかし一方で、「今は時間がないので」、このコードである、というのも理由として十分ではある。

どこを何を、どこまで何をするのか、その判断もまたプログラミングなのだ。

ぜひ今後もプログラミングをしていこう。

References

変化について

Table of Contents

  • 背景
  • 仕事について
  • プライベートについて
  • おわり

背景

転職してから早いものでもうすぐ半年が経とうとしている。 自分の忘却する速度が早いことを自覚しているので、鮮明なうちに諸々言語化しておく。

仕事について

具体的な社名は会社のルールとして多分出せないのですが、外資系の企業でプリセールスエンジニアみたいなことをやっています。

これまでのキャリアを知っている方や、僕のパーソナリティを知っている方からすると、「おまえ……大丈夫なのか?」「どうして?」とよく聞かれるので、イメージにはなかったキャリア路線だと思います。

2014年3月あたりに2015年新卒としてSansan株式会社から内定をもらい、その後2014年5月からSoftware EngineerとしてSoftwareを書いてお金をもらい始めました。

それから何度か転職をし、2022年9月まで基本的には自社サービスそれ自体のSoftwareを書いてお金をもらっていました。都合8年くらいでしょうか。

色々、本当に色々ありました。

その結果、ビジネスの成長、成功のためにSoftwareを作ること、特に「Softwareを作ること」自体には少しだけ自信が付きました。

しかし、前者の「ビジネスの成長、成功」をもっと貪欲に考えていくことをもっとしていきたいと常々思っていました。

これは僕が平凡な、特出すべき頭脳を持っていないがゆえの生存戦略の一つです。

ある問題を見て即座に、あるいは探索的にでもベターなアルゴリズムを構築するスキルを私は持っていませんでした。もちろんこれを身につけることは可能です。

ある大きな問題をみて、問題を分割して解きやすいようにするスキルを私は持っていませんでした。もちろんこれも身につけることは可能でしょう。

そもそも、問題とは何なのかを探索し定義するスキルを私は持っていませんでした。これもまた身につけることは可能だと信じています。

自分のキャリアを見たときに、今後5年10年という未来を見たときに自分がどのようなスキルセットを持っているべきか、どのような私になっていれば、より大きな仕事ができるかを考えていました。

その一環で、より「ビジネスの成長、成功」とは何なのか、そういったことを定義、検証することをしてみたくなりました。

なので、一旦自社サービスのSoftware Engineer としてのキャリアは本業としては停止し、「顧客にとって本当に必要だったもの」を探索する役割へとジョブチェンジしました。

一応JDとしては Presales Engineer なのですが、実際には技術コンサルタントのような振る舞いを目指しています(事実、しています、しようとしています)

前職には4年くらいいましたが、今回はどれくらいいることになるかはわかりませんが、大きな仕事をやれるといいなぁと思い奮闘しています。

ジョブチェンジ、それが仕事面での一番の変化です。

Software Engineer としてのキャリアの継続自体は、副業で Production Code をちょこちょこ書いているので、完全に途切れたわけではなく、時代的にもそれが許容されているのがありがたい限りです。

プライベートについて

子供が3歳になり、もうすぐ幼稚園にはいろうとしています。

子供のアップデート速度はとても早く、3年前は自分で寝返り一つできなかったのに……とよく思います。

しかし3年、4年という月日でコロナがあったり、引っ越ししたり車を新調したりしました。

男女問わず、高校生にまでなると親と買い物、お出かけ、といったイベント事は少なくなっていくだろうなと思っています。

自分の子供がどうしたいかはわかりませんが、そう考えると子供と一緒によく遊んだりするのはあと10年も無いのかもしれないなと思ったりしました。

幼稚園以降、もっと子供の行動範囲や行動力が高まってくる時期だと思い、もっと色んな所に一緒にいったり、いろんなことをしたいなと考えています。

まぁ、ありきたりで抽象的で実現性が見えてこないふんわりとした話でした。

果たして来年はどう思っているのか、ただそれだけのための文章です。

おわりに

2022年は個人的には激動の1年でした。

しかし、2023年もまたドッタンバッタン大騒ぎでしょう。その大騒ぎもまた面白くしていくぞと思っています。

特にとりとめもない文章でしたが、そういった文章がここに投げられるようになったのも私の変化なのかもしれない。

https://upload.wikimedia.org/wikipedia/commons/b/b7/Strudwick-_A_Golden_Thread.JPG

引用 : モイラ (ギリシア神話) - Wikipedia 運命の三女神

退職しました

List of contents

  • 退職しました
  • 理由
  • 4年間のお気持ち
  • 今後
  • 終わりに

退職しました

例の会社を退職しました。 9/30日付けで退職です。 なぜ会社名が出てないかというと、まぁ色々あったからです。I'm sure you understand why I did not write here.

先に書いておくのですが、かなり乱文になります。

以下の画像は僕の好きなアメンドーズ 六本木ヒルズにある謎の蜘蛛こと 「ママン」 です。

ママン

現職が気になる人は twitter で聞いてください。DMで答えます。

twitter.com

理由

まぁ色々ありますが、色々あるので気になる人はTwitterで聞いてください。DMかDiscordかSlackか……まぁ何らかの方法で答えるかもしれません。

待遇についてですが、特に不満は僕はありませんでした。

もちろん、Meta,Apple,Alphabet,Amazon,Microsoft...とかのビッグテックを始め、純粋な外資系などに比べるともちろん劣るかもしれませんが、僕に対しては十分な待遇だったと思います。

とはいえ、僕を始め、チームメンバーは皆優秀な人たちだったのでみんなの待遇が更にもっと良くなってほしいと思っていますが、それはそれです。

お気持ち

もともとは日本事業のBackendEngineer(当時はAPIエンジニアとか言ってた気がする)としてのオファーをもらったんですが、オファー面談で人事の人に「これで待遇ってマックスですか?」みたいなことをまぁ礼儀だと思って言ったんですね。

で、その結果「もっとチャレンジングな環境」みたいなやつとしてアメリカ事業のEMがちょうど日本に帰ってきてHeadCountがあるからという感じで、当時のEMと面談が組まれて、晴れてアメリカ事業のエンジニアになったわけです。

このときの面談では「英語は2割くらいしか使わないよw」みたいな感じで、言われてならいけるっしょみたいな感じになりました。

まぁ僕も海外製のゲームをよくやっていたこともあって、英語は全く喋れなかったんですが、英語自体にアレルギーがなかったわけですね。

で、いざ面談後一ヶ月か二ヶ月くらいして入社後「やっぱ5割くらいかなw」とかになってゲラゲラ笑いながら必死に勉強しました。

そして入社二ヶ月後に「3週間後くらいにUSいくけどパスポートある?」みたいになって急いで取ったりして、とても楽しかったです。

初めてのアメリカ入国審査で「What do you do」が聞き取れなくて死ぬかと思いました。

で、入社3ヶ月後には外国籍のエンジニアが急激に増えた結果PublicCommunicationは基本的にすべて英語になりました。

もちろん僕は英語はまだ喋れませんでした。しょうがないね。

始まりはそんな喜劇がありました。当時のEMの人は今でも高品質なコードをガリガリ書いていますし、まさに凄腕エンジニアって感じがして最高の人です。

彼の人のおかげで英語を使った環境に勢いだけで飛び込み、なんとか生きてこれ、その経験がつめたのはとてもありがたかったです。

そこからは色々ありながらも継続してアメリカ事業のコードを書く日々で、とても充実していました。

素晴らしいチームメンバー、素晴らしいマネージャー、素晴らしい他部署のメンバー、何よりも私の短い会社勤め経験史上(7年くらい)最高の部門長、そう、Brad、あなたのことです。彼らと共に仕事ができたのが本当に幸せでした。

また、子供が入社後1.5年後くらいで生まれ、育休を取れたのはとても良かったです。2ヶ月育休を取り、その後はコロナが始まった影響でリモート中心で仕事をしていたため子供の日々の成長を至近距離で感じられています。

色々ありましたがトータルとしてはとても楽しかったです。少なくとも僕のチームメンバーは100%優秀なメンバーしかおらず、最高でした。

社内では色々なところに結局首を突っ込んで多方面に迷惑をかけたきもします。が、経営陣に認知された「なんかうるさいやつ」としての役割は果たせかなと思っています。ごめんな。

もっと色々あるんですが、気になった人はReplyかDMかなんかで聞いてください。DiscordかSlackあたりで話しましょう。

今後

転職先は決まっています。フリーランスではありません。どこか知りたい方は(以下略

10/3日からお仕事が始まります。厳密には10/1から理論上は所属していることになる契約です。

終わりに

今年度を持って、私は30歳になります。

これまで何かを変えたい、便利にしたい、世の中を良くしたいとして作り上げてきたすべての創造物へ敬意を払い、私もその隊列の末端に加われていると信じ、今後ともやっていきます。

There is nothing like a dream to create the future. - Victor Hugo

GG

トヨタ博物館に展示されている世界で最初の内燃機関車ベンツ1号レプリカ
トヨタ博物館に展示されている世界で最初の内燃機関車ベンツ1号レプリカ

twilio-video-processors.js を Next.js で使う

この記事でわかること

twilio-video-processors.js を Next.js で利用する方法

twilio.github.io

背景

副業先では Twilio を使ってサービスを提供している。

サービスは Next.js + Graphql + React な今どきっぽい構成。

で、今どきのウェブ会議ツールにあるようなVirtualBackgroundとか、BluredBackgroundとか使えたら良いよねって話があった。

調べてみると↓のように、ライブラリが提供されていたのでこれを使えばいいじゃん、となった。

しかし、一向に動作しなかったがこれを解決したのでその方法を記す.

www.twilio.com

結論

執筆時点のバージョン(1.0.1)では、 以下のコマンドを実行しファイルをコピーし、 以下のように設定しなければならない。

また、 Component は dynamic import する必要がある。

$ mkdir -p public/twilio-video-processors 
$ cp -a node_modules/@twilio/video-processors/dist/build public/twilio-video-processors
    const blurBackground = new VideoProcessors.GaussianBlurBackgroundProcessor({
      assetsPath: `/twilio-video-processors/`,
      blurFilterRadius: 20,
      maskBlurRadius: 5,
    });

それぞれの理由

mv と cp と assetsPath について

GaussianBlurBackgroundProcessorassetsPath@twilio/video-processors/dist/build を指し示す必要がある。

このディレクトリには tensolflow lite のファイルや、それを用いて学習させた結果のモデルデータのバイナリが入っている。

これを読み込み、ブラウザがWASMで処理することで初めて実現される。

普通のReactなどであれば、Bundle時に適当にExposeしたりすればいいが、NextJSだとそもそもSSRされた時点でもちろんそんなディレクトリは参照できないし、バンドルできない。

これを回避するために、 Next.js における /public 以下にファイルを配置すると Next.js から / で参照できることを利用する。

dist/build 以下はファイルが複数あるので、適当にディレクトリを切って、そこに展開する。

このコマンドをBuild時に行うようにすれば良い。

dynamic import

Next.js には Dynamic import という機能がある.

Advanced Features: Dynamic Import | Next.js

これを使えばSSRを回避できる。(本来の用途とは違うけども).

なぜSSRを回避したいかというと、 この @twilio/video-processors は動作環境がChromeとEdgeのみとなっている(1.0.1時点).

この判定のために、 window を利用している箇所がある。しかしSSR時点では window が存在しないため、 undefined variable なエラーがでてBuildできない。

よって、 Dynamic import を使い、 ssr: false のオプションを渡してSSRを回避する必要がある。例えば以下のような感じに。

// VideoComponent.tsx
// import 省略
import * as VideoProcessors from "@twilio/video-processors";

const VideoComponent: () => {
  // tracks は `LocalVideoTrack[]`
  const enableBlurOnTrack = React.useCallback(async () => {
    if (!VideoProcessors.isSupported)  return

    const blurBackground = new VideoProcessors.GaussianBlurBackgroundProcessor({
      assetsPath: `/twilio-video-processors/`,
      blurFilterRadius: 20,
      maskBlurRadius: 5,
    });

    await blurBackground.loadModel();

    tracks.forEach((tr) => {
      if (!(tr instanceof LocalVideoTrack)) {
        return;
      }
      tr.addProcessor(blurBackground);
      setEnabledBlur(true);
    });
  }, [tracks])
}
// Room.tsx
const Video = dynamic(() => import("./VideoComponent"), { ssr: false });

まとめ

一番最後のCode exampleが全てといえばそう。

Should I test private method? No. Then....

Background

I like the following page.

shoulditestprivatemethods.com

It have only contents of "No". I normally agree for that's saying "no".
But sometime we'd say "it depends on context/case by case" without argue.
Because it certainly depends on the context, and software has a wide variety of backgrounds, and business purposes.

However, I would like to saying "we should avoid write test for private method at anytime as possible as can".
Therefore, I make a proposal for resolution of avoid to testing at private method.

Issue

First of all, Why does we facing thing of would we want to test private method?
Most of the time, there is a complex logic hidden in private method.
And it's often a Public Method argument, or the conditional branch that depends on the instance variables.

For example

class Registration
  def create(user, params)
    user = modified_user(user, params.except(:user))
    # insert account and update user table
    Account.save(user, params.except(:account))
  end
  private def modified_user(user, params)
    # the complex if statements depends on user and params
  end
end

At this point, I'd want to test what account.user value in the DB.

RSpec.describe Registration do
  describe "when params.user.x was ..."
  describe "when params.user.y was ..."
  describe "when params.account.X was ..."
  # ...
end

This is more than enough. However, the User information in particular is a test of how the Private Method works.
This kind of test will break quickly. Because Private Method is a class-internal entity and the interface is only loosely bound.

Then

How avoid it? I'd say we should do dependency injection.
In before a day ago, I wrote the what's the constructor injection and what the profit for it. let read if you don't know the dependency injection.
Dependency Injection allows you to inject dependent logic and behavior from the outside and delegate the responsibility of the things that depend on.
And you should do the following.

  • Give a name to the behavior that was originally the private method and generate it as a new separate small class.
  • Inject a new small class to original big class at construction.
  • Instead to call dependency class function from calling private method at the original class.
  • Test the Public Method of a small separated class.

For example

class Registration::User
  def modify(user, params)
    // complexy work
  end
end

class Registration
  def initialize(user_modifier)
    @user_modifier = user_modifier
  end
  def create(user, params)
    Account.save(@user_modifier.modify(user, params), params.except(:account))
  end
end

You can write test code for each Registration::user.modify and Registration.create both.
The advantage of this way is that combines responsibility and logic into a small class.
It makes it easier to write test codes.
Also, although I wrote Ruby this time, if you are using PHP or other more typical languages, you can use function type constraints to make the interface fixed.
Refactoring is also easier because the tests are small and easier to write, and if you find out that the changes depend on a class, you only need to fix that part.
I like this small class principle.

Conclusion

I will ever say "Should I test private method? NO!"

refs

stackify.com

I prefer to add case name for each data providers in PHP Unit

Conclusion

This example is too easy, so you don't have to do it actually.

<?php

// Please do not
public function dataProvider(): array
{
    return [
        [1, 2, 3],
        [-1, 1, 0]
    ];
}

/**
 * @dataProvider dataProvider
 */
public function testSum(int $left, int $right, int $expected): void
{
    self::assertSome($expected, $left + $right);
}

// Please do it
public function dataProvider(): array
{
    return [
        'normal' => ['left' => 1, 'right' => 2, 'expected' => 3],
        'anomaly' => ['left' => -1, 'right' =>1, 'expected' => 0]
    ];
}

/**
 * @dataProvider dataProvider
 */
public function testSum(int $left, int $right, int $expected): void
{
    self::assertSome($expected, $left + $right);
}

Why

when the test fail, you got the following message on your terminal.

1) AwesomeTest::testSum with data set #2 (-1, 1, 0)

If you write the my proposal way, you got the following message too.

1) AwesomeTest::testSum with data set "anomaly" (-1, 1, 0)

I prefer to the my proposal way because I can detect where I should looking quickly.

How about?

Easy to write tests with Constructor Injection for PHP

Excuse

This page is translation for my self previous article as a my English training of the following link. Let correct my wrong English/strange explainings in comment or my Twitter

Constructor Injection でやるテストしやすいPHP - Twitter以上ブログ以下

twitter.com

Background

We should write test code for each implementation. However, If you write static function, You face a problem that is difficult written to test codes.

Because, if static function depends on other classes, you have to care about logic for other classes.

So, I want you to know how to write easily testable code by Constructor Injection.

Table of Contents

  • What Constructor Injection?
  • Example code
  • Pro/Cons
  • Conclusion

What Constructor Injection?

First, we should be known to Dependency Injection before learning what about Constructor Injection.

Dependency Injection

Dependency Injection is a way to realize the SOLID principle.

An object can ignore how an injected object is implemented and use only their knowledge/behavior. As a result, An object can focus on describing themself behavior.

DependencyInjection has the following positive effects on your code. * Increase the flexibility of being configurable. * Decrease the cost of writing the test code. * Become independent of external systems/frameworks.

more description wrote in https://en.wikipedia.org/wiki/Dependency_injection#Three_types_of_dependency_injection

Constructor Injection

Constructor Injection is one of the implementation methods for Dependency Injection.

In PHP, for example for following code.

<?php
class K {
  // over 7.4, we can declare property type.
  private Dependency $dependency;
  public function __constructor(Dependency $dependency)
  {
    $this->dependency = $dependency;
  }
}

Okay. It's easy. If you still using old PHP version, you should declare type on way of PHP Doc( /* @var Dependency */ )

Column: Efficient Dependency Injection.

In the more higher design of Dependency Injection, They don't use Dependency as a concreted class. Than the right way that instead of DependencyInterface/AbstractDependency as the abstractions. For example,

<?php

interface DependencyInterface {
  public function awesomeFunction();
}

class K {
  // over 7.4, we can declare property type.
  private DependencyInterface $dependency;
  public function __constructor(DependencyInterface $dependency)
  {
    $this->dependency = $dependency;
  }
}

It's a good way than my first example code. However, I don't use Interface in this article because of the amount of description line increases.

let write code using by Constructor Injection

Basically, I case about only a few things of the

  • Don't new without controller class.
  • Receive a behavior known class in constructor.
  • A method wrote for depends on the argument and call to behavior known class.

The following sample code is a performing check to a transaction that should prevent or not by A/B testing and price threshold.

memo: LaunchDarkly is an A/B test provider https://launchdarkly.com/

<?php

class PreventHighPrice
{
  private const PRICE_THRESHOLD = 100;
  // LaunchDarkly is the A/B test provider.
  private LaunchDarklyService $ld_service;
  public function constructor(LaunchDarklyService $ld_service)
  {
    $this->ld_service = $ld_service;
  }

  public function prevent(User $user, Item $item): void
  {
    if (!$this->ld_service->isExperiment($user)) {
      return;
    }

    if ($item->getPrice() >= self::PRICE_THRESHOLD) {
      throw new PreventTransactionByHighPrice()
    }
  }
}

Okay. It's clear code. This class has two logic.

  • Is a user target on the A/B test?
    • If no, don't check the item price.
  • Is the item price over than equal threshold? *If yes, throw an Exception.

Also, let show test codes.

<?php
class PreventHighPriceTest extends PHPUnit\Framework\TestCase
{
  /**
   * @dataProvider testShouldPreventProvider
   */
  public function testShouldPrevent(bool $is_experiment, int $price, bool $expect): void
  {
    $user = new User()

    $ld_service = $this->createMock(LaunchDarklyService::class)
    $ld_service->method('isExperiment')->expects($this->once())->willReturn($is_experiment);

    $item = new Item(['price' => $price])

    $preventHighPrice = new PreventHighPrice($ld_service);
    $this->assertSame($expect, $preventHighPrice->shouldPrevent($user, $item))
  }

  public function testShouldPreventProvider(): array
  {
    return [
      'no experiment' => [false, 100, false],
      'in experiment lower price' => [true, 99, false],
      'in experiment higher price' => [true, 100, true]
    ]
  }
}

How do you think about this? I'd say test code is clear and test cases are satisfied for describes to expectation.

So, this test code can focus on describing what the function does and under what conditions/arguments.

If this class dependency will increase, the constructor arity is rising. And add a new Mock on test code.

However, This test code expects a class of mocked objects has satisfied for tests. Because what happens when a function is called with arguments is a black box.

Column: Single responsibility principle.

A class should have responsibilities expressed in one concise sentence.

I usually split a class behavior for small classes and build one behavior from dependencies by injection.

https://en.wikipedia.org/wiki/Single_responsibility_principle

Pro/Cons

Pro

  • Easy to write test code.
  • Easy to explain complicated conditions on test code.
  • When the depends class of internal behavior will change, it can ignore that if returning type won't change.

Cons

  • Mock is a mock. In PHP unit cannot catch when the changes of return types by dependency class's function.

That's issue already solved by new version's PHPUnit. We should increase PHPUnit version if you have time!

github.com

Conclusion

If you are worried about how to write test code for your facing class, you can separate that behavior and declare other class and inject that complexity behavior from your facing class's constructor.

After you choose the Dependency Injection way, you can get more clear code, and testing is functional.