<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>やったこと</title>
  
  <subtitle>やったこと、しらべたこと</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://i.botch.me/"/>
  <updated>2020-07-12T08:05:22.281Z</updated>
  <id>https://i.botch.me/</id>
  
  <author>
    <name>ytetsuro</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>CleanCode読書記録〜創発〜</title>
    <link href="https://i.botch.me/2020/07/12/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E5%89%B5%E7%99%BA%E3%80%9C/"/>
    <id>https://i.botch.me/2020/07/12/CleanCode読書記録〜創発〜/</id>
    <published>2020-07-12T07:35:18.000Z</published>
    <updated>2020-07-12T08:05:22.281Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第12章を読んだメモ。</p><h2 id="創発-第12章-の概要"><a href="#創発-第12章-の概要" class="headerlink" title="創発(第12章)の概要"></a>創発(第12章)の概要</h2><ul><li>創発的設計を通して、洗練する<ul><li>ケントベックによる優れた設計を生み出すことが可能となる単純な4つの規則(重要順)<ul><li>全テストを実行する。</li><li>重複がない</li><li>プログラマの意図が表現されている。</li><li>クラスとメソッドを最小化する。</li></ul></li><li>これに従えば、以下が得られる。<ul><li>コードの構造と設計に対する洞察。</li><li>SRPやDIPのような原則を簡単に適用できる。</li></ul></li></ul></li><li>単純な設計への規則1: 全テストを実行する<ul><li>単体テストを書け。</li><li>単体テストが書けるようにすることで、単一機能を実装しているような設計へと促される。</li><li>物事をテストが簡単になる方向へ推し進めることができる。</li><li>強い結びつきがあるとテストが困難になるので、必然的にDIPや依存性注入のようなツールなどで結びつきが最小限になっていく。</li></ul></li><li>単純な設計への規則2〜4: リファクタリング<ul><li>テストができれば気軽にリファクタできるようになる。</li><li>リファクタリングで以下をシステムに与える<ul><li>凝集性を高める</li><li>結合を弱める。</li><li>関心ごとを分離</li><li>システムの関心ごとをモジュール化</li><li>関数とクラスを小さくする</li><li>より良い名前を考える。</li><li>重複の排除</li><li>表現力の保証</li><li>クラスとメソッド数の最小化</li></ul></li></ul></li><li>重複の排除<ul><li>優れたシステムの最大の敵は重複</li><li>余計な作業や、余分な危険、不必要な複雑さをもたらす。</li><li>例えば、isEmptyメソッドとsizeメソッドを持つCollectionクラスがあった場合、isEmptyメソッドでsizeメソッドを使うことで、量を取得するロジックをsizeに共通化できるなど。</li><li>小さな再利用ががシステムの複雑さを大きく減少させることになる。</li><li>大きな再利用を実現するには、小さな再利用を理解することが肝</li></ul></li><li>表現に富む<ul><li>自分自身が理解できるコードを書くのは簡単。</li><li>保守する人たちに、深い理解を期待することはできない。</li><li>ソフトウェアプロジェクトの大半は保守コスト</li><li>良い命名と、驚き最小の原則、関数とクラスを小さく保つkとおで実現できる。</li><li>標準の用語を用いることでも表現しやすくなる。</li><li>デザインパターンの名前をそのままクラスに当てることで他の開発者に意図を伝えるのもあり。</li><li>適切に記述された単体テストでも表現できる。</li><li>最も重要なのは、「試す」こと。<ul><li>コードが書けたら、次に読む人にとって分かり易コードか、想像力を働かせる。</li><li>自分の書いた関数とクラスに少しだけ時間を割いて、良い名前と関数の分割に注意を払う。</li></ul></li></ul></li><li>クラスとメソッドを最小限に<ul><li>重複の排除、コードの表現力、SRPといった基本的な概念は行き過ぎてしまう場合もある。</li><li>クラスとメソッドを小さくしようとすれば、小さなクラスとメソッドがあまりに多く生成されてしまう。</li><li>クラスとメソッドが多すぎる状況は、名器な独断の結果から生じている可能性がある。</li><li>全てのクラスにインターフェースの作成を強要するようなコーディング標準や、フィールドの振る舞いとデータクラスと振る舞いクラスとを常に分けることを強制することは避けた方が良い。</li><li>目的は、システム全体を小さくしつつ、関数とクラスも小さくするということ。</li><li>ただし、優先順位は、規則の中でも最も低いということが重要。</li><li>テストや、DRY、コードの意図の表現の方が重要。</li></ul></li><li>結論<ul><li>経験にとってかわる実践手法は存在しない。</li><li>通常なら、習得に何年も要するが、上記の規則でそれを促進することができる。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>重要順以外は概ね同意すると感じた。<br>個人的には、重複の削除は2番目に来るほど重要ではない。<br>過度な共通化は地獄を見ると思う。<br>そのため、その共通化は本当に必要なのかの判断する目がとても大切になると感じている。</p><ul><li>将来的に共通化した処理が異なる可能性が低いのか。</li><li>処理は一緒だが、概念は異なるのではないか。</li><li>将来的に異なる処理にしたとしても、驚きは少ない実装方法は何か。</li></ul><p>など。</p><p>また、共通化の方法として、特に本書で紹介されていたテンプレートメソッドパターンなどの実装は、is a関係を保ちにくいと考えるため個人的には委譲最優先で実装している。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第12章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;創発-第12章-の概要&quot;&gt;&lt;a href=&quot;#創発-第12章-の概要&quot; class=&quot;headerlink&quot; title=&quot;創発(第12章)の概要&quot;&gt;&lt;/a&gt;創発(第12
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜システム〜</title>
    <link href="https://i.botch.me/2020/07/12/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%80%9C/"/>
    <id>https://i.botch.me/2020/07/12/CleanCode読書記録〜システム〜/</id>
    <published>2020-07-12T05:46:45.000Z</published>
    <updated>2020-07-12T07:25:19.620Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第11章を読んだメモ。</p><h2 id="システム-第11章-の概要"><a href="#システム-第11章-の概要" class="headerlink" title="システム(第11章)の概要"></a>システム(第11章)の概要</h2><ul><li>あなたは、街をどうやって作りますか？<ul><li>自分だけで全ての詳細を管理することはおそらく無理</li><li>何人かは全体像に対する責任があり、残りは詳細に関心を持つ。</li><li>ソフトウェアも同じ。</li><li>より高いシステムのレベルを綺麗な状態に保つ方法を考える。</li></ul></li><li>システムを使うことと、構築することを分離する。<ul><li>使うと構築することは異なる。</li><li>オブジェクトの生成などの開始処理はアプリケーションが扱わざるを得ない関心ごと。<ul><li>大体のアプリケーションは関心ごとの分離ができていない。</li></ul></li><li>よく見られる、1回目はインスタンス化して返す。2回目は以前インスタンス化したものを返すみたいな実装は、構築のロジックと通常の実行処理が混ざっている。<ul><li>小さな意味でSRPに違反している。</li></ul></li><li>これらはモジュール化して通常のランタイム処理から切り離し、それが主要な依存関係を解決するための生合成の取れた手段となるようにする。</li></ul></li><li>mainの分離<ul><li>1つの方法として、構築処理をmainモジュールへと移動し、システムの残りの部分はオブジェクトが適切に生成された前提のもとに構築する。</li><li>依存関係を表す矢印が、いずれもmainから出る方向を向いている。</li><li>これはアプリケーション側は、mainやオブジェクトの構築処理についてなんら知識を持たないことを意味する。</li></ul></li><li>ファクトリ<ul><li>オブジェクトの生成に対してアプリケーションが責任を持たなければならない時もある。</li><li>この時は、抽象ファクトリパターンを用いて、アプリケーションコードから分離する。</li></ul></li><li>DI<ul><li>オブジェクトの利用と生成を分離する強力な仕組み。</li><li>制御の反転(IoC)の適用</li><li>通常はmainルーチンか、特殊用途のコンテナ(多分DIコンテナのこと)を利用する。</li><li>大体のDIコンテナはオブジェクトが必要になるまで生成しない。</li><li>こうしたコンテナは遅延評価の用途に用いることで最適化の効果が得られる。<ul><li>遅延評価と遅延初期化は最適化の1つにすぎなくて、まだ未成熟であるということを忘れないこと。</li></ul></li></ul></li><li>スケールアップ<ul><li>将来を見通して、最初から正しいシステムは神話に過ぎない。</li><li>可能なのは今日の「ストーリー」を実装し、明日のストーリーを実装するためにリファクタリングして拡張していくことだけ。<ul><li>これがアジャイルのイテレーティブでインクリメンタルな手法の本質。</li></ul></li><li>しかし、システムレベルのアーキテクチャは単純なものから複雑なものへとインクリメンタルに成長するものではないことは明らか。</li><li>EJBのバージョン1,2は関心ごとを分離できていない。<ul><li>そのため、大量のライフサイクルメソッドや、RDBの詳細、トランザクションの特性、セキュリティ束縛などを1つ以上のXML配備記述子で指定する必要がある。</li><li>その結果、テストが困難</li><li>EJB2のアーキテクチャ外での再利用は事実上不可能。</li></ul></li></ul></li><li>横断的関心事<ul><li>EJB2のアーキテクチャはある領域では関心ごとの分離ができている。<ul><li>トランザクション、セキュリティ、永続化の挙動の設定はソースコードとは独立して指定できる。</li></ul></li><li>永続性のような関心ごとは、オブジェクトの自然なドメイン境界に横断的に現れる。</li><li>EJBアーキテクチャに対して、今後AOPの活躍が予想される。<ul><li>AOPは横断的関心事に対してモジュール構造を取り戻すための一般的なアプローチ</li></ul></li></ul></li><li>Javaプロキシ<ul><li>プロキシを通じてメソッドの呼び出しをラップすることがAOPであると誤解されがち</li><li>真のAOPの価値的はシステムの振る舞いをわかりやすく、モジュール構造を保って指定できることだが、プロキシではこういったことができない。</li></ul></li><li>Pure JavaのAOPフレームワーク<ul><li>Springでは、ビジネスロジックをPOJOで書くことができ、純粋にドメインに集中できる。<ul><li>横断的関心ごとを含んだアプリケーション基盤は設定ファイルやAPIによって宣言的に組み込まれる。</li></ul></li><li>XMLが冗長で読みにくくなるがアノテーションを使うことで、明示的な結線ロジックを減らすことができる。</li><li>このようなSpringの宣言的モデルによる横断的関心ごとをEJB3では継承している。</li><li>永続化のマッピング詳細が頻繁に変わらない限り、おそらく多くの開発部隊はアノテーションを選ぶ。</li></ul></li><li>AspectJアスペクト<ul><li>非常に豊富で強力な関心ごとの分離を実現するツールだが、ツールに慣れる必要があるのと新しい言語とそのイディオムの使用を学習する必要がある。<ul><li>アノテーションでだいぶ改善された。</li><li>全機能説明するのは本書の範囲を超えてるから詳細は、ドキュメント読んで。</li></ul></li></ul></li><li>システムアーキテクチャのテスト実行<ul><li>アプリケーションのドメインロジックをPOJOで記述できればアーキテクチャのテスト実行が可能となる。</li><li>事前の大規模設計(Design Up, Front, BDUF)が不要。</li><li>世界中の最も大規模なウェブサイトのいくつかは、高い可用性とパフォーマンスを実現できている。</li><li>お互いの依存性が少ない設計は、抽象化とのスコープのレベルが単純だから、キャッシュ、セキュリティ、仮想化といったものを柔軟に、効率的に用いることができる。</li><li>良いAPIは、ほとんどの場面で境界の外になければいけない。<ul><li>開発チームの労力をユーザストーリの実装に集中させる必要がある。</li><li>そうしないと、アーキテクチャのしばりが顧客への価値提供の効率化を邪魔する。</li></ul></li></ul></li><li>意思決定を最適化する<ul><li>大きなシステムであれば全ての意思決定を一人で行うことは不可能</li><li>資格のある人たちに権限を委譲するのが最善。</li><li>決定は、それが手遅れとなる直前まで延期することが最善である。</li><li>早すぎる決定は、最善とは言えない知識のもとで行われる。<ul><li>顧客からのフィードバック、プロジェクトに対する精神的な思い、選択した実装の経験がずっと少なくなってしまう。</li></ul></li></ul></li><li>検証可能な価値を追加する際には、標準を賢く使用する。<ul><li>EJB2が標準であるといった理由で選択されていることがある。<ul><li>誇大広告付きの標準に取り憑かれ、自分自身の顧客にとっての価値を実装することをないがしろにしてしまったチームを数多く見てきた。</li></ul></li><li>標準は、アイディア、コンポーネントの再利用、経験を持った開発者の求人、アイディアのカプセル化などを容易にする。<ul><li>しかし、標準の策定に必要な時間が長過ぎて、いくつかの標準はそれが使われる場面で必要となる真のニーズと乖離してしまう場面がある。</li></ul></li></ul></li><li>システムはドメイン特化言語を必要とする。<ul><li>DSLの作成が再び脚光を浴びている。</li><li>ドメイン専門家が書くような散文構造のようにコードが読めるようになる。</li><li>優れたDSLはドメインの概念とそれを実装するコードとの間の「コミュニケーションギャップ」を最小化する。</li><li>効果的に使用すれば、DSLは抽象レベルをコードのイディオムとデザインパターンよりも上に引き上げることができる。</li><li>開発者はコードの意図を、適切な抽象レベルに見せることができる。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>この章で伝えたかったことがうまく読み取れているか自信がないが、</p><ul><li>オブジェクトの生成とビジネスロジックを分離すること</li><li>詳細と共に死にたくないなら、抽象化してPOJOで書こうね</li><li>標準をありがたがってないで考えろ</li></ul><p>ということかなと。<br>あとは、そうしないことによるデメリットがつらつらと書かれている印象。</p><p>ただ、DSLに関して、本書とは異なった印象を持っている。<br>個人的にはDSLは詳細に含まれると思っている。</p><p>そのため、上記に記載の通り、意思決定の最適化の観点ではそんなに手放しで喜べるものではないと思っている。<br>覚えても3年後には陳腐化とかが十分にありえる危険なものという認識なので極力避けたい。<br>このあたり、何か誤解があるかもしれないのでもう少しドメイン特化言語について学ぶ必要があるかもしれない。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第11章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;システム-第11章-の概要&quot;&gt;&lt;a href=&quot;#システム-第11章-の概要&quot; class=&quot;headerlink&quot; title=&quot;システム(第11章)の概要&quot;&gt;&lt;/a&gt;
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜クラス〜</title>
    <link href="https://i.botch.me/2020/07/11/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E3%82%AF%E3%83%A9%E3%82%B9%E3%80%9C/"/>
    <id>https://i.botch.me/2020/07/11/CleanCode読書記録〜クラス〜/</id>
    <published>2020-07-10T15:47:43.000Z</published>
    <updated>2020-07-10T16:36:50.306Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第10章を読んだメモ。</p><h2 id="クラス-第10章-の概要"><a href="#クラス-第10章-の概要" class="headerlink" title="クラス(第10章)の概要"></a>クラス(第10章)の概要</h2><ul><li>クラスの構成<ul><li>変数定義から書く<ul><li>Robert C. Martin的には以下の順番<ul><li>public static</li><li>private static</li><li>private</li><li>publicは避ける</li><li>public関数</li><li>private関数</li></ul></li></ul></li><li>このようにすると新聞のように詳細が下に来やすくなる。</li></ul></li><li>カプセル化<ul><li>privateに固執するわけではなく、テストを優先に考えている。<ul><li>protectedにしたり、パッケージスコープに入れることも考える。</li></ul></li><li>カプセル化を緩めるのは最後の手段</li></ul></li><li>クラスを小さくしなければならない<ul><li>とにかく小さくする。</li><li>クラスは、行数ではなく責務の数で小さくする。</li><li>メソッド数が少なくても責務が多すぎると良くない。</li><li>名付けでメソッドやクラスの責務を明確にする。<ul><li>Processer,Manager,Superなどの曖昧な名前は責務が不当になっているサイン</li></ul></li><li>クラスの責務を説明説明しようとした時に、「もし」「そして」「あるいは」「しかし」などの単語以外の単語を使って25語以内で説明できるようにする。<ul><li>説明の中に「そして」が出てきたら、責務を持ちすぎているサイン</li></ul></li></ul></li><li>単一責任原則<ul><li>OOPの概念で最も重要なものの1つ</li><li>そして、もっとも無視されがちな原則の一つ</li><li>変更の原因となるものが1つでなければいけない。</li><li>プログラミングの活動においても重要。<ul><li>プログラムが動作するようになった時点で、洗練するという別の関心ごとへ移ることに失敗しがち</li><li>小さなクラスがいっぱいあって、全体を見失うことを恐れている場合もある。</li><li>大きなクラスよりは小さなクラスの方が、可動部分が少ないため、気軽にコードをなげこめるようになる。</li><li>大きなクラスの場合、理解しなくても良い部分まで調べることになってしまう。</li></ul></li></ul></li><li>凝集性<ul><li>限られたインスタンス変数を持つべき。</li><li>クラス内のメソッドは1つ以上の変数を操作する。</li><li>全変数が全メソッドに使用されるクラスは凝集性が最大限に高い。<ul><li>最大限にせよというのは実現可能でもないし、有用でもない。</li><li>だが、凝集性を高める必要はある。</li></ul></li><li>関数を小さくし、引数リストを短くするという鉄則につながる。</li><li>あるメソッド群で使用されるインスタンス変数の急増に繋がった場合、クラスを分割できるサイン</li></ul></li><li>凝集性に気を配ると、大量の小さなクラスが生まれる。<ul><li>大量の引数に依存している大きな関数があった場合、気軽にインスタンス変数にして引数を減らす選択だと凝集度が下がりがち。</li><li>小さなクラスを大量に作って凝集度を高めると良い。</li><li>これにより、責務が区切られやすくなり、気軽に変更ができるようになる。</li><li>表現が豊かな変数名を与えられる。currentUserName -&gt; User.nameなどだと思われる。</li><li>コードの解説が、関数とクラスの宣言を利用できる。</li><li>ソースが見やすくなり、書式化の技法を用いることが容易。</li></ul></li><li>変更のために最適化する。<ul><li>洗練されたシステムでは、クラスは変更に対するリスクが最低限になるように構成されている。</li><li>OCPを意識して、新規機能が既存システムの拡張のみで実現されるようにすることで、既存にシステムに変更を及ぼさないようになる。</li></ul></li><li>変更から切り離す。<ul><li>詳細な具象クラスではなく、抽象に依存することで変更を切り離すことができるようになる。(DIP)</li><li>具象クラスへ依存しているとテストがしづらくなる。</li><li>こうすることで、柔軟性が高まり再利用がより簡単にできるようになる。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>SRPが何を指すかは体感するのに非常に苦労をした覚えがある。<br>確かに説明する時に「そして」が出てきたら、責務違反のサインは他人に伝えやすいなと思った。<br>ただ、モデリングを誤るとよくわからない責務のクラスができがちな印象を持っているので結構バランスが難しいと今でも感じている。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第10章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;クラス-第10章-の概要&quot;&gt;&lt;a href=&quot;#クラス-第10章-の概要&quot; class=&quot;headerlink&quot; title=&quot;クラス(第10章)の概要&quot;&gt;&lt;/a&gt;クラス
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜テスト〜</title>
    <link href="https://i.botch.me/2020/07/09/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E3%83%86%E3%82%B9%E3%83%88%E3%80%9C/"/>
    <id>https://i.botch.me/2020/07/09/CleanCode読書記録〜テスト〜/</id>
    <published>2020-07-09T13:58:42.000Z</published>
    <updated>2020-07-09T14:58:39.468Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第9章を読んだメモ。</p><h2 id="テスト-第9章-の概要"><a href="#テスト-第9章-の概要" class="headerlink" title="テスト(第9章)の概要"></a>テスト(第9章)の概要</h2><ul><li>TDD三原則<ol><li>失敗する単体テストコードを書く前に、製品のコードを書いてはいけない。</li><li>コンパイルが通り、適切に失敗する単体テストができるまでは、次のテストを書いてはいけない</li><li>現在失敗している単体テストが通るまで、次の製品コードを書いてはいけない。</li></ol></li><li>テストを綺麗に保つ<ul><li>汚いテストはテストを持たないのと同値<ul><li>未満ではない。</li><li>変更が難しくなる。</li></ul></li><li>テストも製品コードと同様に重要</li><li>汚いテストはメンテナンスコストが高い</li></ul></li><li>テストをXXX性を可能とする。<ul><li>コードの柔軟性、保守容易性、再利用性を維持、提供しているのは単体テスト。<ul><li>テストがあれば変更が怖くないから。</li><li>アークテク茶と設計を綺麗にするためのキー</li></ul></li><li>テストコードが汚いと最終的にすべて失い、コードが腐る。</li></ul></li><li>クリーンテスト<ul><li>読みやすくしろ。</li></ul></li><li>ドメイン特化テスト言語<ul><li>fixtureやassertをメソッドとしてまとめて、詳細な処理を隠す？</li></ul></li><li>二重規範<ul><li>テストは本番環境では実行されない。</li><li>処理のassertなどもまとめて検証できるように複数の確認項目を1度に返すシンタックスシュガーのようなメソッドを定義するのもアリ</li><li>メモリやCPU使用率などの効率は無視して良い。</li></ul></li><li>1つのテストに1つのアサート<ul><li>デイブ・アテルの見解の道場的には、1つのテストには1つのアサート文までとするべき</li><li>全く同じ実行でもテストメソッドを分けて検証することも検討する。</li><li>実行が同じならテンプレートメソッドパターンなどで重複を防げるはず。</li><li>しかし、2つ以上のassertを実装してもRobert C. Martin的には良いと思っている。<ul><li>可能な限りassertを減らす努力はしましょう。</li></ul></li></ul></li><li>1つのテストでは1つの概念を扱う<ul><li>ごちゃ混ぜ(関数Xを引数を変えて何度も実行して都度、結果をassertする)にテストを実行してはいけない</li><li>テストは短くしなさい。</li><li>テストに抜けがあった場合など、どこに追加すればよいか、どこまで書かれているのか把握しにくくなる？</li></ul></li><li>F.I.R.S.T<ul><li>First…高速に書く<ul><li>時間がかかると頻繁に実行する気が削がれる。</li><li>修正が容易な時期に問題に気づくことができなくなる。</li><li>コードを綺麗にするのが億劫になる。</li></ul></li><li>Independent…独立している<ul><li>好きな順序でテストができるようにしておく</li><li>後続テストの失敗につながる。</li><li>テストの後半部の問題を隠すことになる。</li></ul></li><li>Repeatable…再現性がある<ul><li>どんな環境でも再現可能にする。<ul><li>帰宅途中のネットワークに繋がっていないノートPCでも</li></ul></li><li>テストを失敗する言い訳を抱えることになる。</li><li>特定のテスト環境がなければテストできないという事実に気づかされることになる。</li></ul></li><li>Self-Validating…自己検証可能<ul><li>成功か失敗かのどちらかを出力する。</li><li>2つのテキストファイルを見比べてテストが成功したのかどうかを確認するのは避ける。</li><li>失敗の判定が属人的になってしまう。</li><li>長時間を要する手作業の検証が必要になってしまう。</li></ul></li><li>Timely…適時性がある<ul><li>必要な時にすぐ書けるようにする必要がある。</li><li>製品コードの前に書くこと。</li><li>製品コードがテストしづらいものになっていることに気づくことができなくなる。</li><li>テストが困難なコードを設計してしまうことになる。</li></ul></li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>テストはないよりは、読みにくくてもあった方がマシだとは思っている派なので、テストを持たないのと同値は言い過ぎだと思った。<br>関数でassertや実行をまとめるのは、目から鱗ではあった。(いっぱいassert書いてた。。。)<br>本書には、TDD三原則<br>として <code>現在失敗している単体テストが通るまで、次の製品コードを書いてはいけない。</code> とあるが、誤訳ではないかと思う。</p><p><a href="http://www.butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd" target="_blank" rel="noopener">Robert C. Martinのブログ</a> では以下のように書かれており、本書の言葉とは異なる。</p><ol><li>You are not allowed to write any production code unless it is to make a failing unit test pass.</li><li>You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.</li><li>You are not allowed to write any more production code than is sufficient to pass the one failing unit test.</li></ol><p>厳密には、<code>失敗するテストを通すためのプロダクトコード以外を書かない</code> ではないかと。(Clean Codeの英文の書籍は持っていないから翻訳が間違っているのか、Bobおじさんの考え方が変わったかどうかはわからないけど。。。)</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第9章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;テスト-第9章-の概要&quot;&gt;&lt;a href=&quot;#テスト-第9章-の概要&quot; class=&quot;headerlink&quot; title=&quot;テスト(第9章)の概要&quot;&gt;&lt;/a&gt;テスト(第9章
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜境界〜</title>
    <link href="https://i.botch.me/2020/06/09/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E5%A2%83%E7%95%8C%E3%80%9C/"/>
    <id>https://i.botch.me/2020/06/09/CleanCode読書記録〜境界〜/</id>
    <published>2020-06-08T17:18:01.000Z</published>
    <updated>2020-06-08T17:34:39.766Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第8章を読んだメモ。</p><h2 id="境界-第8章-の概要"><a href="#境界-第8章-の概要" class="headerlink" title="境界(第8章)の概要"></a>境界(第8章)の概要</h2><ul><li>サードバーティーのコードを使用する<ul><li>Mapとかを利用して不要なメソッドを提供するなどをやめよう。</li><li>ラップしてインターフェース境界を隠そう</li><li>インターフェース境界を使うならクラスの中だけ、あるいは、いくつかの強い関連を持ったクラス内での利用に止めよう。</li><li>公開APIでこれらを返したり引数で受け取らない。。</li></ul></li><li>境界の調査と学習<ul><li>学習テスト(サードパーティーのテストコード)を書いた方が良い</li><li>実装しているアプリケーションの中での使い方がわかる</li><li>サードパーティーのコードの利用方法がわかるので独自でカプセル化することもできるようになるため。</li></ul></li><li>学習テストはタダ以上のもの<ul><li>将来的にサードパーティーが更新されたとしても壊れるかどうかが確認できるようになる。</li><li>学習テストでなくとも、外部との接続テスト(インターフェースを製品のコードと同じように使用してみること)によって、外部との明確な境界を担保すべき。</li></ul></li><li>まだ存在しないコードを使用する。<ul><li>ストラテジパターンを使え</li></ul></li><li>綺麗な境界<ul><li>サードパーティーを利用しているコードをビジネスルールに持たせない。</li><li>自分たちが制御できないものではなく、自分たちが制御できるものに依存した方が良い。</li><li>Mapでラップしたりアダプタパターンなどを利用する。</li><li>これらによりサードパーティーコードの変更による影響点を最小化する。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>この章と、前の前の章で言われているデータ構造などの概念を掘り下げたものが、CleanArchtectureへとつながっていくんだなと思った。<br><del>というか、掘り下げていくと必然的にCleanArchitectureのような形になりそう</del><br>学習テストはコストが結構でかめだなと思った。<br>詳細であるサードパーティーのコードのテストよりも、学習テストよりも外部との接続テストぐらいにとどめた方が有用では？とは思った。(ただしチーム内で使い方の共有ができると行った面では有用性は確かにあると思う。)</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第8章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;境界-第8章-の概要&quot;&gt;&lt;a href=&quot;#境界-第8章-の概要&quot; class=&quot;headerlink&quot; title=&quot;境界(第8章)の概要&quot;&gt;&lt;/a&gt;境界(第8章)の概要
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜エラー処理〜</title>
    <link href="https://i.botch.me/2020/06/09/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E3%82%A8%E3%83%A9%E3%83%BC%E5%87%A6%E7%90%86%E3%80%9C/"/>
    <id>https://i.botch.me/2020/06/09/CleanCode読書記録〜エラー処理〜/</id>
    <published>2020-06-08T16:44:25.000Z</published>
    <updated>2020-06-08T17:17:36.088Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第7章を読んだメモ。</p><h2 id="エラー処理-第7章-の概要"><a href="#エラー処理-第7章-の概要" class="headerlink" title="エラー処理(第7章)の概要"></a>エラー処理(第7章)の概要</h2><ul><li>リターンコードではなく例外を使用する。<ul><li>処理の関心ごとと、エラー処理が分かれるから良いよ。</li><li>リターンコードは、呼び出し側が雑然としてしまい、忘れやすいよ。</li></ul></li><li>最初にtry-catch-finallyを書く<ul><li>例外をthrowする可能性のあるコードを書くときにtry-catch-finalyから書くのは良い習慣。</li><li>TDDを使って必要なロジックを実装することが可能になる。</li><li>tryブロックによるトランザクションスコープを作成できる。</li></ul></li><li>非チェック例外を使用する<ul><li>チェック例外は利点もあるが、ソフトウェア開発に必ずしも必要なものではない。<ul><li>C#やC++,Python,Rubyにもない。</li></ul></li><li>チェック例外は代償としてOCPを失っている。<ul><li>キャッチとの間にあるメソッドのシグネチャすべてに例外を追加せねばならず、下層の変更がより高いレベルのシグネチャ変更を強制することになる。</li></ul></li><li>例外送出の連鎖に含まれるすべての関数が下層における例外の詳細を知る必要があるのはカプセル化が壊れている。</li><li>しかし、重要なライブラリを書く際などは有効。</li></ul></li><li>例外で状況を伝える<ul><li>十分な情報を持ったエラーメッセージを作成し、それを例外に含める。</li><li>スタックトレースなど、catchした場所でロギングを行うのに十分な情報を渡す。</li></ul></li><li>呼び出し元が必要とする例外クラスを定義する。<ul><li>いっぱいキャッチする必要があるなら、呼び出しているAPIをラップして共通の例外クラスを定義するようにする。</li><li>アプリケーションを特定のベンダのAPIの設計に依存させなくてすむ。</li><li>特定の領域のコードでは、1つの例外クラスを使用することが適している。</li></ul></li><li>正常ケースのフローを定義する<ul><li>例外による処理の中断を望まないケース(外部API的には例外だが、アプリケーションとしては例外として扱いたくない)</li><li>スペシャルケースパターンを利用する。(返り値を本来のreturnと整合性のとれたオブジェクトにする)</li></ul></li><li>nullを返さない<ul><li>ほぼ1行おきにnullチェックをしているコードは数多くあるが、これらは問題のあるコード。</li><li>nullチェックを忘れれば制御不能となる。</li><li>スペシャルケースパターンを使え。</li></ul></li><li>nullを渡さない<ul><li>nullをメソッドに渡すのはnullを返さないよりよくない。</li><li>多くのプログラミング言語では呼び出し元からnullを渡された場合にうまく対処する方法がない。</li><li>理にかなった方法が、nullを渡すことを原則禁止にする。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>基本例外throw派なので、すごく同意。<br>チェック例外は結構好きな部類だったが、確かに上記の問題を含んでいるなと思った。<br>なんとなく使っていたコードにスペシャルケースパターンという名前が付いていたことには驚いた。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第7章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;エラー処理-第7章-の概要&quot;&gt;&lt;a href=&quot;#エラー処理-第7章-の概要&quot; class=&quot;headerlink&quot; title=&quot;エラー処理(第7章)の概要&quot;&gt;&lt;/a&gt;エ
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜オブジェクトとデータ構造〜</title>
    <link href="https://i.botch.me/2020/06/09/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0%E3%80%9C/"/>
    <id>https://i.botch.me/2020/06/09/CleanCode読書記録〜オブジェクトとデータ構造〜/</id>
    <published>2020-06-08T16:18:57.000Z</published>
    <updated>2020-06-08T16:43:45.439Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第6章を読んだメモ。</p><h2 id="オブジェクトとデータ構造-第6章-の概要"><a href="#オブジェクトとデータ構造-第6章-の概要" class="headerlink" title="オブジェクトとデータ構造(第6章)の概要"></a>オブジェクトとデータ構造(第6章)の概要</h2><ul><li>データ抽象化<ul><li>単に変数の層に関数を入れる(所謂getterとsetterを定義しただけの構造)は実装の隠蔽ではない。</li><li>抽象化しろ。</li><li>オブジェクトが保持するデータを最善の方法で表現するには、真剣に熟考する必要がある。</li><li>軽い気持ちでゲッタとセッタを用意するのは最悪</li></ul></li><li>データ/オブジェクトの非対称性<ul><li>データ構造<ul><li>データを公開し、意味を持った機能は何も提供しない。</li></ul></li><li>オブジェクト<ul><li>裏にあるデータを隠して抽象化し、データを操作する機能を提供する。</li></ul></li><li>手続き型(データ構造を使用するコード)<ul><li>新たな関数を既存のデータ構造に影響を与えず追加することができる。</li><li>新たなデータ構造を追加するには既存のすべての関数を変えなければいけない。</li></ul></li><li>OOP<ul><li>既存の関数に変更を加えることなく、新たなクラスを追加できる。</li><li>すべてのクラスを変える必要があるので新たな関数を追加することは難しくなる。</li></ul></li><li>複雑なシステムでは、新たなデータ型が増えることの方が多いのでOOPがうまく適合する。</li><li>逆にデータ型を追加するよりも、関数を追加することの方が多い場合は手続き型の方がよく適合する。</li><li>OOPが常に優れているわけではない。</li></ul></li><li>デメテルの法則<ul><li>呼び出しのチェインはやめようね。</li><li>クライアントコードがオブジェクトの構造を知っている状態になってしまうよ。</li><li>なんの振る舞いも持たなければ、デメテルの法則は適用されないよ。(データ構造)</li></ul></li><li>混血児<ul><li>データ構造とオブジェクトの混血児が生成されることがある。</li><li>新たな関数を追加することを困難にするだけでなく、データ構造の追加も困難になるからやめた方が良い。</li><li>腐った設計に陥っているサイン</li></ul></li><li>構造隠蔽<ul><li>必要なメソッドを定義して、その仕事をさせるようにして構造を隠蔽する。</li></ul></li><li>データ転送オブジェクト(DTO)<ul><li>データベース、ソケットから取得したメッセージのパッシングなどに便利</li><li>DBから読み込んだ生データを変換していく過程の最初の段階として使われる。</li><li>private変数がゲッターとセッターで操作して、なんとなくカプセル化しているように見える場合もあるが、その使用方法にはなんの利点もない。</li></ul></li><li>アクティブレコード<ul><li>DTOの特殊形態</li><li>ビジネスルールを持ったメソッドを追加しない方が良い。<ul><li>オブジェクトとデータ構造の混血児を作ることになるから</li></ul></li><li>アクティブレコードはデータ構造として扱い、ビジネスルールを持ったオブジェクトは別に用意する。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>混血児はたまに作ってしまうので気をつけようと思った。<br>あと、手続き型のメリットに触れられていてなるほどなと思った。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第6章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;オブジェクトとデータ構造-第6章-の概要&quot;&gt;&lt;a href=&quot;#オブジェクトとデータ構造-第6章-の概要&quot; class=&quot;headerlink&quot; title=&quot;オブジェクト
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜書式化〜</title>
    <link href="https://i.botch.me/2020/06/09/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E6%9B%B8%E5%BC%8F%E5%8C%96%E3%80%9C/"/>
    <id>https://i.botch.me/2020/06/09/CleanCode読書記録〜書式化〜/</id>
    <published>2020-06-08T15:35:26.000Z</published>
    <updated>2020-06-08T16:18:37.671Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第5章を読んだメモ。</p><h2 id="書式化-第5章-の概要"><a href="#書式化-第5章-の概要" class="headerlink" title="書式化(第5章)の概要"></a>書式化(第5章)の概要</h2><ul><li>書式化の目的<ul><li>コーディングスタイルのこと</li><li>宗教戦争と片付けるべきではない。</li><li>現在のちゃんと動くことよりも、将来的に役に立つ書式化の方が重要</li><li>保守用異性と拡張性に影響を与え続ける</li></ul></li><li>縦方向の書式化<ul><li>厳格に定義すべきものではない。</li><li>長いよりは短い方が良い。</li><li>最大でも200〜500行ぐらいが良い。</li></ul></li><li>新聞に例える<ul><li>新聞は一番上に見出しがきてどんな内容なのか明らか。</li><li>ソースコードも同じように名前が単純で説明的でなければいけない。</li><li>一番最初のファイルは高レベルの概念とアルゴリズムが書かれているべき。</li><li>ソースファイルの一番下は、最も低レベルの関数と詳細な記述がされている。</li><li>新聞は多くの記事で構成されている。</li><li>もしも新聞が、事実、日付、名前の無秩序な塊で構成されていたら誰も読まない。</li></ul></li><li>垂直概念分離性<ul><li>インポートの宣言、メソッド、パッケージ宣言などの間の改行を意識しなさい。</li><li>これらを垂直分離性と呼びます。</li></ul></li><li>垂直密度<ul><li>ソースコードの各行が強く関連している場合、垂直密度が高い。</li><li>垂直密度は各行の関連性を表す。</li><li>無駄なコメントや、改行で密接な関係を崩さない。</li></ul></li><li>垂直距離<ul><li>密接に関連した概念は垂直方向に近い距離に置く</li><li>相応な強い理由がなければ、関連を持った複数のファイルに分散するべきではない。<ul><li>protectedな変数を避けるべき理由の1つ</li></ul></li><li>変数を宣言するときは、その変数が使用される場所となるべく近い位置で行う。</li><li>ループ内で利用する変数は、ループ内もしくは、ループの直前で宣言する。</li><li>インスタンス変数はクラスの頭で宣言する。<ul><li>うまく設計されたクラスは、全てではないにしても多くのメソッドで利用されるため。</li><li>ハサミの規則はC++で採用されていることもあるが合理性はない。</li><li>メソッドとの間にメンバ変数宣言などがあると、これを見つけるのは困難。</li></ul></li><li>依存関数<ul><li>関数定義は、使われている場所のすぐ後に来ることが期待できる。</li><li>下に下に呼び出していくようにすればモジュール全体を読むのがはるかに簡単になる。</li></ul></li><li>概念の密接な関係<ul><li>使われているすぐ後でなくとも、シンタックスシュガーや、オーバーライドなどは共通の命名体系を持っており、概念上密接な関係になる。</li><li>そのため、近くに置いた方が良い。</li></ul></li></ul></li><li>垂直方向の並び順<ul><li>呼び出される側を下に置く。</li><li>モジュールのコードを高いレベルの概念から低いレベルへと流れるようにする。</li></ul></li><li>横方向の書式化<ul><li>行の幅はなるべく短くする。</li><li>昔の80文字ルールは独断が過ぎる。</li><li>100,120でも構わない。</li><li>Robert C. Martin的には120</li></ul></li><li>水平分離性と密度<ul><li>代入演算子の両脇にはスペースを置いて強調する。<ul><li>代入文は左辺と右辺が別々の概念なので。</li></ul></li><li>関数の括弧の間にはスペースを入れない。<ul><li>関数とその引数は強く関連しているため。</li></ul></li><li>演算子の優先順位を強調する目的でもスペースは使える。<ul><li><code>(vertical * horizon * heigth) / 2</code> よりも <code>(vertical*horizon*heigth) / 2</code> などの方が読みやすい場合がある。</li><li>ただし、多くのフォーマッターでは同じスペースを挿入する傾向がある。</li></ul></li></ul></li><li>水平方向の位置合わせ<ul><li>アクセス修飾子と変数名のスペースを統一にする的なあれ。</li><li>有用ではない。<ul><li>フォーマッターによって失われる。</li><li>代入演算子などを見逃しがち</li><li>型宣言などを見逃しがち</li></ul></li><li>位置合わせしなければならないほど大きいならファイルを分割した方が良さげ</li></ul></li><li>インデント<ul><li>インデントちゃんとしろ。<ul><li>人間には理解不能になる。</li></ul></li><li>いかに短い関数でインデント規則を破りたくなったとしてもインデントし直しましょう。</li></ul></li><li>ダミーのスコープ<ul><li>while文やfor文の本体がダミーの場合。</li><li>セミコロンを別の行にインデントされると、非常に確認しにくい。</li><li>なるべく避ける</li></ul></li><li>チーム規則<ul><li>自分の書式規則よりもチームの規則に従え</li><li>各人が好き勝手にコードを書いたかに見えるような状態は好ましくない。</li><li>好みと異なっても、チームのメンバーと決めたスタイルに従え。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>動くことよりも、書式化の方が重要と考えるあたり、さすがだなと思った。<br>縦方向の書式化については100行ぐらいが好ましいのではないかと個人的には思う。<br>あと、水平方向の位置合わせについては、テストコードのDataProviderは、位置合わせした方が圧倒的に読みやすいとは思う。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第5章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;書式化-第5章-の概要&quot;&gt;&lt;a href=&quot;#書式化-第5章-の概要&quot; class=&quot;headerlink&quot; title=&quot;書式化(第5章)の概要&quot;&gt;&lt;/a&gt;書式化(第5章
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode〜コメント〜</title>
    <link href="https://i.botch.me/2020/05/30/CleanCode%E3%80%9C%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%80%9C/"/>
    <id>https://i.botch.me/2020/05/30/CleanCode〜コメント〜/</id>
    <published>2020-05-30T04:52:28.000Z</published>
    <updated>2020-05-30T10:32:56.733Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第4章を読んだメモ。</p><h2 id="コメント-第4章-の概要"><a href="#コメント-第4章-の概要" class="headerlink" title="コメント(第4章)の概要"></a>コメント(第4章)の概要</h2><ul><li>コメント<ul><li>コメント自体は良いものではない。(必要悪)</li><li>コードでうまく表現するときに失敗したときに書く。</li></ul></li><li>コメントでダメなコードを取り繕うことはできない。<ul><li>コメントを書く前にコードで表現できないかを考える。</li></ul></li><li>自分自身をコードで説明する。<ul><li><code>(account.accessCount === 0) &amp;&amp; echo &#39;Hello&#39;;</code> よりも <code>(account.firstAccess()) &amp;&amp; echo &#39;Hello&#39;;</code> のようにコードで表現するようにしなさい。</li></ul></li><li>良いコメント<ul><li>まっとうなコメント<ul><li>著作権表示</li><li>ライセンスのリンク(ライセンス文章自体は入れない方が良い)</li><li>ドキュメントへの参照</li></ul></li><li>情報を与えるコメント<ul><li>複雑な正規表現に対してマッチする参考例など</li><li>抽象メソッドの返り値に関する説明(メソッド名で説明できない場合)</li></ul></li><li>意図の説明<ul><li><code>firebaseでは500件ごとにしかcreateできないので500件ごとに配列を分割する</code> など。</li></ul></li><li>明確化<ul><li>標準モジュールを利用していて自分でそのコードを改変できない状況の場合などに意図を明確化するために書く。</li><li><code>expect(a === b).toBe(true); // a === b</code> など</li></ul></li><li>結果に対する警告<ul><li><code>このfixtureは大規模なデータが入っている時の検証用のため、開発マシンの容量に余裕があるときに利用してください。</code> など</li></ul></li><li>todoコメント<ul><li>IDEがだいたい教えてくれる。</li><li>ただし、そのままにせず、検索して積極的に潰すこと。</li></ul></li><li>強調<ul><li>一見筋の通らないことを強調する目的で使う。</li><li><code>searchWord.trim()</code> に対して、<code>前後のスペースを除去しておかないと別のリストとして認識される</code>など</li></ul></li><li>公開APIにおけるJavadoc<ul><li>Javadocをきちんとメンテナンスする必要がある。</li><li>後述のよくないコメントの点に十分に留意して。</li></ul></li></ul></li><li>よくないコメント<ul><li>ぶつぶつ言う<ul><li>他のモジュールを調べなければ意味がわからないコメントは、情報伝達に失敗していて記憶領域の無駄</li></ul></li><li>冗長なコメント<ul><li>コードの処理内容を表している。<ul><li>コードを読んだ方が早い</li></ul></li><li>Javadocのプロパティの説明(プロパティ名を日本語で表しているだけ)</li></ul></li><li>誤解を招くコメント<ul><li>正確な内容を表していないコメントのこと。</li></ul></li><li>命令コメント<ul><li>全ての関数にJavadocを書けのようなルールは悪</li><li>このゴミには何の付加価値もなく、単にコードをわかりにくくし、潜在的な嘘と間違いを生産している(原文ママ)</li></ul></li><li>日誌コメント<ul><li><code>N年N月N日 〇〇をした</code> などのコメント</li><li>バージョン管理システムがないころの過去の遺物</li></ul></li><li>ノイズコメント<ul><li><code>コンストラクタ</code> などのすでに明らかなこと。</li><li><code>ちょっと休ませてほしい</code> など筆者の感情のみで何の意味も持たないコメント<ul><li>コメント書くぐらいなら休ませてほしいと思わないコードを書くことに労力を費やすべき</li></ul></li></ul></li><li>道導<ul><li><code>// Actions ////////////</code> のようなコメント</li><li>こういったコードの下にコードを集約させることにほとんど意味がない。</li><li>なるべく使い過ぎず、注目を集めるという効果が最大限のときに使うべき</li></ul></li><li>閉じ括弧コメント<ul><li>ブロックの最後に、ブロックがどのブロックで終わるのかを表すコメント <code>// endif</code> など</li><li>ゴミ</li><li>必要になるほどネストが深いことが問題。</li></ul></li><li>属性と署名<ul><li><code>Added by ytetsuro</code>などのコメント</li><li>バージョン管理システムでやれ</li></ul></li><li>コメントアウトされたコード<ul><li>コード自体をコメントアウトする行為</li><li>バージョン管理システムでやれ</li></ul></li><li>HTMLコメント<ul><li>読みづらい</li><li>ツールの問題</li></ul></li><li>非局所的なコメント<ul><li>この関数はAで呼び出されるはずなので <code>デフォルトの値は3</code> のような使われ方前提で書かれているコメント<ul><li>デフォルトならデフォルト値をメソッドに定義しろ</li></ul></li><li>いつクライアントコードが変更されるかなんてわからない</li></ul></li><li>多すぎる情報<ul><li>RFCのリンクと、該当箇所の引用などが記載されているコメント</li></ul></li><li>不明瞭なコメント<ul><li>コメントを読むのに説明が必要なコメント</li></ul></li><li>関数ヘッダ<ul><li>説明が不要なほど関数を短くしろ</li></ul></li><li>非公開のJavadoc<ul><li>公開しないならJavadocを書く必要がない。</li></ul></li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>とにかく、コメントが嫌いなようでこの章では <code>ゴミ</code> という直接的な表現が多く出てきた。<br>概ね納得だが、PHPやJSのような動的型付け言語ではIDEレベルでdocコメントを見ているのでその点は勘弁してほしいなと思った。(TypeScript や PHP 7.0 &lt; を使えばある程度解決するが。)<br>冗長なコメントについて、個人的にはプロパティ名に知らない英単語があった場合などは、どのような意味かが調べずともわかると言う点では一概に悪とは言えなさそうだと思った。(メンテナンスされるか？という点に関しては確かに本著の通りだとは思った。)</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第4章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;コメント-第4章-の概要&quot;&gt;&lt;a href=&quot;#コメント-第4章-の概要&quot; class=&quot;headerlink&quot; title=&quot;コメント(第4章)の概要&quot;&gt;&lt;/a&gt;コメント
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜関数〜</title>
    <link href="https://i.botch.me/2020/05/25/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E9%96%A2%E6%95%B0%E3%80%9C/"/>
    <id>https://i.botch.me/2020/05/25/CleanCode読書記録〜関数〜/</id>
    <published>2020-05-24T22:42:19.000Z</published>
    <updated>2020-05-25T14:57:21.206Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第3章を読んだメモ。</p><h2 id="関数-第3章-の概要"><a href="#関数-第3章-の概要" class="headerlink" title="関数(第3章)の概要"></a>関数(第3章)の概要</h2><ul><li>小さいこと<ul><li>関数内の処理は20行に達することがほとんどないように書きなさい。</li><li>ブロックとインデントも同じ<ul><li>インデントレベルは1つか2つまで</li></ul></li></ul></li><li>1つのことを行う<ul><li>TO節で表現して1つの抽象レベルが1つの単位</li><li>もう一つの方法は、その関数の実装の中から単なる言い換えではなく、別の関数を抽出できるか調べる。</li><li>抽象レベルを揃える。(抽象度が異なるものを混ぜない。)<ul><li>複数の抽象レベルのことを一緒に扱うと常に混乱を招く。</li></ul></li></ul></li><li>逓減規則<ul><li>コードは上から下へ物語のように読める必要がある。</li><li>TO節の並びとしてプログラムが読める必要がある。</li></ul></li><li>switch文(if elseを含む)<ul><li>小さくするのが難しいのであんまり使わないこと。</li><li>全く使わないのも難しいので、最下層の抽象Factoryに留めて、多態オブジェクトを生成し、継承の裏に隠されていれば許す。</li></ul></li><li>内容をよく表す名前を使う。<ul><li>小さな関数に内容をよく表す名前をつける。</li><li>長い名前は不可解なコメントよりも優れている。</li><li>名付けには十分時間をかける。</li><li>整合性を持ってつける。</li></ul></li><li>関数の引数<ul><li>3つはできれば避ける。</li><li>4つはよほどの理由がなければやめる。</li><li>引数に現れた時点で実装詳細について知る必要が出てくる。</li><li>テストの観点でも、組み合わせを網羅するテストケースが必要になるため大量の引数は大変。</li></ul></li><li>共通モナディック形式<ul><li>出力引数(本書の文脈の中では、引数のオブジェクトに対して何か代入することっぽい)は避ける。<ul><li>混乱を招くから。</li></ul></li></ul></li><li>フラグ引数<ul><li>booleanを引数に渡す関数は、2つ以上のことをしているからやめるべき。</li></ul></li><li>引数2つの関数<ul><li>1つの関数よりも理解しづらい。</li><li>順番を覚えるのも大変<ul><li><code>assertEquals(expected, actual)</code>も、テスティングFWによって順番がまちまちで学習する必要がある。</li></ul></li><li><code>new Point(0, 0)</code> などもあるので必ずしも悪というわけではない。</li><li>1つの引数に変換するための方法を知っておく必要はある。<ul><li>メンバ変数にする</li><li>クラスを分ける。</li></ul></li></ul></li><li>引数3つの関数<ul><li>2つよりも理解が難しい。<ul><li>順序</li><li>立ち止まりの問題</li><li>頭の中から追い出す問題</li></ul></li></ul></li><li>引数オブジェクト<ul><li>引数が増えるなら、引数をオブジェクトにすれば良いじゃない。</li></ul></li><li>引数リスト<ul><li>可変引数のこと。</li><li>引数が一つのList型であるものとして扱う。</li><li>3つ以上の引数の中で利用してたら誤り。(例: <code>bad(name, count, age, ...item)</code>)</li></ul></li><li>動詞とキーワード<ul><li>キーワードとして引数を表すとわかりやすくなる <code>getElement(id: string) -&gt; getElementById(id: string)</code></li><li>順番の問題もある程度軽減される <code>assertExpectedEqualsActual(expected: any, actual: any)</code></li></ul></li><li>副作用を避ける<ul><li>1つのことを行うルールを破ることになる。</li><li>出力引数は避ける。</li><li>自分自身の状態を変更すべき</li></ul></li><li>CQRS<ul><li>書き込み(プロパティの変更を含む)と読み込みを分ける。</li><li>混乱を招くから</li></ul></li><li>戻りコードよりも例外を好む<ul><li>エラーコードを返すのは微妙にCQRSに反している。<ul><li>呼び出し元がすぐにエラーを処理しなければいけないから。</li></ul></li></ul></li><li>try/catchブロックの分離<ul><li>tryブロックとcatchブロックの中身を関数として外に出してしまった方が良い。<ul><li>try/catch自体が、正常処理と以上処理が混ざって不格好だから</li></ul></li></ul></li><li>エラー処理も一つの処理<ul><li>エラー処理以外のことをさせない。</li><li>tryが関数内にあるなら、関数の一番最初の単語でなければならない。</li><li>catchやfinallyは最後でなければならない。</li></ul></li><li>Error.java依存性磁石<ul><li>エラーコードを返すEnumとかやめて。<ul><li>依存関係が壊れやすくなる。</li><li>コンパイルが必要な言語ならエラーを追加するたびにビルドが必要になるから。</li></ul></li><li>Exceptionの派生クラスをthrowして。</li></ul></li><li>DRY<ul><li>OOP,AOP,COP,構造化プログラミングなど様々なものが重複を取り除く試みをしている。</li><li>エドガー・ダイクストラの関数の出口と入口は1つにすべきというルールは関数が小さければあまり役に立たない。</li><li>gotoダメ絶対!</li></ul></li><li>なぜ関数をこのように書くのか<ul><li>コードを書くのは、執筆と同じようなもの。</li><li>最初はうまく構成されていない。</li><li>様々な遂行を行いテストコードを書いて洗練させる。</li><li>最後に出来上がったものはこの章の規則に従ったものになる。</li><li>最初から完璧なものができるわけではない。(そんなことは誰にもできない)</li></ul></li><li>結論<ul><li>話して聞かせるストーリーのように書け。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>以前と同じく、わかりみが深い。<br>また、知見としてエラー処理も一つの処理としてtryが最初でcatchが最後とかは、結構悩んでいたポイントなので目からウロコだった。<br>あと、直近でError.java依存性磁石は抱えていた問題だったので、なるほど例外クラスの派生クラスで良いのかというポイントも目からウロコだった。</p><p>DRYに関しては、かなり難しい。<br>同著者のCleanArchitectureでも記載されているように <code>偶然の一致</code> か <code>一致</code> を見分けるのがかなりセンスが必要になるなと感じた。</p><p>あと、コンストラクタの引数が結構増えがちだが、これもオブジェクトにして少なくすべきなのだろうか。。。<br>少なくすることは、技術的には可能だが、結局ネストが増えてデメテルの法則を破りそうでかなり難しいと感じた。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第3章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;関数-第3章-の概要&quot;&gt;&lt;a href=&quot;#関数-第3章-の概要&quot; class=&quot;headerlink&quot; title=&quot;関数(第3章)の概要&quot;&gt;&lt;/a&gt;関数(第3章)の概要
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCode読書記録〜意味のある名前〜</title>
    <link href="https://i.botch.me/2020/05/25/CleanCode%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2%E3%80%9C%E6%84%8F%E5%91%B3%E3%81%AE%E3%81%82%E3%82%8B%E5%90%8D%E5%89%8D%E3%80%9C/"/>
    <id>https://i.botch.me/2020/05/25/CleanCode読書記録〜意味のある名前〜/</id>
    <published>2020-05-24T22:42:06.000Z</published>
    <updated>2020-05-26T03:57:34.413Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第2章を読んだメモ。</p><h2 id="意味のある名前-第2章-の概要"><a href="#意味のある名前-第2章-の概要" class="headerlink" title="意味のある名前(第2章)の概要"></a>意味のある名前(第2章)の概要</h2><ul><li>意図が明確の名前にする。<ul><li>名前の解説が必要なら、その名前は明確ではない。</li><li>名前が意図を明確にしていなければ、コードが変更しづらくなる。</li></ul></li><li>偽情報を避ける<ul><li>Unixプラットフォームに使われている名称や、エンジニアが誤解しやすい名前を避ける<ul><li>hp,aix,scoなど</li><li>ListじゃないのにListなど</li></ul></li><li>似た概念に似たつづり<ul><li>HyperTextMarkupLanguageForEffectVanilaとHyperTextMarkupLanguageForEffectJqueryなど。</li><li>違いに気づくのに時間がかかる。</li><li>自動コード補完の時にも使いにくくなる。</li></ul></li></ul></li><li>意味のある対比を行う<ul><li>ノイズワードを利用しない<ul><li>a1などの意味のわからない名前を利用しない。</li><li>InfoやDataなどの意味のある単語を利用しない。</li><li>variable変数など</li></ul></li><li>読み手に違いが明確に伝わる名前を利用する。<ul><li>getAccount(), getAccounts(), getAccountInfo() などの名称だとどれを使えば良いかわかりづらい</li></ul></li></ul></li><li>発音可能な名前をつける<ul><li>造語を利用しない<ul><li>覚えづらいから</li></ul></li><li>その名前のコードについて話しているときマヌケっぽい。</li></ul></li><li>検索可能な名前をつける<ul><li>1文字の名前は間違いなく検索できない</li><li>マジックナンバーも検索できない。</li></ul></li><li>エンコーディングを避ける<ul><li>ミスタイプの原因になる。</li><li>発音可能なことはまずない</li><li>精神的苦痛以外の何物でもない</li></ul></li><li>ハンガリアン記法<ul><li>現代の記法じゃない。<ul><li>コンパイラが型をチェックしてくれない時代の産物</li></ul></li></ul></li><li>メンバープレフィックス<ul><li><code>m_</code>でメンバ変数を表すなど<ul><li>IDEで色分けしたら解決する問題なので、古い。</li></ul></li></ul></li><li>インターフェースと実装<ul><li>インターフェースにIRepositoryのようなプレフィックスをつけない。<ul><li>散漫・情報過多</li></ul></li><li>どうしてもつける場合は具象クラスにRepositoryImplなどの方が良い。</li></ul></li><li>メンタルマッピングを避ける<ul><li>1文字の変数名をやめろ<ul><li>forのiとかjは慣習なのとスコープが狭いから見逃しているだけ(l(小文字のエル)だけは許さない)</li></ul></li></ul></li><li>クラス名<ul><li>名詞で書く。動詞では書かない。</li><li>Manager, Info, Data, Processerとかは避ける。</li></ul></li><li>メソッド名<ul><li>動詞で書く。</li><li>Javaビーンズによれば、先頭に <code>get</code>, <code>set</code>, <code>is</code> のいずれかを置くと良い。</li><li>コンストラクタのオーバーロードは、staticでFactoryを書いて、名前に引数を定義する。(DataModel.formFirebaseSnapshotなど)<ul><li>もちろんConstructorで定義できるのが一番良い。</li></ul></li></ul></li><li>気取らない<ul><li>ウィットに富んだ名前をつけない(abort -&gt; eatMyShorts{私のパンツを食べろ}のような)</li><li>面白さより明確さの方が価値があるから</li></ul></li><li>1つのコンセプトには1つの概念<ul><li>get, fetchなどが同じ意味で使われないようにする。(以下のように使い分ける)<ul><li>get -&gt; 通信をせず、オブジェクトをストレージなどの内部リソースから取得</li><li>fetch -&gt; 通信して取ってくるリソースを取ってくる</li></ul></li><li>語彙集があると重宝する。</li></ul></li><li>語呂合わせをしない<ul><li><code>add</code>が2つのものを連結する目的で多くの箇所へ実装されていた場合に、コレクションに要素を追加するメソッドに対して <code>add</code> と名付けない。<code>append</code>や<code>insert</code>などの命名にする。</li></ul></li><li>解決領域の用語の使用<ul><li>何でもかんでも、解決領域から命名すれば良いってもんじゃない。<ul><li>JobQueueなどのプログラマがわかる命名をつけるのは決して誤りではない。</li><li>無理やり業務用語から持ってくる必要はない。</li><li>いちいち業務用語の意味をお客さんやプロダクトオーナーに聞き取らなくてはならないから。</li></ul></li></ul></li><li>問題領域の用語を使用<ul><li>処理内容がプログラマチックでない場合は、問題領域から名前を取ってくると良い。</li><li>解決領域と問題領域を切り分けるのは優れたプログラマや、設計者の仕事の一つ</li></ul></li><li>意味のある文脈を加える<ul><li>メソッドを細かく分割し、その中に変数を定義するなどでどの文脈なのかを明確にした方が良い。</li></ul></li><li>根拠のない文脈を避ける<ul><li>メールマガジンシステムでネームスペースMailingList内にメールアドレスを表すクラスを作る時に、AccountAddressなどは避けた方が良い。<ul><li>MailingListネームスペースの中にあるからAddressでメールアドレスの意味は伝わるはず。</li><li>アカウントのアドレス以外何が存在するのか。</li></ul></li></ul></li><li>最後に<ul><li>Robert C. Martinは名前の変更は感謝しつつ受け入れている。</li><li>名前の変更は、結果はすぐに現れて長い期間効果を発揮する。</li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>わかりみが深い。<br>ただ、クラス名の必ず動詞つける部分は概ね同意だが、以下のようにCleanArchitectureでUseCaseを動詞にするのはままありそうなので、 <code>根拠のない文脈を避ける</code> と、 <code>Class名</code> はコンフリクトすることはありそう。</p><p>参考: <a href="https://github.com/android10/Android-CleanArchitecture/issues/258" target="_blank" rel="noopener">https://github.com/android10/Android-CleanArchitecture/issues/258</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第2章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;意味のある名前-第2章-の概要&quot;&gt;&lt;a href=&quot;#意味のある名前-第2章-の概要&quot; class=&quot;headerlink&quot; title=&quot;意味のある名前(第2章)の概要&quot;
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>CleanCodeの読書記録-CleanCode-</title>
    <link href="https://i.botch.me/2020/05/17/CleanCode%E3%81%AE%E8%AA%AD%E6%9B%B8%E8%A8%98%E9%8C%B2-CleanCode/"/>
    <id>https://i.botch.me/2020/05/17/CleanCodeの読書記録-CleanCode/</id>
    <published>2020-05-17T09:18:11.000Z</published>
    <updated>2020-05-17T12:25:21.561Z</updated>
    
    <content type="html"><![CDATA[<p>Robert C. Martin先生のCleanCode第1章を読んだメモ。</p><h2 id="CleanCode-第1章-の概要"><a href="#CleanCode-第1章-の概要" class="headerlink" title="CleanCode(第1章)の概要"></a>CleanCode(第1章)の概要</h2><p>CleanCode(第1章)の概要は以下でした。</p><ul><li>この本を読む必要性<ul><li>プログラマだから</li><li>より良いコードが書きたいと思っているから</li></ul></li><li>この本を読んだ後できるようになること<ul><li>良いコードと悪いコードの判別</li><li>良いコードの書き方の理解</li></ul></li><li>コードの書き方に関する必要性<ul><li>「仕様を元にコードが自動生成される時代が来るからコードの書き方はどうでもいい」とか馬鹿馬鹿しい。<ul><li>それができたら、その自動生成される元の仕様がそもそもコードみたいに形式化されてるから、それはもうそれ自体がコード。</li></ul></li></ul></li><li>粗悪なコードの事例<ul><li>粗悪なコードによりメンテ不可能になって、会社を廃業に追いやった事例もある。<ul><li>粗悪なコードによる深刻な障害をwading(ウェーディング)という。</li></ul></li><li>後で修正しようはやらない。(レブランの法則)</li><li>粗悪なコード環境で生産性はどんどん0に近づいていき、管理者も人的リソースを増やして解決しようとし負のスパイラルが生まれる。</li><li>それらを解決するためのチームを作り、リファクタリングに10年続いた例をみたことがある。</li><li>つまり、コードを洗練することは、コスト効率だけではなく生き残っていくのに必要。</li></ul></li><li>粗悪なコードの原因<ul><li>マネージャでも客でも、マーケティングタイプでもなく、我々自身。</li><li>PMはスケジュールを守るため、作業を手伝うべく我々をみている。</li><li>PMも粗悪なコードを望んでいるわけではない。</li><li>PMの仕事はスケジュールと要件を守ること。</li><li>PMの熱意に負けないぐらい熱意を持ってコードを書こう。</li><li>混乱を招くリスクを理解していない管理者に負けるならプロのプログラマじゃない。</li></ul></li><li>綺麗なコードを書くためには<ul><li>良いコードと悪いコードの判別ができれば書けるわけではない。</li><li>骨身を惜しまずに獲得したコードのセンスが必要</li><li>無数の小さな手法が必要</li><li>規律を守ることが必要</li></ul></li><li>綺麗なコードとは(定義がエンジニアの数だけあるので、著名なエンジニアの意見を紹介)<ul><li>ビャーネ・ストラウストラップ(C++の生みの親)<ul><li>1つのことをうまくやる(SRP)</li><li>壊れた窓の誘惑に惑わされない</li><li>エラー処理が完全でなければならない。</li></ul></li><li>グラディ・ブーチ<ul><li>設計者の意図をないがしろにしない。</li><li>明快な抽象化がされている。(ISP)</li><li>まっすぐな境界が引かれている。</li></ul></li><li>デイブ・トーマス<ul><li>原作者以外の人にも読むことができる。<ul><li>文芸的</li><li>依存性が最低限</li><li>明朗で最低限なAPIのみ提供している。</li></ul></li><li>変更が容易なコードではなく、拡張が容易。(OCP)</li><li>単体テストと受け入れテストがある。<ul><li>テストがないコードが洗練されているとは言えない。</li></ul></li></ul></li><li>マイケル・フェザース<ul><li>コードが単純</li><li>秩序が保たれている。</li><li>細部に対して気配りがされている。</li></ul></li><li>ロン・ジェフェリー<ul><li>全てのテストを実行する</li><li>重複がない</li><li>システムのあらゆる設計知識が表現されている</li><li>クラス、メソッド、関数といったものの数が最小限</li></ul></li><li>ワード・カンニズム<ul><li>自分の予想を上回っているコード<ul><li>読んでいて驚きがない。</li><li>読むのに努力が必要ないため。</li></ul></li></ul></li><li>Robert C. Martin<ul><li>どれも絶対的に正しいように説明はするが、絶対的に正しいわけではない。</li><li>この本以外にも専門家技術について主張を行う人はいるのでそれらの意見も必要。</li><li>ただ、我々の考える専門職としてふさわしい綺麗なコードを紹介はするから、目を背けず関心を持って欲しい。</li></ul></li></ul></li><li>コードを満足に書くには<ul><li>読みやすくしなさい。<ul><li>読むと書くの比率は10:1以上</li><li>読みやすくする = 書きやすくするである。</li></ul></li><li>コードを何度も洗練しなければいけない。<ul><li>ボーイスカウトルールを採用する</li></ul></li></ul></li></ul><h2 id="感じたこと"><a href="#感じたこと" class="headerlink" title="感じたこと"></a>感じたこと</h2><p>粗悪なコードの原因については、胸が痛い。<br>ただ、個人的にも粗悪なコードのリスクを説得できる手法が必要だなと思った。<br>個人的に以下を試してみたことはあるが、あまり理解が得られなかった。</p><ul><li>メトリクスの計測(メンテナンス性が世間一般から見てこれだけ低いなどの説明)</li><li>現在の実装により、現在このような機能が開発できないなどの説明。</li></ul><p>そのため、本書にもあるようにコスト効率を計測するのが数字で示せて一番てっとり早いかもしれない。<br>コスト効率の計測のためには、現在の実装の修正に関する既存のコードの調査、修正時間を計測する必要があり、修正の着手に漕ぎ着けるまでには結構な根気が必要になる場面もありそうだなと思った。</p><p>綺麗なコードの定義については概ね同意できるが、「重複がない」については若干、懐疑的。<br>重複については、偶然の一致(Robert C. Martin氏のCleanArchitecture参照)もあるので重複が必ずしも悪であるというわけではないというのが、自論としてある。<br>ここについては、彼の言う重複の意図の詳細を知る必要がありそう。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Robert C. Martin先生のCleanCode第1章を読んだメモ。&lt;/p&gt;
&lt;h2 id=&quot;CleanCode-第1章-の概要&quot;&gt;&lt;a href=&quot;#CleanCode-第1章-の概要&quot; class=&quot;headerlink&quot; title=&quot;CleanCode(第1
      
    
    </summary>
    
    
      <category term="読書メモ" scheme="https://i.botch.me/tags/%E8%AA%AD%E6%9B%B8%E3%83%A1%E3%83%A2/"/>
    
      <category term="CleanCode" scheme="https://i.botch.me/tags/CleanCode/"/>
    
  </entry>
  
  <entry>
    <title>dhashを生成するライブラリ作った</title>
    <link href="https://i.botch.me/2019/08/26/dhash%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E4%BD%9C%E3%81%A3%E3%81%9F/"/>
    <id>https://i.botch.me/2019/08/26/dhashを生成するライブラリ作った/</id>
    <published>2019-08-25T15:55:26.000Z</published>
    <updated>2019-08-26T14:34:20.700Z</updated>
    
    <content type="html"><![CDATA[<h1 id="dhashを生成するライブラリ作った"><a href="#dhashを生成するライブラリ作った" class="headerlink" title="dhashを生成するライブラリ作った"></a>dhashを生成するライブラリ作った</h1><p>先日参加した勉強会でdhashというものを初めて知りました。</p><ul><li><a href="https://nfug.connpass.com/" target="_blank" rel="noopener">参加した勉強会</a></li><li><a href="http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html" target="_blank" rel="noopener">dhash</a></li><li><a href="https://github.com/ytetsuro/image-hash" target="_blank" rel="noopener">作ったリポジトリ</a></li></ul><p>読んでみると、すごく簡単なアルゴリズムで画像の類似度を計算していたので「これ、JSで書けるんじゃない？」と気軽に思ったのが地獄の始まりでした。</p><h2 id="dhashってなに？"><a href="#dhashってなに？" class="headerlink" title="dhashってなに？"></a>dhashってなに？</h2><p>画像の類似度を計算するためのハッシュアルゴリズムです。<br>特徴としては以下のようです。</p><ul><li>誤検知がほとんどない</li><li>高速(aHashやpHashと比べて)</li></ul><h3 id="なぜ作ろうと思ったのか"><a href="#なぜ作ろうと思ったのか" class="headerlink" title="なぜ作ろうと思ったのか"></a>なぜ作ろうと思ったのか</h3><ul><li>InstagramとかGoogle Photoみたいなサービスで、すでに登録してある画像を機械学習などを利用せずに検出できるだけでもメリットとしては大きそう。</li><li>また、類似度計算の性質上、バリデーションの用途で使われやすそうだがほとんどがバックエンド用で書かれているため、フロントエンド用に書けば通信のコスト減らせるくない？と思った。</li></ul><h2 id="dhashってどう作るの？"><a href="#dhashってどう作るの？" class="headerlink" title="dhashってどう作るの？"></a>dhashってどう作るの？</h2><p>dhashの生成手順は以下です。</p><ol><li>画像を9x8にリサイズします。</li><li>グレースケールに変換します。</li><li>右隣と比較して現在の値が小さければ1、それ以外は0として扱います。(右端は計算しない)</li><li>bitとして扱います。</li></ol><p><a href="https://tech.unifa-e.com/entry/2017/11/27/111546" target="_blank" rel="noopener">日本語の詳しい内容</a>もあって、アルゴリズムの概要自体はとてもわかりやすかったです。<br>この生成手順は<a href="http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html" target="_blank" rel="noopener">情報元？</a>を参考にしたものだが、実際に実装してみたらそんなに簡単にはいきませんでした。</p><h2 id="ハマったポイント"><a href="#ハマったポイント" class="headerlink" title="ハマったポイント"></a>ハマったポイント</h2><p>ハマったポイントは以下です。</p><ol><li>JSで64bitは簡単に扱えないよ</li><li>リサイズアルゴリズムが不明だよ</li><li>グレースケールアルゴリズムが不明だよ</li></ol><h3 id="JSで64bitを簡単には扱えないよ"><a href="#JSで64bitを簡単には扱えないよ" class="headerlink" title="JSで64bitを簡単には扱えないよ"></a>JSで64bitを簡単には扱えないよ</h3><p>JSでよくある2進数 -&gt; 16進数の変換方法が以下です。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'1'</span>, <span class="number">2</span>).toString(<span class="number">16</span>);</span><br></pre></td></tr></table></figure><p>おわかりかもしれませんが、一旦Number型にしているので大きな数値を扱う時は桁が溢れて違う数値になります。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 40000000000000</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'111111111111111111111111111111111111111111111111111111'</span>, <span class="number">2</span>).toString(<span class="number">16</span>);</span><br></pre></td></tr></table></figure><p>これは、4文字づつの配列にして16進数に変換するようにしました。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> array = <span class="string">'111111111111111111111111111111111111111111111111111111'</span>;</span><br><span class="line"><span class="keyword">const</span> chunk = <span class="number">4</span>;</span><br><span class="line"><span class="keyword">const</span> chunkedArray = [...Array(<span class="built_in">Math</span>.ceil(array.length / chunk)).keys()]</span><br><span class="line">    .map(<span class="function"><span class="params">index</span> =&gt;</span> array.slice(index * chunk, (index * chunk) + chunk));</span><br><span class="line"><span class="built_in">console</span>.log(chunkedArray.map(<span class="function"><span class="params">row</span> =&gt;</span> <span class="built_in">parseInt</span>(row.join(<span class="string">''</span>), <span class="number">2</span>).toString(<span class="number">16</span>)).join(<span class="string">''</span>));</span><br></pre></td></tr></table></figure><h3 id="リサイズアルゴリズムが不明だよ"><a href="#リサイズアルゴリズムが不明だよ" class="headerlink" title="リサイズアルゴリズムが不明だよ"></a>リサイズアルゴリズムが不明だよ</h3><p>世の中には、リサイズアルゴリズムが複数あります。<br>PythonのPillowでサポートされているアルゴリズムでも<a href="https://pillow.readthedocs.io/en/4.0.x/handbook/concepts.html#filters" target="_blank" rel="noopener">6種類</a>あります。<br>どれかわかりませんでしたが、結果としてはLanczosにしました。<br>ライブラリとして実装する上で、他のライブラリとハッシュ値が異なると非常に使いにくいものになってしまいます。</p><p>私のアプローチは以下です。</p><ol><li>何も考えずブラウザにリサイズさせてみる。</li><li>ライブラリ探す。</li><li>JSから逃げる。</li><li>リサイズアルゴリズムを自前で実装する。</li><li>ライブラリ探す。(再度)</li></ol><h4 id="ブラウザにリサイズさせてみる。"><a href="#ブラウザにリサイズさせてみる。" class="headerlink" title="ブラウザにリサイズさせてみる。"></a>ブラウザにリサイズさせてみる。</h4><p>ブラウザで以下のように実装しました。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> image = <span class="keyword">new</span> Image();</span><br><span class="line"><span class="keyword">const</span> canvas = <span class="built_in">document</span>.createElement(<span class="string">'canvas'</span>);</span><br><span class="line">image.width = <span class="number">9</span>;</span><br><span class="line">image.height = <span class="number">8</span>;</span><br><span class="line">image.onload = <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line"><span class="keyword">const</span> ctx = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line">ctx.drawImage(image, <span class="number">0</span>, <span class="number">0</span>, image.width, image.height);</span><br><span class="line"><span class="keyword">const</span> colorMap = ctx.getImageData(<span class="number">0</span>, <span class="number">0</span>, image.width, image.height);</span><br><span class="line">&#125;;</span><br><span class="line">image.src = <span class="string">'./test.png'</span>;</span><br></pre></td></tr></table></figure><p>似たようなハッシュ値は確かに取れますが、ブラウザによって結果が変わります。<br>これは、drawImageの第7,8引数でリサイズしても、一緒です。<br>変わるとしたらCSSの <code>image-rendering</code> プロパティで多少の違いはあれど、意図した結果は得られませんでした。</p><h4 id="ライブラリを探す。"><a href="#ライブラリを探す。" class="headerlink" title="ライブラリを探す。"></a>ライブラリを探す。</h4><p>まずは、ハッシュ値が他のライブラリと異なっては困るので、Pythonの<a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a>を読むところから始めました。<br>ここで<br><a href="https://github.com/JohannesBuchner/imagehash/blob/master/imagehash/__init__.py#L206" target="_blank" rel="noopener">imagehashのdhashの実装を見て</a>初めて <code>Lanczos</code> という補正アルゴリズムでリサイズされているか知りました。<br>どうやら Pillowでは <code>ANTIALIAS</code> -&gt; <code>Lanczos</code> のようです。<br>そのため<a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a>では <code>Lanczos</code> を利用していることが伺えます。</p><p>JSでは<code>Lanczos</code>をサポートしているライブラリとして<code>fabric</code>というものが見つかったので試してみましたが、意図した結果は得られませんでした。<br>そもそも <code>Lanczos</code> とはどんなアルゴリズムなのか知る必要があると思い<a href="http://www.maroon.dti.ne.jp/twist/4C616E637A6F73B4D8BFF4A4CBA4E8A4EBB2E8C1FCA4CEB3C8C2E7BDCCBEAE.html" target="_blank" rel="noopener">このサイト</a>を読みました。</p><blockquote><p>調べていく中で、ソースが付属しているフリーソフトなどを見かける機会もあったが、座標（距離）の小数部を丁寧に処理していないために、Lanczos関数の値が正確に計算できていない物もあった。<br>たとえLanczos関数を使ったとしても、距離の計算が雑では高品質な画像は得られない。</p></blockquote><p>とあり、なるほど。<br>浮動小数点に弱いJSで頑張ろうとするあたりがダメなんじゃないか？<br>と思うようになりました。</p><h4 id="JSから逃げる。"><a href="#JSから逃げる。" class="headerlink" title="JSから逃げる。"></a>JSから逃げる。</h4><p>次のアプローチとして、JSが苦手な土俵をわざわざJSで戦う必要はないなと思ったので、WebAssemblyを使おうと思いました。<br>今回は<a href="https://github.com/KnicKnic/WASM-ImageMagick" target="_blank" rel="noopener">ImageMagickのWebAssemblyのプロジェクト</a>を利用することにしました。</p><p>意図通りの結果を得ることはできました。🎉<br>ブラウザ上でImageMagickが動くことに感動しました。OSSに感謝。<br>しかし、以下が問題です。</p><ol><li><a href="https://github.com/KnicKnic/WASM-ImageMagick#below-examples-need-additional-files-coppied" target="_blank" rel="noopener">wasmとjsファイルをコピーする必要がある。</a></li><li>バグっている。(ちょっと書き換えれば治る。issue作成済み。)</li></ol><h4 id="リサイズアルゴリズムを自前で実装する。"><a href="#リサイズアルゴリズムを自前で実装する。" class="headerlink" title="リサイズアルゴリズムを自前で実装する。"></a>リサイズアルゴリズムを自前で実装する。</h4><p>TypeScriptにはbigintもあるし、浮動小数点を扱うためのライブラリも数多く出ているので、それらを使ってリサイズしてみることにしました。<br>ちなみに、<a href="https://basarat.gitbooks.io/typescript/docs/javascript/number.html#bigjs" target="_blank" rel="noopener">TypeScript DeepDiveにも書いてある</a>通りパフォーマンスに影響を及ぼす可能性が高いが、この際正しい結果が得られるのであれば無視します( 過程や…！ 方法なぞ…！ どうでもよいのだァーッ)。<br>このあたりで、正直自分の思考が正気ではないのを感じましたが、後には引けない(お盆を全て潰して娘が泣いている隣でコードを書く)状況でしたので諦めませんでした。</p><p><a href="https://github.com/ytetsuro/image-hash/blob/master/src/HashableImageSourceConverter/Resizer/VanilaLanczosResizer.ts" target="_blank" rel="noopener">頑張って書いた</a>結果、意図通りの結果を得ることはできませんでした。</p><p>また、256x256を9x8にリサイズするので12sもかかる有様でした。</p><h4 id="ライブラリを探す。-1"><a href="#ライブラリを探す。-1" class="headerlink" title="ライブラリを探す。"></a>ライブラリを探す。</h4><p>一つ前のステップでリサイズアルゴリズムを実装して思ったのが、JSで <code>Lanczos</code> と謳われているものはほとんど同じようなコードでした。<br>その点を踏まえてGitHubで <code>Lanczos</code> で検索して片っ端から自分の実装と似ていないライブラリを探しているとStar数やFork数は少ないが<a href="https://github.com/rgba-image/lanczos" target="_blank" rel="noopener">自分とは異なる方法でLanczosのリサイズ</a>を実装しているライブラリを見つけました。</p><p>このライブラリで、実装してみた結果意図通りの結果を得ることができました。<br>しかも、高速で256x256を9x8で60msぐらいでリサイズすることができました。</p><p>正直、Lanczosでこの速度はcrazyだと思います。<br>なぜ、こんなに速いのか、なぜ正しい結果が得られるのかは後でちゃんとロジックを読んでみようと思います。<br>OSSに感謝。</p><h3 id="グレースケールアルゴリズムが不明だよ"><a href="#グレースケールアルゴリズムが不明だよ" class="headerlink" title="グレースケールアルゴリズムが不明だよ"></a>グレースケールアルゴリズムが不明だよ</h3><p>グレースケールもリサイズと同じでいっぱいありました。</p><ul><li><a href="https://developer.mozilla.org/ja/docs/Web/Guide/HTML/Canvas_tutorial/Pixel_manipulation_with_canvas#Grayscaling_and_inverting_colors" target="_blank" rel="noopener">RGB平均</a></li><li><a href="https://qiita.com/yoya/items/96c36b069e74398796f3#itu-r-rec-bt601" target="_blank" rel="noopener">ITU-R Rec BT.601</a></li><li><a href="https://qiita.com/yoya/items/96c36b069e74398796f3#hsl-%E3%81%AE-l-rgb-%E3%81%AE%E6%9C%80%E5%A4%A7%E3%81%A8%E6%9C%80%E5%B0%8F%E3%81%AE%E4%B8%AD%E9%96%93" target="_blank" rel="noopener">中央値とるやつ</a></li><li><a href="https://ofo.jp/osakana/cgtips/grayscale.phtml#id2" target="_blank" rel="noopener">NTSC Coef</a></li></ul><p>Pythonの<a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a>を参考にすると<a href="https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert" target="_blank" rel="noopener">ITU-R 601-2 luma</a>となっていました。</p><p>ご丁寧に計算式までドキュメントに記載されていますのでこのまま流用しました。</p><h3 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h3><p>dhashを作りたい時は以下の手順で作ります。</p><ol><li>画像を9x8にリサイズします。(Lanczos lobs 3)</li><li>グレースケールに変換します。(ITU-R 601-2 luma)</li><li>右隣と比較して現在の値が小さければ1、それ以外は0として扱います。(右端は計算しない)</li><li>bitとして扱います。</li></ol><p>また、この情報は、とりあえず<a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a>を参考にしただけで別の正しいdhashの仕様とかあれば教えてください。<br>余談ですが、<a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a>で生成したハッシュは<a href="http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html" target="_blank" rel="noopener">情報元？</a>の画像をハッシュ化しても同じハッシュ値にはなりませんでした。😭</p><p>あと、<a href="https://nfug.connpass.com/" target="_blank" rel="noopener">こんな感じの新しい知見に触れられる勉強会はNFUG</a>です。</p><h4 id="良かったこと"><a href="#良かったこと" class="headerlink" title="良かったこと"></a>良かったこと</h4><ul><li>画像縮小アルゴリズムやグレースケールのアルゴリズムとかが勉強になった。</li><li>途中でJSをやめてWebAssemblyに逃げたのは良いアプローチだと思った。</li><li>難しいアルゴリズムでも実装してみれば割と理解が深まると思った。</li></ul><h4 id="良くなかったこと"><a href="#良くなかったこと" class="headerlink" title="良くなかったこと"></a>良くなかったこと</h4><ul><li>指針を決めずに作り始めたので、どのアルゴリズムにするかの判断が遅くなった。</li><li>openにしたくないモジュールまでopenにしてしまっている。</li><li>ロジックを理解していないままライブラリを使った。</li><li>Canvasのテストがめんどくさすぎてテストが雑になってしまった。</li></ul><h4 id="次にやりたいこと"><a href="#次にやりたいこと" class="headerlink" title="次にやりたいこと"></a>次にやりたいこと</h4><ul><li>ライブラリのロジックを理解する。</li><li>WebGLでLanczosをサポートしてそうなのでやってみる。</li><li>WebAssemblyをつかって何か書いてみる。</li></ul><h3 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h3><ul><li><a href="https://nfug.connpass.com/" target="_blank" rel="noopener">NFUG</a></li><li><a href="http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html" target="_blank" rel="noopener">dhash</a></li><li><a href="https://github.com/JohannesBuchner/imagehash" target="_blank" rel="noopener">imagehash</a></li><li><a href="https://github.com/KnicKnic/WASM-ImageMagick" target="_blank" rel="noopener">WASM-ImageMagick</a></li><li><a href="https://github.com/rgba-image/lanczos" target="_blank" rel="noopener">lanczos</a></li><li><a href="https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert" target="_blank" rel="noopener">ITU-R 601-2</a></li><li><a href="https://tech.unifa-e.com/entry/2017/11/27/111546" target="_blank" rel="noopener">日本語のdhashの説明</a></li><li><a href="https://ofo.jp/osakana/cgtips/grayscale.phtml" target="_blank" rel="noopener">グレースケールについて</a></li><li><a href="http://www.maroon.dti.ne.jp/twist/4C616E637A6F73B4D8BFF4A4CBA4E8A4EBB2E8C1FCA4CEB3C8C2E7BDCCBEAE.html" target="_blank" rel="noopener">Lanczosのリサイズについて</a></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h1 id=&quot;dhashを生成するライブラリ作った&quot;&gt;&lt;a href=&quot;#dhashを生成するライブラリ作った&quot; class=&quot;headerlink&quot; title=&quot;dhashを生成するライブラリ作った&quot;&gt;&lt;/a&gt;dhashを生成するライブラリ作った&lt;/h1&gt;&lt;p&gt;先日参加した
      
    
    </summary>
    
    
      <category term="TypeScirpt" scheme="https://i.botch.me/tags/TypeScirpt/"/>
    
      <category term="image-hash" scheme="https://i.botch.me/tags/image-hash/"/>
    
      <category term="dhash" scheme="https://i.botch.me/tags/dhash/"/>
    
  </entry>
  
  <entry>
    <title>ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Modelの実装</title>
    <link href="https://i.botch.me/2018/12/04/ci-phpunit-test%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%84%E3%81%A6%E6%AC%B2%E3%81%97%E3%81%84CodeIgniter3%E3%81%AE%E5%AE%9F%E8%A3%85-Model%E3%81%AE%E5%AE%9F%E8%A3%85/"/>
    <id>https://i.botch.me/2018/12/04/ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Modelの実装/</id>
    <published>2018-12-03T16:00:39.000Z</published>
    <updated>2018-12-03T16:00:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。<br>私は、その説明が非常にめんどくさいと感じているのでここに記載します。</p><p>知っておいて欲しい実装は次の4つです。</p><ul><li>Controllerの実装(設計)</li><li>Controllerの実装(コンストラクタ)</li><li>依存性解決の実装</li><li><strong>Modelの実装</strong> &lt;- イマココ</li></ul><h2 id="Modelの実装"><a href="#Modelの実装" class="headerlink" title="Modelの実装"></a>Modelの実装</h2><p>CodeIgniterのModelの実装は次の通りです。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CI_Model</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__get</span><span class="params">($key)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> get_instance()-&gt;$key;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>本当に、これだけです。</p><p>マジックメソッドの<code>__get</code>でプロパティが存在しない場合はControllerインスタンスのプロパティを利用する様にしています。<br>ここで重要になるのがマジックメソッドの<code>__get</code>は<strong>プロパティが存在しない場合</strong>に動きます。</p><h3 id="テスト時に知っておきたいこと"><a href="#テスト時に知っておきたいこと" class="headerlink" title="テスト時に知っておきたいこと"></a>テスト時に知っておきたいこと</h3><p>次の様なテストコードを実行した場合、Todo_modelの<code>$this-&gt;todo_dao</code>はControllerインスタンスのtodo_daoプロパティを参照しているわけではないです。<br>テストコードでTodo_modelのtodo_daoプロパティに注入したMockオブジェクトを参照しています。<br>このとき、<code>__get</code>メソッドは呼び出されていません。</p><p>モデル<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_model</span> <span class="keyword">extends</span> <span class="title">CI_Model</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">find</span><span class="params">(int $id)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">         <span class="comment">// Loader::modelを用いているのでControllerインスタンスにtodo_daoプロパティが注入される。</span></span><br><span class="line">         <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'todo_dao'</span>);</span><br><span class="line">         <span class="comment">// `__get`マジックメソッドによりControllerインスタンスのtodo_daoプロパティが利用される。</span></span><br><span class="line">         <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;todo_dao-&gt;get($id);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>テストコード<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_model_test</span> <span class="keyword">extends</span> <span class="title">UnitTestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> $obj;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">setUp</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::setUp();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;obj = <span class="keyword">$this</span>-&gt;newModel(<span class="string">'todo_model'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_inject_todo_dao_property</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// Todo_modelのtodo_daoプロパティにMockオブジェクトを注入している。</span></span><br><span class="line">        <span class="comment">// このプロパティへのMockオブジェクトの注入により、</span></span><br><span class="line">        <span class="comment">// Todo_modelからtodo_daoへの参照時に__getは動作しません。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;obj-&gt;todo_dao = <span class="keyword">$this</span>-&gt;getDouble(Todo_dao::class, [</span><br><span class="line">             <span class="string">'get'</span> =&gt; [<span class="string">'id'</span> =&gt; <span class="number">1</span>]</span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        $actual = <span class="keyword">$this</span>-&gt;obj-&gt;find(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;assertSame(<span class="number">1</span>, $actual[<span class="string">'id'</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>このため、前回のLoader::modelの説明では</p><p><code>Loader::modelはLoader::modelを利用せずにプロパティに代入されているとエラーが発生する</code></p><p>と記載しましたが、この場合は<code>$this-&gt;load-&gt;model(&#39;todo_dao&#39;)</code>を呼び出していますが、エラーは発生しないです。</p><p>なぜならば、Loader::modelでは<strong>Controllerインスタンス</strong>のプロパティに対して、存在やインスタンス名がチェックされるからです。</p><p><img src="https://www.plantuml.com/plantuml/svg/ZLFTQjH05BxFKuIXqAoG5-X5A7YhNdaZKfQboTfWcf7TU2K5puojFnkehTR-KAcYTRSjtHMsnLfg-p27QNmCpqmswsPbLGXaPZZdEz_Fp6mpgJMInukHPJatAhRhUZQxotrUNUH-437BCYSeLr1yH_a2HH_b1SgNA6ZzAj6LVeqZT6_p-OSe3b6-Gt66eesYP-hNKMwYF4QvPa0-8PnTzpjgUR_0heUnZ_8ZYXyefKQ6LArT8FJLytE43HGfYXO-5LJ_YAY6ISZZl8lPJomxfpoLB8HDrujupNSzGzsPBiXDLnYLZVJUeggWCJEJIpGW0vYRF4uQF8g21YaQYXiNaAhkJdPvC207Nn2UQOhGGWbQESaWyR1DXynWXtMRCJjP26ApDUCy-_wzeEaWl37V4KBl-lq-mY6XLUPiDi5FwERkPQVfpsyq9BLAQX3Q19IZKAtUYWxA0nqQfDdsUVPsRvHP43M3tvmqgJlS-JyNUkfuI-rs43PBumezMeneLUP6FHMVKVRrHH9NWmNrZECU3IUAzLnhYN3iXtLQBOqbNpIvteCWSQP2dq9VihJnrIgTwD1Q6mYhmnWAiNyGddpR2pPvx7kJ4ahLgiYMs-MxEp3w7x6plxX1k6N3rOSJzPhczOOAzRFvcW-bYSOfQvR-zBn_0G00"></p><p>つまり、以下の様なテストを書いたとしてもTodo_dao内でdbのmockオブジェクトが参照されることはありません。</p><p>Todo_dao<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_dao</span> <span class="keyword">extends</span> <span class="title">CI_Model</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">get</span><span class="params">(int $id)</span>: <span class="title">array</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;db-&gt;where(<span class="string">'id'</span>, $id);</span><br><span class="line">        $query = <span class="keyword">$this</span>-&gt;db-&gt;get(<span class="string">'todo'</span>, <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> $query-&gt;row(<span class="number">0</span>, <span class="string">'array'</span>) ?? [];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><strong>Todo_model</strong>のテストコード</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_model_test</span> <span class="keyword">extends</span> <span class="title">UnitTestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> $obj;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">setUp</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::setUp();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;obj = <span class="keyword">$this</span>-&gt;newModel(<span class="string">'todo_model'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_inject_todo_dao_property</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $query_mock = <span class="keyword">$this</span>-&gt;getDouble(CI_DB_result::class, [</span><br><span class="line">             <span class="string">'row'</span> =&gt; [<span class="string">'id'</span> =&gt; <span class="number">1</span>]</span><br><span class="line">        ])</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Todo_modelのdbプロパティにMockオブジェクトを注入している。</span></span><br><span class="line">        <span class="comment">// 決してControllerのdbプロパティへインジェクトしているわけではないので、Todo_daoからmockは参照されない。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;obj-&gt;db = <span class="keyword">$this</span>-&gt;getDouble(CI_DB_query_builder::class, [</span><br><span class="line">             <span class="string">'where'</span> =&gt; <span class="keyword">$this</span>-&gt;returnSelf(),</span><br><span class="line">             <span class="string">'get'</span> =&gt; $query_mock</span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        $actual = <span class="keyword">$this</span>-&gt;obj-&gt;find(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;assertSame(<span class="number">1</span>, $actual[<span class="string">'id'</span>]); <span class="comment">// fail</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://www.plantuml.com/plantuml/svg/ZLF1QXfH5Ds_hnW6GItC3xYGa7IPTkMc52ccRqo6ZlEAJbT5yBvNcoOHGbhI92L5YbYj4bsuITkqFtCx-XkzxurZH1C8pE9nkUVSSywzir4BIzNmJSLdBFCeQpbEth8BWeiN5S5TtsRCL52zH_aJrGUK4rGtg0vGqlkGxo2CdeYNhr0EK7r6UOsoXt9iEZ-YEaDrXUh4m3i8rzD9Fsfs4zQo5t1Ktr3-GQKq9xIYank4IRJ_2-4KPGlb4JOazRycaLxeYI16sTPRPiLYK-6ULtFolYXn9sz4PzAXDi1B8fsrgMq9kqrTRZMNYyqPaZdDbWZ2gl1zjqgE4loQWLOq-ZBx-sCk3huZlDCIuGWLQETaWyp31HNjkxc72EFewZowx2ES8Gmq0xHHWXbaaasSQuRwYir4cPFVTSDCokFaiCvMjaOErfGF5vxkczy0zj0GZkMisLlIkRoUmXoMov72zJtn7MtBA-kRMfMehwcLY3CPMk6U6vWWOvRdJqK6uPFvXjElNUh95YtxyIPTMw8g2T_rQsu29UGpjtOBfH0qsjZF5cqzU7Zl6V_xtQ6ZDN6rTRGmPijP3qojH_aL5X6FxZOtZYSQTG7tohGwnZReIV_VVm00"></p><h2 id="総まとめ"><a href="#総まとめ" class="headerlink" title="総まとめ"></a>総まとめ</h2><h3 id="Controllerの実装-設計"><a href="#Controllerの実装-設計" class="headerlink" title="Controllerの実装(設計)"></a>Controllerの実装(設計)</h3><ul><li>システム内で<code>get_instance</code>関数を用いて、Controllerの同一なインスタンスを利用しやすい様になっています。</li><li>Controllerインスタンスは複数のインスタンスを生成することができます。</li></ul><h3 id="Controllerの実装-コンストラクタ"><a href="#Controllerの実装-コンストラクタ" class="headerlink" title="Controllerの実装(コンストラクタ)"></a>Controllerの実装(コンストラクタ)</h3><ul><li>load_class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する。</li><li><code>parent::__construct</code>より前で<code>$this-&gt;load</code>は利用できない。</li><li>最後にインスタンス化したControllerが<code>get_instance</code>で取得できる様になる。</li></ul><h3 id="依存性解決の実装"><a href="#依存性解決の実装" class="headerlink" title="依存性解決の実装"></a>依存性解決の実装</h3><ul><li>load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込むために利用されている。</li><li>Loader::model、Loader::library実行時はinstanceofでインスタンス名がチェックされている。</li><li>Loader::modelはLoader::modelを利用せずにプロパティに代入されているとエラーが発生する。</li></ul><h3 id="Modelの実装-1"><a href="#Modelの実装-1" class="headerlink" title="Modelの実装"></a>Modelの実装</h3><ul><li>Modelはプロパティが存在しないときにControllerインスタンスのプロパティを参照する。</li></ul><h3 id="最後に"><a href="#最後に" class="headerlink" title="最後に"></a>最後に</h3><p>この中でも特に重要なのはCodeIgniter3というフレームワークは、Controllerインスタンスのプロパティへ依存性を注入して依存関係を解決しています。<br>そのフレームワークの機能を用いた依存性の注入が行われる経路は大きく分けて<code>load_class</code>と<code>Loader</code>があります。</p><ul><li>どのControllerインスタンスが利用されているか。</li><li>どの経路から注入することができるか。</li><li>どの様に注入することでテストが動くか。</li></ul><p>これらの知識は、依存関係の解決が複雑なControllerのテスト時に役にたちます。<br>これらを覚えてモリモリci-phpunit-testでテストを書きましょう。</p><p>また、Loaderの存在しないCodeIgniter4では、Controllerのテスト時しやすいようにコンストラクタでの依存関係解決は行われていないです。<br>これは、また別の機会に。</p><p>現場からは以上でした。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。&lt;br&gt;私は、その説明が非常にめんどくさいと感じているのでここに記載します。&lt;/p&gt;
&lt;p&gt;知っておいて欲しい実装は次の4つです。&lt;
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://i.botch.me/tags/PHP/"/>
    
      <category term="ci-phpunit-test" scheme="https://i.botch.me/tags/ci-phpunit-test/"/>
    
      <category term="CodeIgniter3" scheme="https://i.botch.me/tags/CodeIgniter3/"/>
    
  </entry>
  
  <entry>
    <title>ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-依存性解決の実装</title>
    <link href="https://i.botch.me/2018/12/03/ci-phpunit-test%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%84%E3%81%A6%E6%AC%B2%E3%81%97%E3%81%84CodeIgniter3%E3%81%AE%E5%AE%9F%E8%A3%85-%E4%BE%9D%E5%AD%98%E6%80%A7%E8%A7%A3%E6%B1%BA%E3%81%AE%E5%AE%9F%E8%A3%85/"/>
    <id>https://i.botch.me/2018/12/03/ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-依存性解決の実装/</id>
    <published>2018-12-02T19:08:18.000Z</published>
    <updated>2018-12-02T19:08:18.000Z</updated>
    
    <content type="html"><![CDATA[<p>私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。<br>私は、その説明が非常にめんどくさいと感じているのでここに記載します。</p><p>知っておいて欲しい実装は次の4つです。</p><ul><li>Controllerの実装(設計)</li><li>Controllerの実装(コンストラクタ)</li><li><strong>依存性解決の実装</strong> &lt;- イマココ</li><li>Modelの実装</li></ul><h2 id="依存性解決の実装"><a href="#依存性解決の実装" class="headerlink" title="依存性解決の実装"></a>依存性解決の実装</h2><p>前回までの内容で、CodeIgniterではControllerインスタンスを共通化しやすい構造になっていると記載しましたが、<br>CodeIgniterではController以外にもLibraryやModel等のインスタンス(依存性)も共通化しやすい構造になっています。</p><p>その構造に大きく関係するのが<code>load_class</code>関数と<code>Loader</code>クラスです。</p><h3 id="load-class関数の実装"><a href="#load-class関数の実装" class="headerlink" title="load_class関数の実装"></a><code>load_class</code>関数の実装</h3><p>load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込んでそのインスタンスを返します。<br>このとき、読み込んだクラスのインスタンスは共通化しやすい様に関数内部に保持されます。</p><p>つまり、次のようになります。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 最初の読み込みでロードされて、以降のload_classでは同じインスタンスが返却される。</span></span><br><span class="line">$first_config =&amp; load_class(<span class="string">'Config'</span>, <span class="string">'core'</span>);</span><br><span class="line">$second_config =&amp; load_class(<span class="string">'Config'</span>, <span class="string">'core'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// load_classで同じインスタンスを返すため同一である。</span></span><br><span class="line">assert($first_config === $second_config);</span><br></pre></td></tr></table></figure><p>読み込みのロジックはLoader::modelやLoader::libraryとは異なり、Libraryやsystem/coreディレクトリのクラスを読み込むために利用されています。<br>そのため、フレームワーク利用者が触ることはほとんどありません。<br>またsystem/core内のLoaderクラスもこのload_class関数を利用して読み込まれます。</p><h3 id="Loader-modelとLoader-libraryの実装"><a href="#Loader-modelとLoader-libraryの実装" class="headerlink" title="Loader::modelとLoader::libraryの実装"></a>Loader::modelとLoader::libraryの実装</h3><p>Loader::modelとLoader::libraryも同様にLibraryやModelクラスのインスタンスを共通化しやすい様に作られています。<br>何度ロードしてもControllerのプロパティに定義されているインスタンスは同じインスタンスです。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'todo_model'</span>);</span><br><span class="line">        $first = <span class="keyword">$this</span>-&gt;todo_model;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'todo_model'</span>);</span><br><span class="line">        $second = <span class="keyword">$this</span>-&gt;todo_model;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 何度ロードしても同じインスタンス</span></span><br><span class="line">        assert($first === $second);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ただし、既にロード済みの依存性をLoader::libraryやLoader::modelでロードした場合プロパティ名とインスタンス名が異なるとエラーが発生します。<br>プロパティ名とインスタンス名とは、正確には<code>instanceof</code>演算子でチェックしています。<br>そのため、継承関係にある場合はエラーは発生しません。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> $todo;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;todo = <span class="keyword">new</span> CI_Form_validation();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// CI_Form_validationがtodoプロパティに代入されているが、Todoクラスをロードしようとすると</span></span><br><span class="line">        <span class="comment">// インスタンスが異なるためエラーになる。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;library(<span class="string">'todo'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>また、Loader::modelの場合は、Loader::modelを利用してプロパティに依存性を注入していない限り同じインスタンス名であってもエラーが発生します。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> $todo;</span><br><span class="line">    <span class="keyword">public</span> $todo_model;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;todo = <span class="keyword">new</span> Todo();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;todo_model = <span class="keyword">new</span> Todo_model();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// Todoインスタンスがプロパティに定義されている状態で</span></span><br><span class="line">        <span class="comment">// 同じTodoクラスをロードした場合はエラーが発生しない。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;library(<span class="string">'todo'</span>);</span><br><span class="line">        <span class="comment">// Todo_modelインスタンスがプロパティに定義されている状態であっても</span></span><br><span class="line">        <span class="comment">// Loader::modelを利用していなければ同じTodo_modelクラスをロードした場合はエラーが発生する。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'todo_model'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ここまでのまとめ"><a href="#ここまでのまとめ" class="headerlink" title="ここまでのまとめ"></a>ここまでのまとめ</h3><ul><li>load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込むために利用されている。</li><li>Loader::model、Loader::library実行時はinstanceofでインスタンス名がチェックされている。</li><li>Loader::modelはLoader::modelを利用せずにプロパティに代入されているとエラーが発生する。</li></ul><h3 id="テスト時に知っておきたいこと"><a href="#テスト時に知っておきたいこと" class="headerlink" title="テスト時に知っておきたいこと"></a>テスト時に知っておきたいこと</h3><h4 id="load-class関数はLibraryやsystem-coreディレクトリのクラスを読み込むために利用されている"><a href="#load-class関数はLibraryやsystem-coreディレクトリのクラスを読み込むために利用されている" class="headerlink" title="load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込むために利用されている"></a>load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込むために利用されている</h4><p>load_class関数はLibraryやsystem/coreディレクトリのクラスを読み込むために利用されています。</p><p>ci-phpunit-testにはload_class_instance関数があります。<br>そのため、CodeIgniterのsystem/coreに定義されたクラスファイルのモック化もある程度は可能です。</p><p>CodeIgniterの中でload_class関数を利用して読み込まれているクラスは以下の通りです。</p><ul><li>Benchmark</li><li>Hooks</li><li>Config</li><li>Utf8</li><li>URI</li><li>Router</li><li>Output</li><li>Security</li><li>Input</li><li>Lang</li><li>Loader</li></ul><p>これらのcoreクラスをMockオブジェクトに書き換えているテストが以下です。</p><p>テスト対象のコントローラ<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">const</span> APP_KEY = <span class="string">'42qj0j@vpq33-[29hgvaegh38'</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        $app_key = <span class="keyword">$this</span>-&gt;input-&gt;cookie(<span class="string">'app-key'</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> ($app_key !== <span class="keyword">self</span>::APP_KEY) &#123;</span><br><span class="line">            show_error(<span class="string">'APP KEYが誤っています。'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">index</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;view(<span class="string">'sample'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>テストコード</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_app_key</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $mock = <span class="keyword">$this</span>-&gt;getDouble(Input::class, [</span><br><span class="line">            <span class="string">'cookie'</span> =&gt; <span class="string">'42qj0j@vpq33-[29hgvaegh38'</span>,</span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallablePreConstructor(<span class="function"><span class="keyword">function</span><span class="params">()</span> <span class="title">use</span> <span class="params">($mock)</span></span>&#123;</span><br><span class="line">            <span class="comment">// Inputクラスをmockに差し替えることができる</span></span><br><span class="line">            load_class_instance(<span class="string">'Input'</span>, $mock);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        $output = <span class="keyword">$this</span>-&gt;request(<span class="string">'GET'</span>, <span class="string">'sample/index'</span>);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;assertContains(<span class="string">'sample'</span>, $output);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Loader-model、Loader-library実行時はinstanceofでインスタンス名がチェックされている"><a href="#Loader-model、Loader-library実行時はinstanceofでインスタンス名がチェックされている" class="headerlink" title="Loader::model、Loader::library実行時はinstanceofでインスタンス名がチェックされている"></a>Loader::model、Loader::library実行時はinstanceofでインスタンス名がチェックされている</h4><p>Loader::model、Loader::library実行時はinstanceofでインスタンス名がチェックされます。<br>そのため、以下の様なテストをするときは注意が必要です。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        <span class="comment">// MY_Form_validationが定義されているシステムと仮定</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;library(<span class="string">'form_validation'</span>);</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">$this</span>-&gt;form_validation-&gt;run()) &#123;</span><br><span class="line">            show_404();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_instanceof</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// 継承元のCI_Form_validationのmockを作成している。</span></span><br><span class="line">        $mock = <span class="keyword">$this</span>-&gt;getDouble(CI_Form_validation::class, [</span><br><span class="line">            <span class="string">'run'</span> =&gt; <span class="keyword">true</span>,</span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallablePreConstructor(<span class="function"><span class="keyword">function</span><span class="params">()</span> <span class="title">use</span> <span class="params">($mock)</span></span>&#123;</span><br><span class="line">            load_class_instance(<span class="string">'form_validation'</span>, $mock);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Controllerの$this-&gt;load-&gt;libraryの箇所でエラーが発生する。</span></span><br><span class="line">        <span class="comment">// なぜならば、$mock instanceof MY_Form_validationはfalseだからだ。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;request(<span class="string">'GET'</span>, <span class="string">'sample/index'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上の例では、正しくは、<code>MY_Form_validation</code>のmockオブジェクトを作成する様にします。</p><h4 id="Loader-modelはLoader-modelを利用せずにプロパティに代入されているとエラーが発生する"><a href="#Loader-modelはLoader-modelを利用せずにプロパティに代入されているとエラーが発生する" class="headerlink" title="Loader::modelはLoader::modelを利用せずにプロパティに代入されているとエラーが発生する"></a>Loader::modelはLoader::modelを利用せずにプロパティに代入されているとエラーが発生する</h4><p>さきほどと打って変わって、Loader::modelでは同じインスタンスであっても注入の経路によってエラーが発生します。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'auth_model'</span>);</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">$this</span>-&gt;auth_model-&gt;is_loggedin()) &#123;</span><br><span class="line">            redirect(<span class="string">'login/index'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sample_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_instanceof</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">// Mock化したいModelと同じクラスのMockを作成。</span></span><br><span class="line">        $mock = <span class="keyword">$this</span>-&gt;getDouble(Auth_model::class, [</span><br><span class="line">            <span class="string">'is_loggedin'</span> =&gt; <span class="keyword">true</span>,</span><br><span class="line">        ]);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallablePreConstructor(<span class="function"><span class="keyword">function</span><span class="params">()</span> <span class="title">use</span> <span class="params">($mock)</span></span>&#123;</span><br><span class="line">            <span class="comment">// load_classを用いて読み込んだことにするのでLoader::modelを経由したことにはならない。</span></span><br><span class="line">            load_class_instance(<span class="string">'auth_model'</span>, $mock);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Controllerの$this-&gt;load-&gt;modelの箇所でエラーが発生する。</span></span><br><span class="line">        <span class="comment">// $mock instanceof Auth_modelはtrueだが、Loader::modelを経由していないためエラーが発生する。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;request(<span class="string">'GET'</span>, <span class="string">'sample/index'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。&lt;br&gt;私は、その説明が非常にめんどくさいと感じているのでここに記載します。&lt;/p&gt;
&lt;p&gt;知っておいて欲しい実装は次の4つです。&lt;
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://i.botch.me/tags/PHP/"/>
    
      <category term="ci-phpunit-test" scheme="https://i.botch.me/tags/ci-phpunit-test/"/>
    
      <category term="CodeIgniter3" scheme="https://i.botch.me/tags/CodeIgniter3/"/>
    
  </entry>
  
  <entry>
    <title>ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Controllerの実装-コンストラクタ</title>
    <link href="https://i.botch.me/2018/12/02/ci-phpunit-test%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%84%E3%81%A6%E6%AC%B2%E3%81%97%E3%81%84CodeIgniter3%E3%81%AE%E5%AE%9F%E8%A3%85-Controller%E3%81%AE%E5%AE%9F%E8%A3%85-%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF/"/>
    <id>https://i.botch.me/2018/12/02/ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Controllerの実装-コンストラクタ/</id>
    <published>2018-12-02T14:59:59.000Z</published>
    <updated>2018-12-02T19:08:01.000Z</updated>
    
    <content type="html"><![CDATA[<p>私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。<br>私は、その説明が非常にめんどくさいと感じているのでここに記載します。</p><p>知っておいて欲しい実装は次の4つです。</p><ul><li>Controllerの実装(設計)</li><li><strong>Controllerの実装(コンストラクタ)</strong> &lt;- イマココ</li><li>依存性解決の実装</li><li>Modelの実装</li></ul><h2 id="Controllerの実装-コンストラクタ"><a href="#Controllerの実装-コンストラクタ" class="headerlink" title="Controllerの実装(コンストラクタ)"></a>Controllerの実装(コンストラクタ)</h2><p>コンストラクタの中では次のことを行います。</p><ul><li>自身を静的変数に定義</li><li>load_class関数を用いてロードしたクラスファイルをプロパティとして定義</li><li>Loaderクラスをインスタンス化してプロパティとして定義</li><li>Loaderクラスの初期化処理を実行</li></ul><h3 id="自身を静的変数に定義"><a href="#自身を静的変数に定義" class="headerlink" title="自身を静的変数に定義"></a>自身を静的変数に定義</h3><p>Controllerでは、最初に自身を静的変数に代入します。<br>つまりは、new CI_Controllerを実行したインスタンスが<code>get_instance</code>関数で取得できる様になります。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$first = <span class="keyword">new</span> CI_Controller();</span><br><span class="line"></span><br><span class="line">actual($first === get_instnace()); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">$second = <span class="keyword">new</span> CI_Controller();</span><br><span class="line"></span><br><span class="line">actual($first === get_instnace()); <span class="comment">// false</span></span><br><span class="line">actual($second === get_instnace()); <span class="comment">// true</span></span><br></pre></td></tr></table></figure><p>この様に、最後にインスタンス化したControllerが<code>get_instance</code>で取得できる様になります。</p><h3 id="load-class関数を用いてロードしたクラスファイルをプロパティとして定義"><a href="#load-class関数を用いてロードしたクラスファイルをプロパティとして定義" class="headerlink" title="load_class関数を用いてロードしたクラスファイルをプロパティとして定義"></a>load_class関数を用いてロードしたクラスファイルをプロパティとして定義</h3><p>このload_class関数については後述しますが、ここでは指定されたクラスをインスタンス化するnewのラップ関数と理解してください。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">load_class(&apos;Input&apos;, &apos;core&apos;) == new CI_Input</span><br></pre></td></tr></table></figure><p>CodeIgniterはControllerをインスタンス化するまでに様々なクラスをインスタンス化します。<br>その様々なクラスをインスタンス化する際は、load_class関数を用いてインスタンス化されます。</p><p>load_class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入します。<br>このような処理が、Constrollerのコンストラクタで行われています。</p><p>何もロードせずとも<code>$this-&gt;input</code>などが利用できるのはこのためです。</p><h3 id="Loaderクラスをインスタンス化してプロパティとして定義"><a href="#Loaderクラスをインスタンス化してプロパティとして定義" class="headerlink" title="Loaderクラスをインスタンス化してプロパティとして定義"></a>Loaderクラスをインスタンス化してプロパティとして定義</h3><p>Controllerをインスタンス化するときにLoaderクラスをインスタンス化し、プロパティとして定義しています。<br>Controllerを実装するときに<code>parent::__construct</code>より前で<code>$this-&gt;load</code>が利用できないのはこのためです。</p><p>また、非常に重要なポイントとしてLoaderクラスをインスタンス化するときはCommon.phpに定義されているload_class関数を利用しています。</p><h3 id="Loaderクラスの初期化処理を実行"><a href="#Loaderクラスの初期化処理を実行" class="headerlink" title="Loaderクラスの初期化処理を実行"></a>Loaderクラスの初期化処理を実行</h3><p>ここで、少しControllerと離れてしまいますが、Loaderクラスには初期化処理があります。<br>この初期化処理では、config/autoload.phpに記載されたModelやLibrary、Helperなどをロードします。<br>このautoloadに記載されたファイルをロードする際はLoaderに実装されているmodelメソッドやlibraryメソッドなどが利用されます。</p><h3 id="ここまでのまとめ"><a href="#ここまでのまとめ" class="headerlink" title="ここまでのまとめ"></a>ここまでのまとめ</h3><ul><li>load_class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する。</li><li><code>parent::__construct</code>より前で<code>$this-&gt;load</code>は利用できない。</li><li>最後にインスタンス化したControllerが<code>get_instance</code>で取得できる様になる。</li></ul><h3 id="テスト時に知っておきたいこと"><a href="#テスト時に知っておきたいこと" class="headerlink" title="テスト時に知っておきたいこと"></a>テスト時に知っておきたいこと</h3><h4 id="load-class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する"><a href="#load-class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する" class="headerlink" title="load_class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する"></a>load_class関数を用いてインスタンス化されたオブジェクトをControllerのプロパティに代入する</h4><p>ci-phpunit-testには<code>load_class_instance</code>関数があります。<br>これは、load_classを用いてインスタンス化したことにする関数です。</p><p>つまり、load_class_instanceを用いてMockオブジェクトを挿入しておけば<br>Controllerのコンストラクタ内の処理で、Controllerのプロパティにmockオブジェクトが挿入されます。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// コンストラクタが動くときに、ControllerのsampleプロパティへSample_dummyクラスが注入される。</span></span><br><span class="line">load_class_instance(<span class="string">'sample'</span>, <span class="keyword">new</span> Sample_dummy);</span><br></pre></td></tr></table></figure><h4 id="parent-constructより前で-this-gt-loadは利用できない"><a href="#parent-constructより前で-this-gt-loadは利用できない" class="headerlink" title="parent::__constructより前で$this-&gt;loadは利用できない"></a><code>parent::__construct</code>より前で<code>$this-&gt;load</code>は利用できない</h4><p>ci-phpunit-testには<code>request-&gt;setCallablePreConstructor</code>メソッドがあります。<br>これは、Controllerのインスタンス化前に処理を挟むことのできるメソッドです。</p><p>多くの場合load_class_instance関数と併用して利用されます。<br>また、インスタンス化前なので引数として与える関数では、テスト対象のコントローラへアクセスすることはできません。<br>この点が<code>setCallable</code>とは異なります。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_should_show_validation_error</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $mock = <span class="keyword">$this</span>-&gt;getDouble(CI_Form_validation::class, [</span><br><span class="line">           <span class="string">'run'</span> =&gt; <span class="keyword">false</span></span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 引数に$CIはない。</span></span><br><span class="line">        <span class="comment">// なぜならば、Controllerのコンストラクタが動く前のフックだからだ。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallablePreConstructor(<span class="function"><span class="keyword">function</span><span class="params">()</span> <span class="title">use</span><span class="params">($mock)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// form_validationがTodoコントローラがインスタンス化するときにプロパティに定義される。</span></span><br><span class="line">            load_class_instance(<span class="string">'form_validation'</span> $mock);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        $output = <span class="keyword">$this</span>-&gt;request(<span class="string">'POST'</span>, <span class="string">'todo/add'</span>, [</span><br><span class="line">            <span class="string">'title'</span> =&gt; <span class="string">'subject'</span>,</span><br><span class="line">            <span class="string">'body'</span> =&gt; <span class="string">'body'</span></span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">$this</span>-&gt;assertContains(<span class="string">'タイトルは必須です'</span>, $output);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><img src="https://www.plantuml.com/plantuml/svg/XP9FIm916CRl-ocMFMdWLmX128A23jsNJJUGHYTsnoxHOMV4F-IXCXKbCKiqLxJ0IuNeXtbPNRz5CxCPQKMmXu5vt-VvFSzir2Q6HJ8ff2YXhR0QYMohsY5EO9sODj4KvPJV9aaIfrNDDadCGCWuGkQ1PSPmsYPM9awmfQddYXgi8cmaz3WoR1leZLVeBswVmAc1qutjwZlOIkbd1aec32cuZ-CdG5rWLQ2lG7j0du4L-Dxn-Y0udng-EmHdvaydGClWt8DJbbS3SEf0BpNbOWsN0n8B8sHQGCV0na3VX3ph2nyw-mRjvrt_VS2Dbytwef7rEiLvvONhUgF6OjhNVcPRLX_41BsHyR8S3TWGs0HOKPAsP9I-TAm0RGFj01C6m6foyXfO3kZZLxB0IGd9IFphRLQmkLVwepnHXilB2A-ezpYqQ635mna4Z6o3peA3Lwe66F_W_QxeZBn2StxNuYfUlhkuoecN7xNyTaayIDHC9_ZVzG40"></p><p>また似た<code>request-&gt;addCallablePreConstructor</code>もありますが、このメソッドはsetCallablePreConstructorでセットした関数を上書きしないで追加するメソッドです。<br>詳細は割愛します。</p><h4 id="最後にインスタンス化したControllerがget-instanceで取得できる様になる"><a href="#最後にインスタンス化したControllerがget-instanceで取得できる様になる" class="headerlink" title="最後にインスタンス化したControllerがget_instanceで取得できる様になる"></a>最後にインスタンス化したControllerが<code>get_instance</code>で取得できる様になる</h4><p>次の様なコードのテストを書く際は、どのControllerインスタンスが利用されているか意識すると良いです。</p><p>ライブラリ<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_validator</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">private</span> $ci;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">   </span>&#123;</span><br><span class="line">       <span class="keyword">$this</span>-&gt;ci = get_instance();</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">is_valid</span><span class="params">(array $value)</span>: <span class="title">bool</span></span></span><br><span class="line"><span class="function">   </span>&#123;</span><br><span class="line">       <span class="keyword">$this</span>-&gt;ci-&gt;form_validation-&gt;set_data($value);</span><br><span class="line">       <span class="keyword">$this</span>-&gt;ci-&gt;form_validation-&gt;set_rules(<span class="string">'title'</span>, <span class="string">'タイトル'</span>, <span class="string">'trim|required|maxlength[100]'</span>);</span><br><span class="line">       <span class="keyword">$this</span>-&gt;ci-&gt;form_validation-&gt;set_rules(<span class="string">'body'</span>, <span class="string">'本文'</span>, <span class="string">'trim|required|maxlength[255]'</span>);</span><br><span class="line"></span><br><span class="line">       <span class="keyword">return</span> <span class="keyword">$this</span>-&gt;ci-&gt;form_validation-&gt;run();</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>テストコード<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Todo_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_controller_instance_not_equal</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallablePreConstructor(<span class="function"><span class="keyword">function</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="comment">// Todo_validatorがインスタンス化される時に呼び出されるget_instanceでは、</span></span><br><span class="line">            <span class="comment">// テスト対象のコントローラインスタンス化前のフックなので</span></span><br><span class="line">            <span class="comment">// Todo_test::classの$this-&gt;CIが取得される。</span></span><br><span class="line">            load_class_instance(<span class="string">'todo_validator'</span> <span class="keyword">new</span> Todo_validator);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">$this</span>-&gt;request-&gt;setCallable(<span class="function"><span class="keyword">function</span><span class="params">($CI)</span> </span>&#123;</span><br><span class="line">            <span class="comment">// load_class_instanceで入れたTodo_validatorの$ciプロパティと</span></span><br><span class="line">            <span class="comment">// setCallableの引数の$CIは違うインスタンスなのでこのMockオブジェクトは参照されない。</span></span><br><span class="line">            <span class="keyword">$this</span>-&gt;form_validation = <span class="keyword">$this</span>-&gt;getDouble(CI_Form_validation::class, [</span><br><span class="line">                <span class="string">'run'</span> =&gt; <span class="keyword">true</span></span><br><span class="line">            ]);</span><br><span class="line">        &#125;);</span><br><span class="line">        $output = <span class="keyword">$this</span>-&gt;request(<span class="string">'POST'</span>, <span class="string">'todo/add'</span>, [</span><br><span class="line">            <span class="string">'title'</span> =&gt; str_repeat(<span class="string">'s'</span>, <span class="number">1000</span>),</span><br><span class="line">            <span class="string">'body'</span> =&gt; <span class="string">'body'</span></span><br><span class="line">        ]);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// つまりこのテストは失敗する。</span></span><br><span class="line">        <span class="keyword">$this</span>-&gt;assertRedirect(<span class="string">'todo/complete'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><img src="https://www.plantuml.com/plantuml/svg/XLHVQzDG57_FfmZ1XrRe5zZ361G4GS47tqTi8XQpPYHtleZGUs-tTXYnJjUniH6dTMaxcWbziEheFinPcknRU6xwPscQ8eIISk_v_JldPCCcgaLsjWn9oZtCouN2kgmyDpLpa-WsKIHf6q_Bf6nMPCNMIL4r3FM5eJ-pzA9PiOcrKoAcfSXl9NbIQfYgjbaoLDi6TX0qkk6N3j0Ze1SnvXlLA6igLk1XrEi3lOb6ry0-0_qA57_nxYNGOs3l5UdT0lTYUOAmL0P-1BmF_1FmFM3V4GAb4Siq31rvsi07m7u3kvc-K2VmJy9H5-WXC0UfeCgat0FogcmNrekFyNnyMWr63kBSIwGZSPUw2MJQ0-e3zPAL6MA6-ImhIOM3gJ3U0Du5TeLI4_wYUY_wSubvtPqTXoUreBq_Flo9sYOcbENSxrhVn0rs44TVm-XZGTV0z-DEk892424Z9d0UHoG8bgHFEpTXad9nX_3no68hqsNfVFCpaKzGep0yeqVAfVO4Jcl2-XyrsOZK3nfdurCNKOBwHTZS4qFekz6va-f1OfGJMQSwG_sNfhKrSufrovFsr2oz1jO3tW8s1ER5KXh8EXvqWjqVgsVvdcPjJTMqb8Y5HKbk4V0cSAIhYuQoNy0zZ6rgLGGlhCw3LrP0KZzZJfUy8jn75kvwA_Ua4pITss5rlWY9JF4ViB_0kGXZKa_THsc2gmK20LERpugIwHHnRuSVndtKsmkgRQ6XqSQ9I6gCqzxGAnf-q_u1"></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。&lt;br&gt;私は、その説明が非常にめんどくさいと感じているのでここに記載します。&lt;/p&gt;
&lt;p&gt;知っておいて欲しい実装は次の4つです。&lt;
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://i.botch.me/tags/PHP/"/>
    
      <category term="ci-phpunit-test" scheme="https://i.botch.me/tags/ci-phpunit-test/"/>
    
      <category term="CodeIgniter3" scheme="https://i.botch.me/tags/CodeIgniter3/"/>
    
  </entry>
  
  <entry>
    <title>ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Controllerの実装(設計)</title>
    <link href="https://i.botch.me/2018/12/01/ci-phpunit-test%E3%82%92%E4%BD%BF%E3%81%86%E6%99%82%E3%81%AB%E7%9F%A5%E3%81%A3%E3%81%A6%E3%81%8A%E3%81%84%E3%81%A6%E6%AC%B2%E3%81%97%E3%81%84CodeIgniter3%E3%81%AE%E5%AE%9F%E8%A3%85-Controller%E3%81%AE%E5%AE%9F%E8%A3%85-%E8%A8%AD%E8%A8%88/"/>
    <id>https://i.botch.me/2018/12/01/ci-phpunit-testを使う時に知っておいて欲しいCodeIgniter3の実装-Controllerの実装-設計/</id>
    <published>2018-12-01T11:24:43.000Z</published>
    <updated>2018-12-01T11:24:43.000Z</updated>
    
    <content type="html"><![CDATA[<p>私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。<br>私は、その説明が非常にめんどくさいと感じているのでここに記載します。</p><p>知っておいて欲しい実装は次の4つです。</p><ul><li><strong>Controllerの実装(設計)</strong> &lt;- イマココ</li><li>Controllerの実装(コンストラクタ)</li><li>依存性解決の実装</li><li>Modelの実装</li></ul><h2 id="Controllerの実装-設計"><a href="#Controllerの実装-設計" class="headerlink" title="Controllerの実装(設計)"></a>Controllerの実装(設計)</h2><p>ControllerはSingletonのような神クラスです。</p><h3 id="Sigletonとは"><a href="#Sigletonとは" class="headerlink" title="Sigletonとは"></a>Sigletonとは</h3><p>Sigletonとは、そのクラスのインスタンスが一つしか保証されないクラスです。<br>おそらく実装を見た方が理解が早いと思います。</p><p>次のようなクラスが存在する場合は、getInstanceメソッドを経由してしかSingletonクラスをインスタンス化できません。<br>また、newやcloneは利用できず、PHPを実行するプロセスの中でインスタンスが一つしか生成することができません。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Singleton</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> $instance;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__clone</span><span class="params">()</span> </span>&#123;</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> LogicException(<span class="string">'This is Singleton'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="function"><span class="keyword">function</span> <span class="title">getInstance</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="keyword">if</span> (!<span class="keyword">self</span>::$instance <span class="keyword">instanceof</span> Singleton) &#123;</span><br><span class="line"><span class="keyword">self</span>::$instance = <span class="keyword">new</span> Singleton();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">self</span>::$instance;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// コンストラクタがprivateなのでエラーが発生します</span></span><br><span class="line">$obj = <span class="keyword">new</span> Singleton(); <span class="comment">// fail</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// getInstanceを経由してSingletonインスタンスを取得することができます。</span></span><br><span class="line">$singleton = Singleton::getInstance(); <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// cloneすると例外が発生します。</span></span><br><span class="line">$new_singleton = <span class="keyword">clone</span> $singleton; <span class="comment">// fail</span></span><br></pre></td></tr></table></figure><h3 id="Controllerは神クラス"><a href="#Controllerは神クラス" class="headerlink" title="Controllerは神クラス"></a>Controllerは神クラス</h3><p>CodeIgntierのControllerは上記で説明したSingletonに似た神クラスです。<br>Singletonと違い、cloneやnewを行うことができます。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// コンストラクタがpublicなのでエラーが発生しません。</span></span><br><span class="line">$ci_controller = <span class="keyword">new</span> CI_Controller(); <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 1度でもnewしていると、get_instanceからCI_Controllerインスタンスを取得できます。</span></span><br><span class="line">$ci_controller2 = CI_Controller::get_instance(); <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line">assert($ci_controller === $ci_controller2); <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// cloneすることが可能です。</span></span><br><span class="line">$new_ci_controller = <span class="keyword">clone</span> $ci_controller; <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// cloneしているのでもちろん別のインスタンスです。</span></span><br><span class="line">assert($ci_controller === $new_ci_controller); <span class="comment">// fail</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 別のインスタンスを新たに作ることももちろんできますが、インスタンスは異なります。</span></span><br><span class="line">$second_ci_controller = <span class="keyword">new</span> CI_Controller(); <span class="comment">// success</span></span><br><span class="line"></span><br><span class="line">assert($ci_controller === $second_ci_controller) <span class="comment">// fail</span></span><br></pre></td></tr></table></figure><p>つまり、CI_Controllerは<strong>一意でない可能性はあり得る構造</strong>になっています。<br>しかし、システム内では基本的に<code>get_instance</code>関数を用いてSingletonの様に扱っています。</p><p>Singletonの様に扱うことで現在ユーザからアクセスされているControllerと同一のインスタンスを利用しやすい設計になっています。</p><h3 id="ここまでのまとめ"><a href="#ここまでのまとめ" class="headerlink" title="ここまでのまとめ"></a>ここまでのまとめ</h3><ul><li>システム内で<code>get_instance</code>関数を用いて、Controllerの同一なインスタンスを利用しやすい様になっています。</li><li>Controllerインスタンスは複数のインスタンスを生成することができます。</li></ul><h3 id="テスト時に知っておきたいこと"><a href="#テスト時に知っておきたいこと" class="headerlink" title="テスト時に知っておきたいこと"></a>テスト時に知っておきたいこと</h3><p>実際にテストコードに落とし込んで知っておきたい事を記します。<br>次の例は、Controllerのテストコードです。</p><p>テスト対象のController<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test_target</span> <span class="keyword">extends</span> <span class="title">CI_Controller</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">parent</span>::__construct();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;load-&gt;model(<span class="string">'todo_model'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">list</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        $list = <span class="keyword">$this</span>-&gt;todo_model-&gt;get_list();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">$this</span>-&gt;output-&gt;enable_profiler(<span class="keyword">false</span>)</span><br><span class="line">            -&gt;set_content_type(<span class="string">'application/json; charset=UTF-8'</span>)</span><br><span class="line">            -&gt;set_output(json_encode(</span><br><span class="line">                $list,</span><br><span class="line">                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP</span><br><span class="line">            ));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="Controllerの同一なインスタンスを利用しやすい様になっている"><a href="#Controllerの同一なインスタンスを利用しやすい様になっている" class="headerlink" title="Controllerの同一なインスタンスを利用しやすい様になっている"></a>Controllerの同一なインスタンスを利用しやすい様になっている</h4><p>ci-phpunit-testでは実際にCodeIgniterで作成したアプリケーションへリクエストを行なった時と同じ様に振る舞う<code>request</code>メソッドがあります。</p><p>requestメソッドを行う際に、テストコード実装者が意図した振る舞いをするMockオブジェクトに差し替えたいと考えるでしょう。</p><p>そんな時は、Controllerのメソッドが呼び出される前処理を挟む<code>setCallable</code>を利用すると良いです。</p><p><code>setCallable</code>では引数に渡した関数の第一引数としてControllerインスタンスが渡されます。<br>このControllerインスタンスは、テスト対象のリクエストされるControllerインスタンスと同一です。</p><p>第一引数へ渡された、ControllerインスタンスのプロパティにMockオブジェクトを注入することができます。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test_target_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_setCallable_argument_CI_equal_target_controller</span><span class="params">()</span></span></span><br><span class="line"><span class="function">   </span>&#123;</span><br><span class="line">      <span class="comment">// $CIとは、requestを実行する際のControllerインスタンス(Test_target)である。</span></span><br><span class="line">      <span class="keyword">$this</span>-&gt;request-&gt;setCallable(<span class="function"><span class="keyword">function</span> <span class="params">($CI)</span> </span>&#123;</span><br><span class="line">          $CI-&gt;todo_model = <span class="keyword">$this</span>-&gt;getDouble(Todo_model::class, [</span><br><span class="line">              <span class="string">'get_list'</span> =&gt; [<span class="string">'sample'</span> =&gt; <span class="string">'1'</span>]</span><br><span class="line">          ]);</span><br><span class="line">      &#125;);</span><br><span class="line"></span><br><span class="line">      $output = <span class="keyword">$this</span>-&gt;request(<span class="string">'test_target/list'</span>);</span><br><span class="line">      <span class="keyword">$this</span>-&gt;assertContains(<span class="string">'"sample"'</span>, $output);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Controllerインスタンスは複数のインスタンスを生成することができる"><a href="#Controllerインスタンスは複数のインスタンスを生成することができる" class="headerlink" title="Controllerインスタンスは複数のインスタンスを生成することができる"></a>Controllerインスタンスは複数のインスタンスを生成することができる</h4><p>先ほどとは打って変わって、次のコードではテスト対象のController(Target_test)とは異なるControllerインスタンスへMockオブジェクトを注入してしまっている例です。<br>このテストではMockオブジェクトがテスト対象のController(Target_test)からアクセスされることはありません。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Test_target_test</span> <span class="keyword">extends</span> <span class="title">TestCase</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">   <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">setUp</span><span class="params">()</span></span></span><br><span class="line"><span class="function">   </span>&#123;</span><br><span class="line">      <span class="keyword">$this</span>-&gt;resetInstance();</span><br><span class="line">      <span class="keyword">$this</span>-&gt;CI-&gt;todo_model = <span class="keyword">$this</span>-&gt;getDouble(Todo_model::class, [</span><br><span class="line">          <span class="string">'get_list'</span> =&gt; [<span class="string">"sample"</span> =&gt; <span class="string">'this is unit test'</span>]</span><br><span class="line">      ]);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">test_target_controller_not_equal_this_CI</span><span class="params">()</span></span></span><br><span class="line"><span class="function">   </span>&#123;</span><br><span class="line">      <span class="comment">// $this-&gt;CIとTest_targetコントローラは違うインスタンスである</span></span><br><span class="line">      $output = <span class="keyword">$this</span>-&gt;request(<span class="string">'test_target/list'</span>);</span><br><span class="line">      <span class="keyword">$this</span>-&gt;assertContains(<span class="string">'"sample"'</span>, $output);</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>これは、Test_targetコントローラのインスタンスと、<code>$this-&gt;CI</code>で持っているControllerインスタンスが異なるためです。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;私はしばし、ci-phpunit-testを使ったテストの書き方で、CodeIgniter3自体の挙動を説明しなければならないことがあります。&lt;br&gt;私は、その説明が非常にめんどくさいと感じているのでここに記載します。&lt;/p&gt;
&lt;p&gt;知っておいて欲しい実装は次の4つです。&lt;
      
    
    </summary>
    
    
      <category term="PHP" scheme="https://i.botch.me/tags/PHP/"/>
    
      <category term="ci-phpunit-test" scheme="https://i.botch.me/tags/ci-phpunit-test/"/>
    
      <category term="CodeIgniter3" scheme="https://i.botch.me/tags/CodeIgniter3/"/>
    
  </entry>
  
  <entry>
    <title>TypeScriptのinferとは何か</title>
    <link href="https://i.botch.me/2018/10/10/TypeScript%E3%81%AEinfer%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B/"/>
    <id>https://i.botch.me/2018/10/10/TypeScriptのinferとは何か/</id>
    <published>2018-10-10T13:59:15.000Z</published>
    <updated>2018-10-10T18:00:37.000Z</updated>
    
    <content type="html"><![CDATA[<p>本日の勉強会で、TypeScriptの<a href="https://github.com/Microsoft/TypeScript/pull/21496" target="_blank" rel="noopener">このPR</a>をみてinferとは何かを調べたので備忘録として。</p><h2 id="結論"><a href="#結論" class="headerlink" title="結論"></a>結論</h2><ul><li>型定義ファイルを作るときに利用する。</li><li>依存している型を三項演算子の値として利用することができようにするためのもの。(@todo: この説明の正確性は要検討。)</li></ul><h2 id="例"><a href="#例" class="headerlink" title="例"></a>例</h2><p>JavaScriptで次の様なコードが実装されているとします。</p><p>簡単な、クイズを行うクラスです。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Questioner</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(question)</span><br><span class="line">  &#123;</span><br><span class="line">     <span class="keyword">this</span>.question = question;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  assert(answer)</span><br><span class="line">  &#123;</span><br><span class="line">     <span class="keyword">return</span> (<span class="keyword">this</span>.question.expected === answer);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">QuestionerFactory</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="keyword">static</span> create(question)</span><br><span class="line">  &#123;</span><br><span class="line">     <span class="keyword">return</span> <span class="keyword">new</span> Questioner(question);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>クライアントコードはこんな感じです。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> questioner = QuestionerFactory.create(&#123;</span><br><span class="line">  detail: <span class="string">'私のこと0〜100で表すとしたらどれくらい好き？'</span>,</span><br><span class="line">  expected: <span class="string">'1と0の間の距離くらいだよ'</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(questioner.assert(<span class="number">100</span>));</span><br></pre></td></tr></table></figure><p>ちょっと質問内容が難しすぎます。<br>Questionerを利用する側では、せめてassertで引数の型ぐらい教えて欲しいものです。</p><h2 id="TypeScriptによる型"><a href="#TypeScriptによる型" class="headerlink" title="TypeScriptによる型"></a>TypeScriptによる型</h2><p>TypeScriptでは型がありますので、型定義ファイルを作成することで<br>assertメソッドの引数の型を絞り込むことができます。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="keyword">namespace</span> QuestionerFactory &#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">create</span>&lt;<span class="title">T</span>&gt;(<span class="params">question: T</span>): <span class="title">Questioner</span>&lt;<span class="title">T</span>&gt;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">type</span> Expected&lt;Y&gt; = Y <span class="keyword">extends</span> &#123; expected: infer Z &#125; ? Z : never;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">interface</span> Questioner&lt;X&gt; &#123;</span><br><span class="line">    assert(answer: Expected&lt;X&gt;): <span class="built_in">boolean</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>この様にassertメソッドの引数の型がstringであるということがわかる様になります。</p><img src="/2018/10/10/TypeScriptのinferとは何か/vs-code.png" title="VSCodeView"><h2 id="解説"><a href="#解説" class="headerlink" title="解説"></a>解説</h2><h3 id="ジェネリクス"><a href="#ジェネリクス" class="headerlink" title="ジェネリクス"></a>ジェネリクス</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">create</span>&lt;<span class="title">T</span>&gt;(<span class="params">question: T</span>): <span class="title">Questioner</span>&lt;<span class="title">T</span>&gt;</span>;</span><br></pre></td></tr></table></figure><p>ここは、ジェネリクスです。<br>QuestionerFactory.createメソッドの引数<code>T</code>をQuestionerの<code>X</code>として定義しています。<br>つまり、次の様なコードを書いたときの返り値は<code>Questioner&lt;string&gt;</code>です。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">QuestionerFactory.create&lt;<span class="built_in">string</span>&gt;(<span class="string">'sample'</span>);  <span class="comment">// returns Questioner&lt;string&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 次の様に書いても型推論で、Tにstringは入るのでcreate&lt;string&gt;('sample')と同じ</span></span><br><span class="line">QuestionerFactory.create(<span class="string">'sample'</span>); <span class="comment">// returns Questioner&lt;string&gt;</span></span><br></pre></td></tr></table></figure><p><a href="https://www.typescriptlang.org/docs/handbook/generics.html" target="_blank" rel="noopener">ジェネリクス</a></p><h3 id="型エイリアス"><a href="#型エイリアス" class="headerlink" title="型エイリアス"></a>型エイリアス</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Expected&lt;Y&gt; = Y <span class="keyword">extends</span> &#123; expected: infer Z &#125; ? Z : never;</span><br></pre></td></tr></table></figure><p><code>type</code>は型エイリアスです。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> hoge = <span class="built_in">string</span>|<span class="built_in">number</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fuga</span>(<span class="params">value: hoge</span>)</span>;</span><br></pre></td></tr></table></figure><p>このように書くことで、fuga関数のvalueにはstringかnumberを入れる様に型定義することができます。</p><p><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases" target="_blank" rel="noopener">型エイリアス</a></p><h3 id="ConditionalTypes"><a href="#ConditionalTypes" class="headerlink" title="ConditionalTypes"></a>ConditionalTypes</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Expected&lt;Y&gt; = Y <span class="keyword">extends</span> &#123; expected: infer Z &#125; ? Z : never;</span><br></pre></td></tr></table></figure><p>型を条件分岐して定義することができます。<br>例えば、次の例では、<code>Y</code>に<code>string</code>が拡張された型が定義された場合は<code>nubmer</code>になり、それ以外の場合は<code>never</code>になります。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Sample&lt;Y&gt; = Y <span class="keyword">extends</span> <span class="built_in">string</span> ? <span class="built_in">number</span> : never;</span><br></pre></td></tr></table></figure><p>三項演算子で分岐を定義しています。<br>次の様な、三項演算子のネストして記述することも可能な様です。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Sample&lt;Y&gt; = Y <span class="keyword">extends</span> <span class="built_in">string</span> ? <span class="built_in">number</span> :</span><br><span class="line">  Y <span class="keyword">extends</span> <span class="built_in">number</span> ? <span class="built_in">string</span>:</span><br><span class="line">  never;</span><br></pre></td></tr></table></figure><p>このコードは次のフローになります。</p><p><img src="https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuUBIqD9KqDMrKr0AJimjo4crYRHJukBAJ5DGYdpSs5DSKfIPb_wuiLj9eIGZDOzB0GY0QgCXuh58ce2AyqfparAB7ZUkVjouwd5Jjn9NQavnAbWBp3YO2cJZ80P2PD0CXDaEDn2a6cwe0jZPqJ0pbuCKDRO0ZSXBBKijWYhEIybCuuAGN0v06kXb0000"></p><p><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types" target="_blank" rel="noopener">ConditionalTypes</a></p><h4 id="Type-inference-in-ConditionalTypes"><a href="#Type-inference-in-ConditionalTypes" class="headerlink" title="Type inference in ConditionalTypes"></a>Type inference in ConditionalTypes</h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Expected&lt;Y&gt; = Y <span class="keyword">extends</span> &#123; expected: infer Z &#125; ? Z : never;</span><br></pre></td></tr></table></figure><p>inferでは定義した<code>Y</code>が依存している型を<code>Z</code>として利用することができます。<br>次の様なフローになります。</p><p><img src="https://www.plantuml.com/plantuml/svg/SoWkIImgAStDuUBIqD9KqDMrKr1ohIX8JIv9JNdSs1Fvk6dIq_xfZnidFksVzhYfMucBApDDGIdoSUFgnytJ7pUlVTmyyN5ps-EcnQa8JK_NpNg-R-NZngcFcteUDovxtDW2rFcuiLj9eIGZDOzB0GX0zaHZLHUhe8GmQjtJUTr05qGzRbpnk17eYcKmBJF1Re6Pf1Ibn9MQKvmA4S1kSkCg80aqF2-rBBK8g3ml9JEDYyi1I0Cz3m00"></p><p>この様に、依存している型を三項演算子の値として利用することができます。</p><p><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-inference-in-conditional-types" target="_blank" rel="noopener">Type inference in ConditionalTypes</a></p><h2 id="まとめ"><a href="#まとめ" class="headerlink" title="まとめ"></a>まとめ</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="keyword">namespace</span> QuestionerFactory &#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">create</span>&lt;<span class="title">T</span>&gt;(<span class="params">question: T</span>): <span class="title">Questioner</span>&lt;<span class="title">T</span>&gt;</span>;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">type</span> Expected&lt;Y&gt; = Y <span class="keyword">extends</span> &#123; expected: infer Z &#125; ? Z : never;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">interface</span> Questioner&lt;X&gt; &#123;</span><br><span class="line">    assert(answer: Expected&lt;X&gt;): <span class="built_in">boolean</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>この型定義は次の様なフローになります。</p><p><img src="https://www.plantuml.com/plantuml/svg/RP7DIiD04CVl-nJBdbIeJr1BBteNUeWL3o6TOg2c9TcA8eApMmHDBugXw44GHVp0uWFuyJ13rCTmaXWQeRVPtT_y_hExpKYxeHxkz8HOM5gKjLf3glKXHDhl1n2kkPxkX_lBNWYkXdgheOJ8aP_F2pAdwT4TuNY6Cq3uJFX49cPom6O_EwagbJmGiiXnv8figNBbHX64ckmDcNUobkm9uJHzIxwIrwouZZbgTMy0dePEtQa67VwBAfbAq0O7EKhuNIwpFZiX-qBsZEmncLluQs1_Ehtw_dWaJCYC2Uy9Hnn062kfjo5WNI7SdCjjIJLJvGERypRX0-64cRBbCX-vDAcs4j2BeDYOR-PRueZb0Upoyma8EdutktEJA_wuNm00"></p><p>このため、assertメソッドの引数の型がZであるということがわかる様になります。</p><p>この記事のSinonのStubについて説明している例が、実例として非常にわかりやすかったです。</p><p><a href="https://qiita.com/Quramy/items/b45711789605ef9f96de#%E4%BE%8B2-sinonjs%E3%81%AEstub" target="_blank" rel="noopener">https://qiita.com/Quramy/items/b45711789605ef9f96de#%E4%BE%8B2-sinonjs%E3%81%AEstub</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;本日の勉強会で、TypeScriptの&lt;a href=&quot;https://github.com/Microsoft/TypeScript/pull/21496&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;このPR&lt;/a&gt;をみてinferとは何かを調べたので
      
    
    </summary>
    
    
      <category term="TypeScript" scheme="https://i.botch.me/tags/TypeScript/"/>
    
  </entry>
  
  <entry>
    <title>jsdomでinput[type=file]のファイル選択のテストを書く</title>
    <link href="https://i.botch.me/2018/10/07/jsdom%E3%81%A7input-type-file-%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E9%81%B8%E6%8A%9E%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88%E3%82%92%E6%9B%B8%E3%81%8F/"/>
    <id>https://i.botch.me/2018/10/07/jsdomでinput-type-file-のファイル選択のテストを書く/</id>
    <published>2018-10-06T18:23:45.000Z</published>
    <updated>2018-10-06T18:57:23.000Z</updated>
    
    <content type="html"><![CDATA[<p>input[type=”file”]の要素では、ファイルを選択させることができない。<br>しかし、jsdomを利用してテストを書きたいときの解決方法です。</p><p>何かの参考になれば幸いです。<br>また、issueがすでにopenになっているので将来的にjsdomで別の方法で修正されるかもしれません。</p><p><a href="https://github.com/jsdom/jsdom/issues/1272" target="_blank" rel="noopener">https://github.com/jsdom/jsdom/issues/1272</a></p><h2 id="解決方法まとめ"><a href="#解決方法まとめ" class="headerlink" title="解決方法まとめ"></a>解決方法まとめ</h2><p>解決方法としては、次の手順を踏んでinput[type=”file”]要素でファイルを選択した状態を作る。</p><ol><li>FileListクラスをFile[]のprototypeとして設定する。</li><li>Object.definePropertyで、input[type=”file”]のvalueに設定する。</li></ol><h2 id="環境"><a href="#環境" class="headerlink" title="環境"></a>環境</h2><h3 id="必要なパッケージ"><a href="#必要なパッケージ" class="headerlink" title="必要なパッケージ"></a>必要なパッケージ</h3><ul><li><a href="https://www.npmjs.com/package/jsdom" target="_blank" rel="noopener">jsdom</a> … (12.1.0で検証済み)</li><li><a href="https://www.npmjs.com/package/mime-types" target="_blank" rel="noopener">mime-types</a> … (2.1.20で検証済み)</li></ul><p>JS用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev jsdom mime-types</span><br></pre></td></tr></table></figure><p>TypeScript用</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install --save-dev jsdom mime-types @types/node @types/mime-types</span><br></pre></td></tr></table></figure><h2 id="解決方法詳細"><a href="#解決方法詳細" class="headerlink" title="解決方法詳細"></a>解決方法詳細</h2><h3 id="FileListクラスのモック作成用クラスの作成"><a href="#FileListクラスのモック作成用クラスの作成" class="headerlink" title="FileListクラスのモック作成用クラスの作成"></a>FileListクラスのモック作成用クラスの作成</h3><p>次のコードの様に、ファイルパスの配列からFileListクラスのモックを作成するクラスを実装した。<br>参考: <a href="https://bitbucket.org/william_rusnack/addfilelist/src/master/" target="_blank" rel="noopener">https://bitbucket.org/william_rusnack/addfilelist/src/master/</a></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> fs <span class="keyword">from</span> <span class="string">"fs"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> path <span class="keyword">from</span> <span class="string">"path"</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> mime <span class="keyword">from</span> <span class="string">"mime-types"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> FileMockFactory</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="built_in">window</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(<span class="params"><span class="built_in">window</span>: Window</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">this</span>.window = <span class="built_in">window</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    setFileList(input: HTMLInputElement, file_paths: <span class="built_in">string</span>);</span><br><span class="line">    setFileList(input: HTMLInputElement, file_paths: <span class="built_in">string</span>[]);</span><br><span class="line"></span><br><span class="line">    setFileList(input: HTMLInputElement, file_paths: <span class="built_in">string</span>[]|<span class="built_in">string</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">typeof</span> file_paths === <span class="string">'string'</span>)</span><br><span class="line">          file_paths = [file_paths]</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (!<span class="built_in">Array</span>.isArray(file_paths)) &#123;</span><br><span class="line">          <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'file_paths needs to be a file path string or an Array of file path strings'</span>)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">const</span> file_list = file_paths.map(<span class="function"><span class="params">fp</span> =&gt;</span> <span class="keyword">this</span>.createFile(fp));</span><br><span class="line">        <span class="comment">// FileListはaddとかできないので、この様に配列をFileListとして扱わせる。</span></span><br><span class="line">        <span class="built_in">Object</span>.setPrototypeOf(file_list, <span class="built_in">Object</span>.create(<span class="keyword">this</span>.window.FileList.prototype));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// definePropertyでvalueプロパティを変更する。</span></span><br><span class="line">        <span class="built_in">Object</span>.defineProperty(input, <span class="string">'files'</span>, &#123;</span><br><span class="line">          value: file_list,</span><br><span class="line">          writable: <span class="literal">false</span>,</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> input</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      createFile(file_path: <span class="built_in">string</span>)</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="keyword">const</span> &#123; mtimeMs: lastModified &#125; = fs.statSync(file_path)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">this</span>.window.File(</span><br><span class="line">          [fs.readFileSync(file_path)],</span><br><span class="line">          path.basename(file_path),</span><br><span class="line">          &#123;</span><br><span class="line">            lastModified,</span><br><span class="line">            <span class="keyword">type</span>: mime.lookup(file_path) || <span class="string">''</span>,</span><br><span class="line">          &#125;</span><br><span class="line">        )</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="使い方"><a href="#使い方" class="headerlink" title="使い方"></a>使い方</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> assert <span class="keyword">from</span> <span class="string">'power-assert'</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> JSDOM <span class="keyword">from</span> <span class="string">'jsdom'</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> path <span class="keyword">from</span> <span class="string">"path"</span>;</span><br><span class="line"><span class="keyword">import</span> FileMockFactory <span class="keyword">from</span> <span class="string">'../../TestHelpers/FileMockFactory'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fixture = <span class="string">`&lt;html&gt;</span></span><br><span class="line"><span class="string">&lt;head&gt;&lt;/head&gt;</span></span><br><span class="line"><span class="string">&lt;body&gt;</span></span><br><span class="line"><span class="string">    &lt;input type="file" id="file-input"&gt;</span></span><br><span class="line"><span class="string">&lt;/body&gt;</span></span><br><span class="line"><span class="string">&lt;/html&gt;`</span>;</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'file attached test'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">let</span> jsdom: JSDOM.JSDOM;</span><br><span class="line">    <span class="keyword">let</span> fileMockFactory: FileMockFactory;</span><br><span class="line"></span><br><span class="line">    before(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        jsdom = <span class="keyword">new</span> JSDOM.JSDOM(fixture);</span><br><span class="line">        fileMockFactory = <span class="keyword">new</span> FileMockFactory(jsdom.window);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    describe(<span class="string">'file attached test'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">        it(<span class="string">'should setting FileList.'</span>, <span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">const</span> targetElement = &lt;HTMLInputElement&gt;jsdom.window.document.getElementById(<span class="string">'file-input'</span>);</span><br><span class="line">            <span class="keyword">const</span> generator = <span class="keyword">new</span> FileUpload(parser);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// こんな感じで設定する。</span></span><br><span class="line">            fileMockFactory.setFileList(targetElement, [__filename]);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// これでfilesプロパティにセットされているはず。</span></span><br><span class="line">            assert.equal(targetElement.files[<span class="number">0</span>].name, path.basename(__filename));</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://github.com/jsdom/jsdom/issues/1272" target="_blank" rel="noopener">jsdom issue</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;input[type=”file”]の要素では、ファイルを選択させることができない。&lt;br&gt;しかし、jsdomを利用してテストを書きたいときの解決方法です。&lt;/p&gt;
&lt;p&gt;何かの参考になれば幸いです。&lt;br&gt;また、issueがすでにopenになっているので将来的にjsdom
      
    
    </summary>
    
    
      <category term="TypeScript" scheme="https://i.botch.me/tags/TypeScript/"/>
    
      <category term="jsdom" scheme="https://i.botch.me/tags/jsdom/"/>
    
      <category term="test" scheme="https://i.botch.me/tags/test/"/>
    
  </entry>
  
  <entry>
    <title>TypeScriptのFactoryクラスでDRYする</title>
    <link href="https://i.botch.me/2018/06/07/TypeScript%E3%81%AEFactory%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A7DRY%E3%81%99%E3%82%8B/"/>
    <id>https://i.botch.me/2018/06/07/TypeScriptのFactoryクラスでDRYする/</id>
    <published>2018-06-06T16:25:52.000Z</published>
    <updated>2018-06-06T18:17:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="問題"><a href="#問題" class="headerlink" title="問題"></a>問題</h2><p>私は、業務では主にPHPとJavascriptを書き、ごく稀にJavaやSwiftをたまに触ります。<br>また、趣味で最近TypeScriptを触っています。</p><p>PHPでこんな感じでFactoryパターンを書くと、DRYで好きです。</p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Factory</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> $class_map;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">$this</span>-&gt;class_map = <span class="keyword">new</span> SplObjectStorage();</span><br><span class="line">    <span class="keyword">$this</span>-&gt;class_map[MammalianType::Dog()] = Dog::class;</span><br><span class="line">    <span class="keyword">$this</span>-&gt;class_map[MammalianType::Cat()] = Cat::class;</span><br><span class="line">    <span class="keyword">$this</span>-&gt;class_map[MammalianType::Capibara()] = Capibara::class;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">create</span><span class="params">(MammalianType $mamalian_type, PetSource $source)</span>: <span class="title">Mammalian</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    assert(<span class="keyword">isset</span>(<span class="keyword">$this</span>-&gt;class_map[$mamalian_type]));</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">$this</span>-&gt;class_map[$mamalian_type]([</span><br><span class="line">       <span class="string">'name'</span> =&gt; $source-&gt;name, <span class="comment">// constructorに渡すものは全て一緒で引数からインジェクトしたものを入れる。</span></span><br><span class="line">       <span class="string">'blood_type'</span> =&gt; $source-&gt;blood_type,</span><br><span class="line">    ]);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>しかし、Java・Swiftなどの静的型付言語を触る時に同じような実装をするには、どうしたら良いものかと頭を抱えてしまいます。<br>PHPでも$class_mapプロパティに代入されているSplObjectStorageのvalueは厳密にはstringなのです。</p><h3 id="Javaでの実装"><a href="#Javaでの実装" class="headerlink" title="Javaでの実装"></a>Javaでの実装</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Factory</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="comment">// Any!?</span></span><br><span class="line">  <span class="keyword">private</span> Map&lt;MammalianType, Any&gt; classMap;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Factory</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="comment">// Any!?</span></span><br><span class="line">    <span class="keyword">this</span>.classMap = <span class="keyword">new</span> HashMap&lt;MammalianType, Any&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// どう書けば良いか不明</span></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> Mammalian <span class="title">create</span><span class="params">(MammalianType mamalianType, PetSource source)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="comment">// Any!?</span></span><br><span class="line">    Any constructor = <span class="keyword">this</span>.classMap.get(mamalianType);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> constructor(source.name, source.bloodType);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>もちろんHashMapのvalueをStringにしてRefrectionを利用することで、おそらくインスタンス化はできます。<br>しかし、そんなことのためにRefrectionを利用するのも筋が悪そうですし、何より私はそれを美しいと感じません。</p><p>それではこんなコードはどうでしょうか？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Factory</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> Mammalian <span class="title">create</span><span class="params">(MammalianType mamalianType, PetSource source)</span> <span class="keyword">throws</span> Error</span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    Mammalian result;</span><br><span class="line">    <span class="keyword">switch</span> (mamalianType)</span><br><span class="line">    &#123;</span><br><span class="line">       <span class="keyword">case</span> Dog:</span><br><span class="line">         result = <span class="keyword">this</span>.createDog(source);</span><br><span class="line">         <span class="keyword">break</span>;</span><br><span class="line">       <span class="keyword">case</span> Cat:</span><br><span class="line">         result = <span class="keyword">this</span>.createCat(source);</span><br><span class="line">         <span class="keyword">break</span>;</span><br><span class="line">       <span class="keyword">case</span> Capibara:</span><br><span class="line">         result = <span class="keyword">this</span>.createCapibara(source);</span><br><span class="line">         <span class="keyword">break</span>;</span><br><span class="line">       <span class="keyword">default</span>:</span><br><span class="line">         <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">'未定義'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> Dog <span class="title">createDog</span><span class="params">(PetSource source)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Dog(source.name, source.bloodType);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> Cat <span class="title">createCat</span><span class="params">(PetSource source)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Cat(source.name, source.bloodType);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">private</span> Dog <span class="title">createCapibara</span><span class="params">(PetSource source)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Capibara(source.name, source.bloodType);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>それぞれのメソッドを定義しました。<br>私は、これでは以下のことを不安に感じてしまうのです。</p><ul><li>createXXXメソッドの中身がDRYにできないのか。</li><li>Mammalianクラスの子クラスが増えた場合にメソッドとswitch文両方を追加しなければならないのか。</li></ul><p>この不安は私が静的型付言語に慣れていないので発生する不安なのかはわかりません。</p><p>この点で、TypeScriptでは途中までうまくいきました。</p><h3 id="TypeScriptでの実装"><a href="#TypeScriptでの実装" class="headerlink" title="TypeScriptでの実装"></a>TypeScriptでの実装</h3><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Factory</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">private</span> classMap: Map&lt;MammalianType, <span class="keyword">typeof</span> Mammalian&gt;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params"></span>)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">this</span>.classMap = <span class="keyword">new</span> Map&lt;MammalianType, <span class="keyword">typeof</span> Mammalian&gt;();</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Dog, Dog);</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Cat, Cat);</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Capibara, Capibara);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> create(mamalianType: MammalianType, source: PetSource): Mammalian </span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>.classMap.has(mammalianType)) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'定義されていない哺乳類種別です。'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> mammalianConstructor: <span class="keyword">typeof</span> Mammalian = <span class="keyword">this</span>.classMap.get(mamalianType);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> mammalianConstructor(&#123;name: source.name, bloodType: source.bloodType&#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>このコードには重大な問題があります。<br>Mammalianをabstractクラスにできないのです。</p><p>ここまでの私の要求は</p><ol><li>FactoryクラスをDRYに実装したい。</li><li>Mammalianクラスを抽象クラスにしたい。</li></ol><p>この二つですが、静的型付言語ではこれらの要求に沿う実装を行うことが難しく感じました。</p><p>しかし、TypeScriptはこのような要求に応えることのできる言語でした。</p><h2 id="解決"><a href="#解決" class="headerlink" title="解決"></a>解決</h2><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> MammalianConstructor &#123;</span><br><span class="line">  <span class="keyword">new</span> (source: PetSource) : Mammalian;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Factory</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">private</span> classMap: Map&lt;MammalianType, MammalianConstructor&gt;; <span class="comment">// ここの型が変わった。</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">constructor</span>(<span class="params"></span>)</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">this</span>.classMap = <span class="keyword">new</span> Map&lt;MammalianType, MammalianConstructor&gt;();</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Dog, Dog);</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Cat, Cat);</span><br><span class="line">    <span class="keyword">this</span>.classMap.set(MammalianType.Capibara, Capibara);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> create(mamalianType: MammalianType, source: PetSource): Mammalian </span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>.classMap.has(mammalianType)) &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'定義されていない哺乳類種別です。'</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">const</span> mammalianConstructor: MammalianConstructor = <span class="keyword">this</span>.classMap.get(mamalianType);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> mammalianConstructor(source);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>なんということでしょう。</p><p>TypeScriptでは、interfaceにインスタンス化するときの返り値を定義することができるため、<br>intefaceへ依存することで、私の要求を全て叶えることができました。</p><p>私は、この発見をしたときすごく嬉しかったです。<br>同じようなことで困っている人がいたら、この記事が参考になれば幸いです。</p><h2 id="余談"><a href="#余談" class="headerlink" title="余談"></a>余談</h2><h3 id="TypeScript"><a href="#TypeScript" class="headerlink" title="TypeScript"></a>TypeScript</h3><p>本日参加した、<a href="https://nfug.connpass.com" target="_blank" rel="noopener">Nagoya Frontend User Group</a>のもくもく会で以下のようにすれば、Enumと派生クラスの網羅性を保証できると教えていただきました。<br>ありがとうございます。🤗</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">interface</span> MammalianConstructor &#123;</span><br><span class="line">  <span class="keyword">new</span> (source: PetSource) : Mammalian;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">class</span> Factory</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">private</span> toMammalianConstructor() : MammalianConstructor</span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">switch</span> (<span class="keyword">type</span>) &#123;</span><br><span class="line">      <span class="keyword">case</span> MarkerType.First: <span class="keyword">return</span> FirstMarker;</span><br><span class="line">      <span class="keyword">case</span> MarkerType.Second: <span class="keyword">return</span> SecondMarker;</span><br><span class="line">      <span class="keyword">case</span> MarkerType.DeliveryService: <span class="keyword">return</span> DeliveryServiceMarker;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// never型の変数に代入してEnumがすべて網羅されているかチェックする。網羅されていなければコンパイルエラー</span></span><br><span class="line">    <span class="keyword">const</span> unreachableCheck: never = <span class="keyword">type</span>;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">public</span> create(mamalianType: MammalianType, source: PetSource): Mammalian </span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">const</span> mammalianConstructor: MammalianConstructor = <span class="keyword">this</span>.classMap.get(mamalianType);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> mammalianConstructor(source);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h3><p>あと、この記事を書いている時に以下のようにすればいけるんじゃ？という内容を見つけたのでメモ。(未検証)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Factory</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">  <span class="keyword">private</span> Map&lt;MammalianType, Class&lt;? exnteds Mammalian&gt;&gt; classMap;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="title">Factory</span><span class="params">()</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.classMap = <span class="keyword">new</span> HashMap&lt;MammalianType, Class&lt;? exnteds Mammalian&gt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">this</span>.classMap.put(MammalianType.Dog, Dog);</span><br><span class="line">    <span class="keyword">this</span>.classMap.put(MammalianType.Cat, Cat);</span><br><span class="line">    <span class="keyword">this</span>.classMap.put(MammalianType.Capibara, Capibara);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> Mammalian <span class="title">create</span><span class="params">(MammalianType mamalianType, PetSource source)</span></span></span><br><span class="line"><span class="function">  </span>&#123;</span><br><span class="line">    Class&lt;? exnteds Mammalian&gt; constructor = <span class="keyword">this</span>.classMap.get(mamalianType);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> constructor(source.name, source.bloodType);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://stackoverflow.com/questions/39614311/class-constructor-type-in-typescript" target="_blank" rel="noopener">Stack Overflow - Class constructor type in typescript?</a><br><a href="https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript/39419171#39419171" target="_blank" rel="noopener">Stack Overflow - How do I check that a switch block is exhaustive in TypeScript?</a><br><a href="https://nfug.connpass.com" target="_blank" rel="noopener">Nagoya Frontend User Group</a><br><a href="https://waman.hatenablog.com/entry/20101003/1286102501" target="_blank" rel="noopener">いまさら!? Class クラス (2) ： Class オブジェクトの取得方法</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;問題&quot;&gt;&lt;a href=&quot;#問題&quot; class=&quot;headerlink&quot; title=&quot;問題&quot;&gt;&lt;/a&gt;問題&lt;/h2&gt;&lt;p&gt;私は、業務では主にPHPとJavascriptを書き、ごく稀にJavaやSwiftをたまに触ります。&lt;br&gt;また、趣味で最近TypeScri
      
    
    </summary>
    
    
      <category term="Factory" scheme="https://i.botch.me/tags/Factory/"/>
    
      <category term="DRY" scheme="https://i.botch.me/tags/DRY/"/>
    
      <category term="TypeScript" scheme="https://i.botch.me/tags/TypeScript/"/>
    
  </entry>
  
</feed>
