[オブジェクト指向]メッセージ基点の設計で柔軟性を高める
こんにちは。お久しぶりです。浅野です。 最近、「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本を読みました。いくつか新しい発見がある中で、「設計において重要なのは、ドメインオブジェクトよりもメッセージだ」という主張が印象に残ったので、アウトプットしていきたいと思います。 本の紹介 今回読んだ「オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方」という本は、オブジェクト指向設計についての重要なエッセンスが分かりやすくまとまった一冊です。タイトル通りサンプルコードがRubyで書かれており、非常に理解しやすかったです。 前提 本書によると、オブジェクト指向設計の目的は、「あとにでも」設計できるようにすることです。そしてオブジェクト指向設計とは、「依存関係を管理すること」だと主張します。変更を許容できるようなかたちでオブジェクトの依存関係を構成することで、未来の新たな要求にも柔軟に対応できるようになります。 本題 本書の主張では、「設計においては、ドメインオブジェクトよりもメッセージが重要」とされています。ドメインオブジェクトに集中しすぎると、オブジェクトに不自然な振る舞いを強いることがあります。しかし、メッセージを起点に設計を考えることで、必要なオブジェクトを特定し、適切な振る舞いを実現することができます。 事例 本書では、ある自転車旅行会社の事例をもとに説明されています。旅行には、参加者の上限と体力に応じた難易度が設定されています。自転車は持ち込みも可能ですが、旅行会社から借りることもできます。今回は、「参加者が、適切な難易度の、特定の日付の、自転車が借りられる旅行の一覧を見たい」というユースケースについて考えていきます。 必要なドメインオブジェクト 上記の例では、以下のドメインオブジェクトの必要性が見えてきます。 参加者(Customer)旅行(Trip)行程(Route)自転車(Bicycle) それでは、これらのオブジェクト同士がどのようなやりとりをしていくのか考えていきたいと思います。 シーケンス図の利用 著者は、オブジェクト同士のメッセージを考えるのに最適な方法が「シーケンス図」を書くことだと主張します。これにより、どのオブジェクトがどんなメッセージを送受信するかが明確になります。例えば今回のユースケースでは、CustomerであるAliceとTripは、以下のメッセージのやりとりを行うことが想定できそうです。 ここでは、CustomerであるAliceがTripに向けてsuitable_tripsというメッセージを送っています。このメッセージはon_date, of_difficulty, need_bikeという3つのパラメータを要求します。これはAliceがTripクラスに適切な難易度の、特定の日付の、自転車が借りられる旅行を見つけてくれることを期待しています。 誰がメッセージに応答すべきか しかし、ひとつ違和感を覚えます。Tripは日程と難易度によって旅行を見つける責任を負うのは妥当に見えますが、自転車が利用可能かどうかを調べる責任を負うべきでしょうか?Tripの責任領域ではないように思われます。では、誰が適任でしょうか?先程考えたドメインオブジェクトからはBicycleクラスの方がより適切に見えます。その場合のシーケンス図は以下でしょうか。 今回の改善によって、Tripから余計な責任を取り除くことができました。しかし単にそれをCustomerに移しただけとも言えます。Aliceは適切な旅行を見つけたいだけにも関わらず、その旅行が具体的にどのようなものか(例えば、自転車が借りられる旅行か)まで知ってしまっています。つまり、Aliceが必要以上にシステムの内部構造を理解しているということです。これではあまりいい設計とは言えません。 必要なオブジェクトを見つける これまでCustomerが送るsuitable_tripsメッセージに対する適切な受け手を考えてきましたが、既存のドメインオブジェクトからは見つけることができませんでした。新しいオブジェクトの導入が必要そうです。その新しいクラスには、適切な旅行を見つけるという責任を担ってほしいため、TripFinderと名付けましょう。シーケンス図は以下となります。 TripFinderは、適切な旅行の条件に関する知識を全て持ち合わせ、このメッセージに応答するために必要なことを何でも行います。さらに、安定したパブリックインターフェースを提供する一方、変化しやすい内部実装の詳細は隠蔽しています。いまや、TripFinderはCustomerから独立し、他のオブジェクトからも利用可能となりました。将来他の旅行会社からWebサービスを介してTripFinderを利用したいという要求が出てきた場合にも、柔軟に対応できます。このアプローチは、システムの拡張性と柔軟性を大幅に向上させています。 まとめ 今回はメッセージ中心のアプローチを取り、アプリケーションの設計に取り組んできました。「どのオブジェクトがこのメッセージに応答すべきか」という視点で考えた結果、本当に必要なオブジェクトを特定することができました。オブジェクトは存在するからメッセージを送るのではなく、メッセージを送るためにオブジェクトが存在するのです。シーケンス図は、このプロセスでメッセージに集中することを助けてくれました。もしドメインオブジェクトにのみ焦点を当てていた場合、今回のような解決策は見逃されていた可能性が高く、オブジェクトに無理な振る舞いを強いた複雑な設計になっていたかもしれません。メッセージを中心とした設計により、システムの構成要素間の関連を適切に把握し、より効率的で理解しやすいものにすることができます。 参考 Sandi Metz, "オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方"
Railsマイグレーションの管理:schema.rbのコンフリクトを避けよう
こんにちは、冨田です。 2024年のF1シーズンが開幕し、今年もレッドブルが一強になるのかどうか、楽しみにしているところです。 先日、MigrationファイルのコンフリクトをGitHub上で修正しようとしたら、CIに怒られてしまいました。 原因を調べていく中で、GitHubとRailsのMigration及びschema.rbについて勉強することができたので、ブログにしました。 概要 dev1ブランチ(commit-4)でafterオプションを使って新しいカラムを特定の位置に追加するMigrationファイルを作成 Migrationファイル add_column :posts, :content, :text, after: :title, comment: '内容' schema.rb t.string "title", null: false t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false dev1ブランチ(commit-4)から派生した後続ブランチ(dev2, etc)で開発を進めていました。 後にafterオプションが特定のデータベース環境でサポートされていないことがわかり、dev1ブランチに新たなコミット(commit-7)を追加し、Migrationファイルとschema.rbを以下のように修正しました。なお、この変更は後続ブランチには反映していませんでした。 Migrationファイル add_column :posts, :content, :text, comment: '内容' schema.rb t.string "title", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 featureブランチへのマージ過程で、Migrationファイルのコンフリクトが発生。 見逃しポイント: schema.rb の修正はコンフリクトが発生しておらず、見逃していた。 GitHub上でこのMigrationファイルの修正。 その後、CIの実行中にyou can't define an already defined columnのエラーが発生しました。 結果 schema.rbに重複したカラムが生成されてしまっていました。 t.string "title", null: false t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 原因 GitHubでMigrationファイルを修正した際、ローカル環境でschema.rbファイルを更新しなかったため、dev1ブランチのschema.rbは修正後の状態になっていましたが、dev2ブランチ以降は修正前の状態を維持していました。GitHubの履歴は同じ場所の変更でないため、schema.rbはコンフリクトの対象にならず、結果としてdev1とdev2ブランチのschema.rbの変更が両方とも反映されてしまいました。 [dev1ブランチ]schema.rb t.string "title", null: false, "タイトル" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "content", comment: "内容" # dev1ブランチ commit-7 [dev2ブランチ]schema.rb t.string "title", null: false, "タイトル" t.string "content", comment: "内容" # dev1ブランチ commit-4 t.datetime "created_at", null: false t.datetime "updated_at", null: false まとめ Migrationファイルに関わる変更は、ローカル環境で必ず確認することをおすすめします。それは、schema.rbの修正・変更にも関わってくるからです。 今回の失敗から、GitHubとRailsのMigration及びschema.rbについて理解を深めることができました。 最後までお付き合いいただき、ありがとうございました。また、新たな学びがあれば、ぜひ共有したいと思います!
WYRD年賀状企画2024 当選者発表
新年あけましておめでとうございます。代表の大越です。 昨年中は弊社ウィルドのメンバーが大変お世話になりました。心より感謝申し上げます。本年も皆様にとって幸多き年となることを心よりお祈りします。 近年、個人や企業の間で年賀状の習慣が減少傾向にありますが、弊社の年賀状企画を楽しみにしていただいている方々もいらっしゃるため、今年も変わらず、皆さまに年賀状を送らせていただきました。 今年も毎年恒例のウィルド新春企画で、皆様への感謝の気持ちを込め、お年玉抽選プレゼントを実施いたします。 プレゼントは弊社が運営する「蔵出し米.com」にて販売しております山形県高畠町産のお米をご用意しました。 ・有機JAS 無農薬・無化学肥料 つや姫 精米 5kg ・有機JAS 無農薬・無化学肥料 金のいぶき 玄米 5kg 下記、お年玉はがきナンバーが当選者となりますので、ご確認ください。 企画内容 日頃の感謝の気持ちを込めた品を、弊社からお送りしました年賀状のお年玉くじ番号で抽選しプレゼント致します。 賞品 下記賞品よりお好きなものを1つ選んでください。 【令和5年産】有機JAS 無農薬・無化学肥料 つや姫 精米 5kg 【令和5年産】有機JAS 無農薬・無化学肥料 金のいぶき 玄米 5kg 賞品の詳細な情報はこちら つや姫・ 金のいぶき(蔵出し米.comの商品ページへ移動します) ※ご当選の銘柄が売切れとなった場合は別の銘柄とさせていただく場合があります。あらかじめご了承ください。 当選確定期間 発表 〜 2024年1月31日まで ※期間内にお申し出がなかった場合は無効となります。 当選番号はこちら! 2024年は当選30本あります! 当選確定方法 ①ご希望の賞品 ②賞品の発送先(郵便番号、住所、氏名、電話番号) ③配送希望日・希望時間帯(1週間以上先の日にちをご指定ください) ④メールアドレス(折り返し確認メールを送らせて頂きます) ⑤年賀状宛名全体の写真 お手数ですが、①②③④をメール本文に記入、⑤を添付のうえ、nenga@wyrd.co.jp までメール送信してください。 弊社より折り返しの確認メール受信をもちまして当選確定と致します。
動的型付け言語と静的型付け言語を理解してみる
年末までに10kgダイエットと言いながら、8kgで目標未達になってしまった冨田です。 今回のテーマについて深く学習をしてこなかったので、何が違うのかを改めて調べてみました。(TypeScriptに何故か苦手意識があり、避けてきたので...) 静的型付け言語: MDN Static typing 静的型付け言語(例えば、Java, C, C++)とは、コンパイル時に変数の型がわかる言語です。静的型付け言語の多くは、プログラマが明示的に型を指定 動的型付け言語: MDN Dynamic typing 動的型付け言語とは、(JavaScript のように) インタプリターが実行時に、変数の値 に基づいて、変数 にデータ型 を割り当てる ※インタプリター型言語とコンパイラー型言語で動的・静的型付け言語を判断しているわけではないので注意! メリットとデメリット 詳細な部分までは、触れない。 エラーのタイミング 動的型付け言語の場合、実行時。 静的型付け言語の場合は、コンパイル時(意味解析の型検証時)。 コード量 動的型付け言語の場合は、少ない。 静的型付け言語の場合は、多い。 コンパイルなどの機械語の部分は、省略する。(機械語部分の詳細は、これから勉強していきます!) RubyとTypeScriptをつかって、同じコードを書いてみる。 TypeScript 明示的にどの型が必要なのかを定義する。(number型しか入りませんよ〜) class Calculator { add(x: number, y: number): number { return x + y; } } ※実行する際は、下記の流れで実行する。(node.jsとTypeScriptコンパイラが必要です) //コンパイル中にエラーがない場合、jsファイルが生成される // --noEmitOnError がないと、エラーがあっても生成されます $ tsc --noEmitOnError ファイル名.ts // 対象ファイルを実行する $ node ファイル名.js 正しい型の引数を渡した場合 下記のように、計算結果を出力する const calculator = new Calculator(); const result: number = calculator.add(3, 5); console.log(result); // 8 定義した型と異なる型を引数に渡した場合 const calculator = new Calculator(); const result: number = calculator.add("aaa", 5); console.log(result); JSへコンパイルするときにエラーが発生し、JSファイルを生成しない。 $ tsc typescript.ts typescript.ts:8:39 - error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. const result: number = calculator.add('aaa', 5); number型に定義したパラメータにstring型の引数を割り当てられないというエラーを、プログラム実行前のコンパイル時に出力する。 Ruby 暗黙的に型を引数に定義することになる。(もちろんInt型ですよね?) class Calculator def add(x, y) x + y end end 正しい型の引数を渡した場合 calculator = Calculator.new result = calculator.add(3, 5) puts result # 8 異なる型を定義した場合 calculator = Calculator.new result = calculator.add('aaa', 5) puts result 実行後にエラーが出力される。(実行するまで、エラーが発生しない) ruby.rb:3:in `+': no implicit conversion of Integer into String (TypeError) x + y ^ from ruby.rb:3:in `add' from ruby.rb:8:in `<main>' まとめ どちらが良いという判断ではなく、「どちらも使い道によっては...」と要素が大きく感じました。 型注釈をコストと考えるか、投資と考えるか? サバイバルTypeScriptより 静的型付け言語 チーム開発や大規模開発向け 型情報によって、可読性が良くなる 動的型付け言語 個人開発やスピード感が求められる開発 すぐプログラムを実行できる エンジニア経験の浅い私としては、静的型付け言語のほうが、IDEでエラーやタイポに気づきやすいといった点でこっちのほうが見やすいのかな?と思った気づきとなりました。 (動的型付け言語でも検出できる機能やIDEはあるかと思いますが) 以上、最後までお付き合いいただきありがとうございました!
AWSにおけるアカウントとは?
こんにちは、小林です。 先日、自社でも現場でも管理しているAWSアカウントを整理する必要がありました。 その際に「AWSアカウントのルートユーザー」という単語を出したときに、伝わらなかった(伝え方が下手だったというのもあります)ので、改めて、整理・周知しようかと思った次第です。 というわけで、今回のテーマはこちらです。 AWSにおけるアカウントとはなにか? アカウントとは? そもそも、アカウントとは何でしょうか? Wikipediaによると、以下のように定義されています。 コンピュータ用語でのアカウント (英: account) は、ユーザーがネットワークやコンピュータやサイトなどにログインするための権利のことである。ユーザーに割り当てられたアカウントをユーザーアカウントとも呼ぶ。 引用:アカウント AWSにおけるアカウントとは? AWSという文脈では、アカウントというと以下の3つが登場します。 AWSアカウント ルートユーザー IAMユーザー それでは、ひとつずつ説明をしていきます。 AWSアカウント AWSとの契約の単位。要は1契約につき、1アカウント付与されます。 AWS的にはリソースのコンテナであり、リソースのセキュリティ境界である、と定義されています。 ルートユーザー AWSアカウントの所有者。AWSアカウントを作成する際に使用したメールアドレス。 このメールアドレスとパスワードを利用して、AWSアカウントにログインすることができますが、普段使いするアカウントではありません。 最初のIAMユーザーを作成したり、契約に関する設定を行ったりすることに使用します。 AdministratorAccessの権限を持つIAMユーザーとは異なります。 IAMユーザー 普段、S3のバケットを作成したり、CloudWatchのログを確認したりするために、作業者一人ひとりに払い出されるアカウントです。 おまけ ちなみに、クラスメソッド様経由でAWSを契約すると、initial-admin-user-123456789012のようなユーザーを最初に払い出していただきますが、これもIAMユーザーです。 ルートユーザー自体はクラスメソッド様にて管理していただいています。 さいごに 私自身はたまたま以下の機会があったため、これらを明確に区分することができましたが、普段の業務でIAMユーザーしか使用する機会がないのであれば、意識することもないでしょう。 自身でAWSと契約した クラスメソッド様経由でAWSと契約した これを機に理解してもらえればと思います。 参考資料 AWS アカウント管理リファレンスガイドへようこそ AWS アカウントのルートユーザーの使用 IAM ユーザー



