Upgrade to Pro — share decks privately, control downloads, hide ads and more …

改めて、DIっていいですよね

Avatar for Haruki Haruki
March 01, 2024

 改めて、DIっていいですよね

2024/3/1トラスバンク社内LT資料

Avatar for Haruki

Haruki

March 01, 2024

Other Decks in Programming

Transcript

  1. こちらはDIP(依存性逆転の原則) SOLID原則の一つ • S (Single Responsibility Principle) ◦ 単一責任の原則 •

    O (Open-Closed Principle) ◦ オープン・クローズドの原則 • L (Liskov Substitution Principle) ◦ リスコフの置換原則 • I (Interface Segregation Principle) ◦ インターフェイス分離の原則 • D (Dependency Inversion Principle) ◦ 依存性逆転の原則
  2. DIしないとどうなるか・・・! ビジネスロジックの中に DB のCRUDやAPIのコールの 記述があって肝心のビジネ スロジックがわからない! ユニットテスト時に外部 API のコールやメール送信は走 らせたくないけど

    どうしよう・・・ ビジネスロジックは変えずに DB周りや外部APIのコール 部分だけ変えたいのに、密 結合になっちゃってて修正後 のデグレが怖いよ😭
  3. Q.このコードをどうやってUnitTestしますか? class StockUpdateNotification { function notify(User $user, Product $product) {

    $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n” $message .= “現在の品数は{$product->amount}です。\n” $mailer = new Mailer(); $mailer->send($user->email, $subject, $message) } } Mockeryを使って Mailerを強制的に書 き換えてしまおうかな UnitTestのときだけ、 実行しないif文を作ろう かな (終わりの始まり) 品のストックが増えたのでユーザに通知したい
  4. class Mailer { function send($email, $subject, $message) { // メール送信の実装

    } } これを class StockUpdateNotification { function notify(User $user, Product $product) { $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n”; $message .= “現在の品数は{$product->amount}です。\n”; $mailer = new Mailer(); $this->mailer->send($user->email, $subject, $message); } } ( ^ω^)
 ⊃ Mailer ⊂ 上位レイヤーが下位レイヤーに依存 😭
  5. class Mailer { function send($email, $subject, $message) { // メール送信の実装

    } } こうして... ( ^ω^)
 ⊃ ..ai.. ⊂ class StockUpdateNotification { function notify(User $user, Product $product) { $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n”; $message .= “現在の品数は{$product->amount}です。\n”; $mailer = new Mailer(); $this->mailer->send($user->email, $subject, $message); } } interface MaiIerInterface { function send($email, $subject, $message); } 上位レイヤーが下位レイヤーに依存 😭 MailerInterface誕生!!
  6. こうじゃ! class StockUpdateNotification { function __construct( MaiIerInterface $mailer ) {

    } function notify(User $user, Product $product) { $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n”; $message .= “現在の品数は{$product->amount}です。\n”; $mailer = new Mailer();//消える $this->mailer->send($user->email, $subject, $message); } } interface MaiIerInterface { function send($email, $subject, $message); } こんな感じで使う $mailer = new Mailer(); $notification = new StockUpdateNotification($mailer); $notification->notify(); MailerInterface誕生!! class Mailer implements MailerInterface { function send($email, $subject, $message) { // メール送信の実装 } } ( ^ω^)
 ⊃ MailerInterface ⊂ 下位レイヤーが上位レイヤーに依存! 依存性注入(DI) 依存性逆転(DIP)
  7. 例えばUnitTest用にクラスを差し替えられる class StockUpdateNotification { function __construct( MaiIerInterface $mailer ) {

    } function notify(User $user, Product $product) { $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n”; $message .= “現在の品数は{$product->amount}です。\n”; $mailer = new Mailer();//消える $this->mailer->send($user->email, $subject, $message); } } interface MaiIerInterface { function send($email, $subject, $message); } テストケースにて実装 $mailer = new TestMailer(); $notification = new StockUpdateNotification($mailer); $notification->notify(); $this->assertSame(‘肉が入荷しました’, $mailer->subject); … class TestMailer implements MailerInterface { function send($email, $subject, $message) { $this->email = $email; $this->subject = $subject; … } } 下位レイヤーが上位レイヤーに依存! UnitTest専 用
  8. メール送信の処理が未実装でも、ビジネスロジック(StockUpdateNotification ) だけ書いてUnitTestしてコミットできてしまう! class StockUpdateNotification { function __construct( MaiIerInterface $mailer

    ) { } function notify(User $user, Product $product) { $subject = “{$product->name}が入荷しました”; $message = “{$user->name}様\n”; $message .= “{$product->name}が入荷しました。\n”; $message .= “現在の品数は{$product->amount}です。\n”; $this->mailer->send($user->email, $subject, $message); } } interface MaiIerInterface { function send($email, $subject, $message); } こんな感じで使う $mailer = new Mailer(); $notification = new StockUpdateNotification($mailer); $notification->notify(); class Mailer implements MailerInterface { function send($email, $subject, $message) { // 一旦メールの送信はあとまわしで内容だけチェック Log::debug(“{$email}, {$subject}, {$message}”); } } 下位レイヤーが上位レイヤーに依存! 完成! 完成! 完成! 未完成
  9. (小話)LaravelでのDI • ちなみにLaravelではInterfaceを継承した実装クラスをインスタンス化せずに 自動で注入することができる • ServiceContainerというもので、どのInterfaceにどの実装クラスを注入するかを事前に決めておくこ とができる 超便利! これが $mailer

    = new Mailer(); $notification = new StockUpdateNotification($mailer); こうなる!! $notification = new StockUpdateNotification(); App\Providers\AppServiceProvider.phpにて下記bindを記述 public function register() { $this->app->bind('App\Product\MailerInterface::class', 'App\Product\Mailer::class'); }
  10. 終