はじめてのデザインパターン #1
はじめに
ちょこちょこ自分で機能を作ることはできてきたけど、設計がイケてなかったりして苦戦していた時に先輩に相談したところ「デザインパターンとか知っておくと損ないかもね」と教えてもらった。
とはいえ「デザインパターンって何?」状態だったので調べてみたのでメモ。
参考
自分がPHP使いということもあり、以下を参考にしようとした
とおもったら、廃版になってて著者の方が本内容をはてブに掲載してくれていたのでこちらでインプット
感謝です。
デザインパターンとは
ソフトウェアを何度も設計していると、あちらこちらで共通する機能や問題があることに気づくことがあります。...。経験を積んだ開発者であれば、「こういった問題を解決するには、こういう構造でプログラムを書けばうまくいく」といったノウハウを持っているものです。
このノウハウを、他の開発者が再利用できるようカタログ化したものがデザインパターンです。
一言でいうとオブジェクト指向プログラミングにおいて開発者がソフトウェア設計する上での「型」
記事内では
「設計のコツの虎の巻」
と表現されている。
メリット
じゃあ、「デザインパターン学んでどうなるのさ?」という話で行くといくつか列挙されていますが個人的には
- 再利用性が向上する
- 保守性が向上する
が大きいメリットだなと感じます。
再利用性が向上
クラスの一つ一つの独立性が担保される設計になるので、クラス同士が疎結合になり再利用性が高くなる
保守性が向上する
独立性が高くなるとクラスが持つ役割がしっかり分担されるので、急な仕様変更や機能追加の対応がしやすくなる
例えば、LaravelでDBから値を取得する際にAカラムではなくBカラムの値をwhereする、みたいな修正があった時に
// 当初仕様 $user = User::where('Aカラム', $A_value)->first(); ↓ // 仕様変更 $user = User::where('Bカラム', $B_value)->first();
これが一か所のクラスのメソッドにまとまって(抽象化されて)いて、呼び出し側は全て抽象メソッドを呼び出していれば修正は一か所ですが、上記の記述をそのまま呼び出し側に書いていたら修正規模は100、200...箇所と膨大なものになる、という話。
デメリット
デメリットもいくつか掲載されていますが例えば
- 開発者同士がデザインパターンを知らないとコミュニケーションコストが生まれる
- 使いこなすまでに時間がかかる
など、物理的に仕方ないかな、ということが多いです。
ただ、
- 無理にでもパターンを適用しようとする
- パターンのクラス図をそのまま適用しようとする
あたりはデザインパターンを利用する上で注意点かなと思います。
冒頭に「デザインパターンは虎の巻」と記載されていても絶対的なものではなく、むしろやりすぎると逆にイケていない設計になる、ということも注意しておく必要あり、と学びました。
どんなものがあるか
とは言え、やはり同じプロダクトの中で開発者同士が共通の型を使って、再利用性の富んだ設計ができるようになるのは大きな魅力なので、どんなものがあるか見ていきたい。
※第1回なので超基礎のみにしています。
#1, テンプレートメソッドパターン
こんな時に使う
今目の前に以下のようなクラスが2つあるとします。
↓ListDisplayクラス
<?php require_once 'AbstractDisplay.class.php'; class ListDisplay { /** * 表示するデータ */ private $data; /** * コンストラクタ * @param array 表示するデータ */ public function __construct($data) { $this->data = $data; } /** * クライアントから呼び出されるメソッド */ public function display() { $this->displayHeader(); $this->displayBody(); $this->displayFooter(); } /** * ヘッダを表示する */ protected function displayHeader() { echo '<dl>'; } /** * ボディ(クライアントから渡された内容)を表示する */ protected function displayBody() { foreach ($this->getData() as $key => $value) { echo '<dt>Item ' . $key . '</dt>'; echo '<dd>' . $value . '</dd>'; } } /** * フッタを表示する */ protected function displayFooter() { echo '</dl>'; } }
↓TableDisplayクラス
<?php require_once 'AbstractDisplay.class.php'; class TableDisplay { /** * 表示するデータ */ private $data; /** * コンストラクタ * @param array 表示するデータ */ public function __construct($data) { $this->data = $data; } /** * クライアントから呼び出されるメソッド */ public function display() { $this->displayHeader(); $this->displayBody(); $this->displayFooter(); } /** * ヘッダを表示する */ protected function displayHeader() { echo '<table border="1" cellpadding="2" cellspacing="2">'; } /** * ボディ(クライアントから渡された内容)を表示する */ protected function displayBody() { foreach ($this->getData() as $key => $value) { echo '<tr>'; echo '<th>' . $key . '</th>'; echo '<td>' . $value . '</td>'; echo '</tr>'; } } /** * フッタを表示する */ protected function displayFooter() { echo '</table>'; } }
↓利用側(クライアント)
<?php require_once 'ListDisplay.class.php'; require_once 'TableDisplay.class.php'; $data = array('Design Pattern', 'Gang of Four', 'Template Method Sample1', 'Template Method Sample2'); $display1 = new ListDisplay($data); $display2 = new TableDisplay($data); $display1->display(); echo '<hr>'; $display2->display();
TableDisplayクラスとListDisplayクラスを見てあなたは思います。
「なんだかすごく似ている処理をしているぞ。。。」
この時に使うのがテンプレートメソッド。
使い方
- 複数の似た処理のクラスの抽象化クラスを1個作る
- 抽象クラスには似た処理のクラス(具象クラス)でやってほしい処理のabstractでの定義と共通の処理を定義
- 抽象クラスをexxtendsした似た処理のクラス(具象クラス)では、abstractでのメソッドの具体的な処理の実装を定義
- 利用側(クライアント)で、具象クラス経由で抽象クラスの共通処理を呼び出す
言葉で話してもわかりにくいのでソースコードにて
修正後
↓AbstractDisplayクラス(新規作成) ここに共通の処理と具象クラスで処理してほしいメソッドを定義
<?php /** * AbstractClassクラスに相当する */ abstract class AbstractDisplay { /** * 表示するデータ。下記コンストラクタで値が格納される */ private $data; /** * コンストラクタ。実際にインスタンス化するのは具象クラスなので、具象クラスをnewしたら呼ばれる * @param array 表示するデータ */ public function __construct($data) { $this->data = $data; } /** * template methodに相当する(共通処理) */ public function display() { $this->displayHeader(); $this->displayBody(); $this->displayFooter(); } /** * データを取得する(共通処理) */ public function getData() { return $this->data; } // 具象クラスで実装するメソッドたち /** * ヘッダを表示する * サブクラスに実装を任せる抽象メソッド */ protected abstract function displayHeader(); /** * ボディ(クライアントから渡された内容)を表示する * サブクラスに実装を任せる抽象メソッド */ protected abstract function displayBody(); /** * フッタを表示する * サブクラスに実装を任せる抽象メソッド */ protected abstract function displayFooter(); }
↓ListDisplayクラス
データをリストで表示するための処理を書く
<?php require_once 'AbstractDisplay.class.php'; /** * 具象クラスに相当する */ class ListDisplay extends AbstractDisplay { // 抽象クラスで実装することを強制したabstractで定義したメソッドたち(これがないとエラーになる) /** * ヘッダを表示する */ protected function displayHeader() { echo '<dl>'; } /** * ボディ(クライアントから渡された内容)を表示する */ protected function displayBody() { foreach ($this->getData() as $key => $value) { echo '<dt>Item ' . $key . '</dt>'; echo '<dd>' . $value . '</dd>'; } } /** * フッタを表示する */ protected function displayFooter() { echo '</dl>'; } }
↓TableDisplayクラス
データをテーブルで表示するための処理を書く
<?php require_once 'AbstractDisplay.class.php'; /** * ConcreteClassクラスに相当する */ class TableDisplay extends AbstractDisplay { // 抽象クラスで実装することを強制したabstractで定義したメソッドたち(これがないとエラーになる) /** * ヘッダを表示する */ protected function displayHeader() { echo '<table border="1" cellpadding="2" cellspacing="2">'; } /** * ボディ(クライアントから渡された内容)を表示する */ protected function displayBody() { foreach ($this->getData() as $key => $value) { echo '<tr>'; echo '<th>' . $key . '</th>'; echo '<td>' . $value . '</td>'; echo '</tr>'; } } /** * フッタを表示する */ protected function displayFooter() { echo '</table>'; } }
↓利用側(クライアント)・・・変更なし
<?php require_once 'ListDisplay.class.php'; require_once 'TableDisplay.class.php'; $data = array('Design Pattern', 'Gang of Four', 'Template Method Sample1', 'Template Method Sample2'); $display1 = new ListDisplay($data); $display2 = new TableDisplay($data); $display1->display(); echo '<hr>'; $display2->display();
- やっているのは「継承」を使った実装
- 重複部分を抽象クラスに実装することで、実際にインスタンス化する具象クラスの記述量を減らすことができ、拡張性もUP
- 似たような処理(ただ文字列を羅列するだけの処理など)が欲しくなったときに同じように具象クラスのメソッドを実装してあげればいい
- 呼び出し側に影響が出ないので、呼び出し側は抽象クラスのメソッドを使っていることを意識しなくていいのは嬉しい
おわりに
update/save, create/makeの違い
update/saveについて
update
差分を見ずにUPDATEのSQLを発行する
Customer::where('id', 1) ->update(['name' => $request->name]);
- ここで
$request->name
が既存のDBの内容と同じだった場合でもUPDATEのSQLが発行され、クエリが実行されることになる - updated_atがカラムにあれば現在時刻に更新する
save
差分を見てUPDATEのSQLを発行する
$customer = Customer::find(1) $customer->name = $request->name; $customer->save();
- ここで
$request->name
が既存のDBの内容と同じだった場合、差分がないことを検知しUPDATEのSQLは発行されず、クエリが実行されない - updated_atカラムも更新はされない
<補足>saveのお供「fill」
やっていることはモデルのプロパティの変更を一括で出来てしまう
public function create(UserControllerRequest $request) { $user = $this->user; $user->name = $request->name; $user->password = $request->password; $user->save(); }
例えば、こんなコードを
public function create(UserControllerRequest $request) { $this->user->fill($request->all())->save(); }
fillを使ってスッキリ書ける、というメリットがある。
saveとの相性がいいので、頻出する。
所感
余計なクエリを発行しなくていいので、updateよりもfill&saveが主流ではないかなと思いました。
※実際にアサインしているプロジェクトもfill, saveが主流
update/saveの参考記事
- saveとupdateの違い
https://tech1313.hatenablog.com/entry/2020/10/21/155947
- fillについて
https://zenn.dev/naonao70/articles/a8abba2b170c31
ありがとうございます。
create/makeについて
create
Model::create()
に該当。INSERTのSQLが発行され、クエリが実行される
インスタンスの作成 → 属性の代入 → データの保存を一気通貫
make
new Model()
に該当。INSERTのSQLは発行されない。
インスタンスの作成 → 属性の代入 まで実行
所感
要はDBに保存しているか、せずにモデルのインスタンス化だけしているかの違い
create/makeの参考記事
- createとmakeについて
https://qiita.com/nunulk/items/06370af1594a10faa749#eloquent-model-factory-とは
- 特にcreateについて
https://katsusand.dev/posts/laravel-save-data-db/
ありがとうございます。
例外のカスタム~reportable, renderable~
reportable
https://readouble.com/laravel/8.x/ja/errors.html#the-exception-handler
クロージャ―で処理をしたい時とかに使う
※以下記事の感じ。実際に使ってないけど、例外投げられた時にslackへnotifyする、みたいな
https://tsyama.hatenablog.com/entry/laravel-slack-notification
renderable
https://readouble.com/laravel/8.x/ja/errors.html#rendering-exceptions
- デフォルトではmessageやstatus code、traceが返されるが、これを変換する場合に使う
- ex. 作成した例外クラスで404を返したいときとか
S3基本概念の理解
はじめに
LaravelでS3にある画像データを処理する機能を実装しようとなった際につまづき、自力で解決しきれなかったので、特に圧倒的不足を感じたS3の知識を一通り確認
参考
AWS公式Doc(https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/Welcome.html)
機能
ストレージクラス
S3 Standard, S3 標準 – IA, S3 Glacier Instant Retrieval...などユースケースに応じていろんな機能がある
アクセス管理
S3ブロックパブリックアクセス
一般ネットワークからのアクセスを拒否。オブジェクトごとにURLが付与されるが、URL踏んでもアクセスできないようになる
アクセスコントロールリスト(ACL)
バケット・オブジェクトへの書き込み、読み込みの許可を管理
…などなど
データ処理
S3内のデータに変更があった時、イベントを発生させたり。
ストレージのログ記録とモニタリング
請求金額が上限行きそうなときのアラート発生させたり
分析とインサイト
保存されているデータの状況に応じて、コストの最適化をしてくれたり。
Amazon S3 の仕組み
これが一番知りたかった。
これなしではサーバーサイドでS3内のデータを操作するのは難しいと思う
Bucket
Object
Keys
- オブジェクトの固有の識別子
S3バージョニング
- 同じバケット内で同一のオブジェクト(複数のバリアント)を保持する時に使用
- 障害対応に有効
バージョンID
- バージョニング有効時にオブジェクトに付与されるID
バケットポリシー
- IAMごとに、バケットとそのオブジェクトへのアクセスを制限(認可)
アクセスコントロールリスト (ACL)
- 個々のバケットとオブジェクトへの読み取り・書き込みの制限ができる
- 直近はあまり使用がないらしい
- IAMで制御できちゃうからかな?
- サーバーサイド側でその制御(他のユーザーのマイページ内の画像にアクセス、など)ができてしまうからかな?
Amazon S3 の最新のユースケースの大部分では ACL を使用する必要がなくなっており、オブジェクトごとに個別にアクセスを制御する必要がある特例を除き、ACL を無効にすることをお勧めします。
S3 アクセスポイント
arn:aws:s3:***
みたいなやつ- S3 オブジェクトのオペレーション (
GetObject
やPutObject
など) を実行するために使用
ライフサイクルフックの違いによるレンダリングのタイミング~img src編③~
はじめに
前回はv-ifの条件をurl(親のプロパティ)にしていたが、今回はchildUrl(子のdataプロパティ)にする
話すこと
前回と同じで、以下コードを例にどのライフサイクルフックをつかったら、どんな挙動を示すのかを順にみていく
やりたいこと
子コンポーネントで定義するimgタグのsrc属性の内容に応じて表示する画像を変更する
OKな時
NGな時
ソースコード
親コンポ―ネント
<template> <div> <ImageChildComponent :url="url" @replace="replaceUrl" ></ImageChildComponent> </div> </template> <script> import ImageChildComponent from './ImageChildComponent.vue' export default { components: { ImageChildComponent }, data() { return { url: '', } }, methods: { replaceUrl(newUrl) { console.log(newUrl); this.url = newUrl; }, } } </script>
親コンポーネントでやっていることは前回と変わらない
子コンポーネント
<template> <div> <!-- OK時の画像 --> <div v-if="childUrl" // 変更 > <h2>OK!!!</h2> <img :src="childUrl" width=200 > </div> <!-- NG時の画像 --> <div v-else > <h2>NG...</h2> <img src="../assets/no-image.png" width=200 > </div> </div> </template> <script> export default { props: { url: { type: String, default: "", }, }, data() { return { childUrl: '', } }, beforeCreate() { console.log('beforeCreate: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; }, created() { console.log('created: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; }, beforeMount() { console.log('beforeMount: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // console.log('beforeMount: ', this.childUrl); }, mounted() { console.log('mounted: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // console.log('mounted: ', this.childUrl); }, methods: { getUrl() { console.log('methods(getUrl): ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // return this.childUrl; } } } </script>
<template>
でやっていることは基本的には一緒ですが、変化点は以下
- v-ifの条件が子コンポーネント特有のdataプロパティ
childUrl
になっている
<script>
でやっていることは前回と全く一緒
懸賞方法
前回同様、コメントアウトしてある箇所を随時コメントインして実行していく
デフォルト
ブラウザ
コンソール
- 今回はエラーがでずにchildUrlがundifiedになっている…
ギモン
- beforeCreateの時点ではdata()は実行されていないから、前回同様「childUrlなんてない!」って怒られてもいいと思うけど、なんで今回undefinedが定義されているんだろう…
beforeCreated
... beforeCreate() { console.log('beforeCreate: ', this.childUrl); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; }, ...
ブラウザ
デフォルトと同じなので割愛
コンソール
コチラもデフォルトと同じなので割愛
デフォルトでchildUrlを認識しているのであれば、beforeCreateでchildUrlにnewUrl代入すればDOMに反映されると思ったが、違った
created
created() { console.log('created: ', this.childUrl); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; console.log('created: ', this.childUrl); },
ブラウザ
コンソール
今までで一番Vueライフサイクルのイメージ通りの挙動した
consoleもcreatedでchildUrlにnewUrlを定義した後は、全てconsole.logでnewUrlの値が出力できている
beforeMount
beforeMount() { console.log('beforeMount: ', this.childUrl); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; console.log('beforeMount: ', this.childUrl); },
ブラウザ
createdと同じ
コンソール
こちらも想定通り
boforeMount以降はnewUrlが表示されている
mounted
mounted() { console.log('mounted: ', this.childUrl); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; console.log('mounted: ', this.childUrl); },
ブラウザ
created, beforeMouteと同じ
コンソール
mountedでnewUrlをchildUrlに代入しているので、mountedでdataが変更されたタイミングでre-renderingされ、mountedでconsoleに表示される
methods
ここで想定するのは以下のようなソースコード
<template> <div v-if="url" > <h2>OK!!!</h2> <img :src="getUrl()" // 変更 width=200 > </div> ... </template> <script> export default { ..., methods: { getUrl() { console.log('methods(getUrl): ', this.childUrl); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; return this.childUrl; } ... } </script>
ブラウザ
コンソール
- v-ifで表示childUrlがnullなので、常にno-imageが表示される
getUrlをv-ifで呼び出した場合
<div v-if="getUrl()" <!-- 変更 --> > <h2>OK!!!</h2> <img :src="getUrl()" width=200 > </div>
ブラウザ
コンソール
前回同様で
- mountedまでは1回めのDOM生成時(v-if, imgのsrc属性)のgetUrl呼び出し時のconsole
- mounted以降は2回目のDOM生成時のgetUrl呼び出し時のconsole
の出力結果
参考記事
https://jp.vuejs.org/v2/guide/instance.html#ライフサイクルダイアグラム
所見
- createdでdataプロパティの値を変更するとそれ以降のライフサイクルフックでも変更後の値が取得できる点から、一番Vueのライフサイクル通りの挙動をしていた
- 前回同様、最後のgetUrの例はライフサイクルを知るのにかなりわかりやすかった
- imgのsrcの変遷を追うことでだいぶ理解はできたと思う
ライフサイクルフックの違いによるレンダリングのタイミング~img src編②~
はじめに
今回はv-ifの値が子コンポーネントで定義したdataプロパティだった時はどうなるのか、といいう確認がしたい
いまだに画面描画時に処理をしたい時にbeforeCreate, created, beforeMount, mountedの内どれを使えばいいのかの判断も曖昧なので、そんな自分の一助になればと思った
加えてmethodsでsrcの値をreturnするパターンはどうなのか、ということも確認
話すこと
前回と同じで、以下コードを例にどのライフサイクルフックをつかったら、どんな挙動を示すのかを順にみていく
やりたいこと
子コンポーネントで定義するimgタグのsrc属性の内容に応じて表示する画像を変更する
OKな時
NGな時
ソースコード
親コンポ―ネント
<template> <div> <ImageChildComponent :url="url" @replace="replaceUrl" ></ImageChildComponent> </div> </template> <script> import ImageChildComponent from './ImageChildComponent.vue' export default { components: { ImageChildComponent }, data() { return { url: '', } }, methods: { replaceUrl(newUrl) { console.log(newUrl); this.url = newUrl; }, } } </script>
親コンポーネントでやっていることは前回と変わらない
子コンポーネント
<template> <div> <!-- OK時の画像 --> <div v-if="url" > <h2>OK!!!</h2> <img :src="childUrl" width=200 > </div> <!-- NG時の画像 --> <div v-else > <h2>NG...</h2> <img src="../assets/no-image.png" width=200 > </div> </div> </template> <script> export default { props: { url: { type: String, default: "", }, }, data() { return { childUrl: '', } }, beforeCreate() { console.log('beforeCreate: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; }, created() { console.log('created: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; }, beforeMount() { console.log('beforeMount: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // console.log('beforeMount: ', this.childUrl); }, mounted() { console.log('mounted: ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // console.log('mounted: ', this.childUrl); }, methods: { getUrl() { console.log('methods(getUrl): ', this.childUrl); // const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; // this.childUrl = newUrl; // return this.childUrl; } } } </script>
<template>
でやっていることは基本的には一緒ですが、変化点は以下
- 表示するimgのsrc属性の値が子コンポーネント特有のdataプロパティ
childUrl
になっている
<script>
でやっていることも基本一緒ですが、変化点は以下
- 子コンポーネント特有のプロパティ
childUrl
を定義 - 各ライフサイクルフックで
childUrl
にnewUrl
を代入 - consoleで出力するのは
childUrl
検証方法
手順として前回同様、コメントアウトしてある箇所を随時コメントインして実行していく
デフォルト
ブラウザ
コンソール
前回同様、エラーが2つ。理由も同じなので割愛
beforeCreated
... beforeCreate() { console.log('beforeCreate: ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; }, ...
ブラウザ
デフォルトと同じなので割愛
コンソール
コチラもデフォルトと同じなので割愛
ここまでも前回同様
※beforeCreatedに console.log('beforeCreate: ', this.url); を書いていると常にエラーがでてくるので、これ以降はこの処理をコメントアウトする
created
created() { console.log('created: ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; console.log('created: ', this.url); },
ブラウザ
beforeCreateと同じ
コンソール
今回は何も表示されない
恐らくv-ifでurlがnullなので、v-else側に入っているからだと思う
そして、再インスタンス化(前回同様console.logをcreatedに追加)しても結果は変わらない
結果は割愛
created() { console.log('created: ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.$emit("replace", newUrl); // インスタンス化するために追加 console.log('created: ', this.url); },
ギモン
多分上記推測であってるはず
今回はv-elseに入って、前回はv-if側に入った点からみると
一度templateの描画、インスタンス化→urlの値が変化(前回)→再描画、インスタンス化
という流れだろうか
ただこれで行くと、前回各ライフサイクルフックでthis.urlの内容が表示されなかった点はつじつまが合わない
beforeMount
beforeMount() { console.log('beforeMount: ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; console.log('beforeMount: ', this.url); },
ブラウザ
createdと同じ
コンソール
createdと同じ
ギモン
createdと同じ
mounted
mounted() { console.log('mounted: ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.$emit("replace", newUrl); },
ブラウザ
created, beforeMouteと同じ
コンソール
created, beforeMouteと同じ
ギモン
created, beforeMouteと同じ
methods
ここで想定するのは以下のようなソースコード
<template> <div v-if="url" > <h2>OK!!!</h2> <img :src="getUrl()" // 変更 width=200 > </div> ... </template> <script> export default { ..., methods: { getUrl() { console.log('methods(getUrl): ', this.url); const newUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=='; this.childUrl = newUrl; return this.childUrl; } ... } </script>
ブラウザ
created, beforeMoute, mountedと同じ
コンソール
created, beforeMoute, mountedと同じ
getUrlをv-ifで呼び出した場合
<div v-if="getUrl()" <!-- 変更 --> > <h2>OK!!!</h2> <img :src="getUrl()" width=200 > </div>
ブラウザ
コンソール
ここで初めてブラウザに表示
コンソールの流れからして、
- beforeMountまではDOMは生成されないため
getUrl
が呼ばれないためchildUrlはnull - beforeMount以降はDOMが生成され、getUrlが呼ばれる
- console3行目:1回目のDOM生成時のv-ifでのgetUrl。初回のconsole.logなので、childUrlには何も入っていない
- console4行目:1回目のDOM生成時のimg内のgetUrl。既にchildUrlが変更されているので、newUrlが出力
- console5行目:1回目のDOM生成が終わり、dataが変更されているので、mountedが呼び出される。childUrlはgetUrlで設定したnewUrlが入っている
- console6, 7行目:それぞれ2回目のv-if, img内のgertUrl呼び出し時のconsole.log。1回目のDOM生成時に設定したchildUrlがそのまま引き継がれている
参考記事
https://jp.vuejs.org/v2/guide/instance.html#ライフサイクルダイアグラム
所見
- 今回はv-ifの条件がnullでスタートしたので、ほぼno-imageの表示であまり変化が負えなかった
- とは言え、最後のgetUrの例はライフサイクルを知るのにかなりわかりやすかった
- いくつかVueのライフサイクルとは想定外の挙動をしている、、、
- 次はdataをv-ifにした上で、ライフサイクルを追ってみる
自分のPCのネットワーク回線を知る時に使ったネットワーク知識
背景
「イナグマくんちのインターネット回線ってセキュリティ面大丈夫かな?」
と聞かれ、何調べればいいんだ?となり、先輩に手取り足取り教えてもらいながら大丈夫であることが分かった
その際にネットワーク周りの知識の不足を感じ、「これはまずい…」と思い、いろんなワードを調べた
備忘録。
パケット
- TCP/IPを使うプロトコル内で使う、通信データを分割した単位のデータ
- 分割する理由は通信経路上に流せるデータ量は有限だから
- 同じネットワーク上でやり取りしている複数の人の内、誰かが激重の通信をした際に、ネットワークの通信効率が激落ちするため
- 分割しているメリットは、通信エラーが起きれてもエラーが起きたパケットだけ送ればいいので通信効率が上がる
IPアドレス
- コンピュータやネットワーク機器の住所
- TCP/IPで接続した機器は必ず持っている
- パケットはIPアドレス単位で送られてくるので、重複はない
ipconfig
コマンドでaaa.bbb.ccc.***
が自前PCのIPv4アドレスが表示されたら、データ(パケット)の受信時には- aaa.bbb.ccc.1(デフォルトゲートウェイ、ルータ)宛にパケット送信。
- そこから先ほど表示された
***
でパケットの送付先を特定してパケットを送信
aaa.bbb.ccc
がネットワーク部、***
がホストアドレス部という- 192.~から始まるものは小規模ネットワーク用で
クラスC
となる- 規模ごとに
クラスA
は0.~、クラスB
は128.~となる
- 規模ごとに
サブネットマスク
- サブネットマスクで1つのネットワークを分割
- クラスCでも小さなネットワークでも254台のホストが使えるが、1ネットワークで大量のホストをまとめてしまうと通信トラフィックが多くなり、通信効率が下がるのでこの対処法
- 小さな会社で「事業部ごとに分けたい」という要望があるときなどに使う
- パケットの送信形式によるがネットワーク内に全送信する形式だとサイアク…
- 分割方法はネットワークアドレス部とホストアドレス部を再定義することで実現
- 例:クラスCのネットワーク
- 本来24ビットであるネットワークアドレスを、サブネットマスクとして2ビット追加した26ビットに拡張することで、サブネット2ビット+残ったホストアドレス6ビットで分割することができる
- 上記例だとホストアドレス範囲は1ネットワーク254ホストから4ネットワーク62ホスト(1ネットワークあたり)に変更できる
- 例:クラスCのネットワーク
VPN
この際なので。
https://www.ipa.go.jp/files/000024561.pdf
トンネリング
社内サーバーと自分のPC(クライアント)間をデータ通信するトンネルを作り不特定多数の公衆回線から通信を断絶する
カプセル化
仮想トンネル内を行き来するデータはトンネルに入る前に再パッケージ化し、セキュリティ性を高める
認証
トンネルに入るために認証が必要。2段階認証をすることもある
暗号化
万が一、認証が突破され再パッケージ化されたデータが盗み見された際に大丈夫なように暗号化をする
デメリット
- セキュリティは万全じゃない
- なので、そもそも危険性のあるインターネットにつなぐのはよくない
- 通信品質の低下の可能性有り
PING
https://atmarkit.itmedia.co.jp/ait/articles/0012/01/news002.html
- TCP/IPネットワークで使えるコマンド
- パケットを送り返すだけの機能
- Http通信でいうリクエストみたいなイメージ
- ICMPの「echo request/echo reply」メッセージを使った、単純なパケットの通信テストプログラム
- 通信先が稼働しているか否か確認する
ping ドメイン名/IPアドレス
実際に会社PC側でインターネットに接続していることを確認しているので、4個目あたりが原因だろうということだった
ICMP
- Internet Control Message Protocolの略
https://atmarkit.itmedia.co.jp/ait/articles/0306/13/news002.html
- ↑コチラで見ていてもなんのことかわからなかったので、導入として↓こちらを参照
https://wa3.i-3-i.info/word13132.html
とはいえ、
- 結局はプロトコルなので通信時のお約束事なのか
- 直接このプロトコルを使うことはpingコマンド時くらい
- IPやTCP、UDPなどの通信において、何らかのエラーが生じたり、障害が発生したりした場合には、このICMPを使ってエラーを通知する機能をもつ
と理解
netEnum
https://forest.watch.impress.co.jp/library/software/netenum/
https://i0.wp.com/bacque.biz/wp-content/uploads/2015/05/20150504_165759_netenum.jpg?ssl=1
ルーター
- OSI基本参照モデルのネットワーク層(第3層)位置する
- 複数の異なるネットワーク(LAN)同士の中継役
- パケットを受け取ったら、最適なルーターへパケットを転送
- 監理しているネットワークからブリッジ経由でパケットを受け取る
- ブリッジはMACアドレスで最適な場所にパケットを送る
ゲートウェイ
- OSI基本参照モデルのトランスポート層(第4層)以上に位置する
- 基本的にはパケットのプロトコルの変換を実施
- 携帯からの電子メールをPCで受け取るためには変換が必要
- ルーターがゲートウェイの機能をもち、ルーターそのものをゲートウェイと呼ぶこともある
ARP
https://wa3.i-3-i.info/word174.html
https://atmarkit.itmedia.co.jp/aig/06network/arp.html
- Address Resolution Protocolの略
- MACアドレスをあて先のIPアドレスから求めるためのプロトコル
- ARPはIPアドレスと一緒に実行し、そのIPアドレスと一致するPCのみがレスポンスを返す
参考
各URL
最後に
実は基本情報技術者試験は一度受けており、あと一歩のところで不合格となった経験があります。
当時はあまり、単語のつながりがまとまらないままだったのですが、こうやって業務でぶち当たった見ると「こういうことか!」と理解するようになりました。