代表的な面接トピック

コーディング面接:レストランの座席予約モジュールの設計

コーディング難しい
Offer.cc 編集チーム公開日 更新日

質問

レストランの座席予約モジュールを設計してください。顧客は人数と時間でテーブルを検索し、予約の作成やキャンセルを行い、スタッフは直接来店した客(ウォークイン)を案内します。システムは重複する予約に同一のテーブルを割り当ててはなりません。クラス、インターフェース、および並行性の境界をどのように構成しますか?

プロンプトと適切なコンテキスト

これは低レベル設計(LLD)およびコーディングの質問です。目標は、予約、テーブル、時間区間、およびキャンセル待ちリストを、明確な責務を持つオブジェクトへと落とし込むことです。公開されている面接レポートにはレストラン予約の設計が含まれており、OODガイドではスコープを明確にし、要件をリストアップした上でオブジェクトとインターフェースを選択することが強調されています。1つのレストラン、固定の営業時間、インメモリのモジュールを前提とし、面接官から求められた場合に永続化と並行性制御を追加してください。

面接官が評価するポイント

  • RestaurantTableReservationWaitlist、および割り当てポリシーを分離できているか。
  • 半開区間、収容人数、テーブルの連結、キャンセル後の解放を正しく処理しているか。
  • 状態マシン、べき等なリクエスト、および並行処理下での二重割り当て防止を設計できているか。
  • テストが境界条件を網羅しており、計算量や拡張ポイントを説明できるか。

最初に確認すべき明確化の質問

顧客が特定のテーブルを選択するのか人数のみを指定するのか、またテーブルを連結できるかどうかを質問します。予約の所要時間と清掃バッファ、予約変更、遅刻、ノーショー、ウォークイン、キャンセル待ちの順序付け、およびリクエストの再試行について明確にします。時間の精度、タイムゾーン、単一店舗か複数店舗か、プロセス間での永続化が必要かどうかを確認します。

30秒で答える回答フレームワーク

まず、イミュータブルな TimeRange と予約状態マシンを定義します。AvailabilityService が競合クエリを処理し、TableAllocator が収容人数とポリシーに基づいて選択し、ReservationService が作成・キャンセル・通知をオーケストレーションし、Waitlist が候補者を個別に管理します。作成処理ではべき等性キーを使用し、テーブルを確保する前に同一のクリティカルセクション内で空き状況を再確認します。キャンセルは有効な遷移のみを許可し、空き枠を解放します。テストでは、隣接する区間、並行リクエスト、繰り返しのキャンセル、キャンセル待ちからの繰り上げを網羅します。

ステップバイステップの詳細な回答

1. 時間、テーブル、予約状態のモデリング

予約を半開区間 [start, end) で表現し、start < end を必須とします。2つの区間は a.start < b.end && b.start < a.end の場合に重複します。Table は収容人数、識別子、空き状況を保持し、Reservation は顧客、人数、区間、テーブル、状態を保持します。状態には HELDCONFIRMEDSEATEDCANCELLEDNO_SHOW があり、無効な遷移は拒否します。

2. 割り当てポリシーの差し替えを可能にする

TableAllocator は候補テーブル群とリクエストを受け取ります。デフォルトでは、無駄を防ぐために条件を満たす最小のテーブルを選択します。他のポリシーとして、隣接するテーブルやバリアフリーを優先することも可能です。アロケータは予約の書き込みを行わず、単一の巨大なクラスを作るのではなく、検索、決定、永続化を分離した状態に保ちます。

3. 競合チェックと作成の実装

レストラン、時間区間、テーブルインデックスでフィルタリングし、アロケータに選択させます。作成処理は入力を検証し、清掃バッファを適用し、べき等性キーを作成し、書き込み前に単一のロックまたはトランザクション内で空き状況を再読み込みします。古い検索結果を信用することはできません。不変条件は明確です:1つのテーブルには、重複する CONFIRMED な予約が0件であることです。

text
create(request, key):
  if idempotency.exists(key): return idempotency.result(key)
  range = TimeRange(request.start, request.end + cleanupBuffer)
  lock(restaurantId, range):
    table = allocator.choose(availableTables(range), request.partySize)
    if table is null: return WAITLISTED
    reservation = Reservation.confirm(request, table, range)
    store(reservation)
    idempotency.save(key, reservation.id)
    return reservation

4. キャンセル、遅刻、繰り上げの処理

キャンセルは、HELD または CONFIRMED から CANCELLED への遷移のみを許可します。キャンセルを繰り返しても同じ結果が返され、通知が重複することはありません。来店により状態は SEATED に変化し、ポリシーに基づくタイムアウトによって NO_SHOW となりテーブルを解放できます。WaitlistMatcher は解放イベントを消費し、待ち時間、人数、優先度でマッチングを行い、新規予約がテーブルを二重割り当てしないように作成時と同じクリティカルセクションを再利用します。

5. テストと計算量の説明

隣接する [19:00,20:00)[20:00,21:00) の区間が競合しないこと、逆順の入力が拒否されること、清掃バッファが競合範囲を広げること、同一のべき等性キーで余分な予約が作成されないこと、キャンセルによる繰り上げが高々1回行われること、並行作成で勝者が1つだけになることをテストします。各テーブルがソートされた予約を保持している場合、1テーブルあたりの競合検索は O(log n + k) です。m 個の候補テーブルがある場合、選択と書き込みはおよそ O(m log n) です。収容人数バケットや区間インデックスを使用することで改善できます。

質の高い模範回答

イミュータブルな半開区間 TimeRange、明確な予約状態マシン、および差し替え可能な割り当てポリシーをモデリングします。AvailabilityService はレストラン、時間、テーブルでフィルタリングし、TableAllocator は条件を満たす最小のテーブルを選択し、ReservationService はべき等性キーを使用して単一のロックまたはトランザクション内で再読み込みと書き込みを行います。キャンセル、遅刻、ノーショーは有効な遷移を通じて空き枠を解放し、キャンセル待ちのマッチングは同一のクリティカルセクションを再利用します。テストでは隣接区間、清掃バッファ、重複キャンセル、並行作成、キャンセル待ちの競合を網羅し、インデックス適用時の計算量を明示します。

よくある間違い

  • 明確な責務分離やポリシーの差し替えを行わず、すべてのロジックを Restaurant に詰め込むこと。
  • 閉区間を使用して、隣接する予約を誤って競合として判定すること。
  • 空き状況を検索した後、書き込み境界で再チェックせずに書き込むこと。
  • キャンセル処理を非べき等にしてしまい、再試行時にテーブルの二重解放や重複通知を引き起こすこと。
  • 顧客予約のみを実装し、ウォークイン、遅刻、ノーショー、キャンセル待ちを無視すること。
  • 不変条件、境界テスト、または計算量を示さず、クラス図のみを提示すること。

フォローアップの質問と回答

テーブルの連結をどのようにサポートしますか?

単一の tableId ではなく順序付けられたテーブルのセットを返し、総収容人数、連結可能性、セット全体での競合に関する制約を追加します。アロケータのインターフェースを維持し、ComposableTableAllocator の実装を追加します。

プロセス間での二重予約をどのように防ぎますか?

テーブルと時間に対する検証可能なロックまたはトランザクション制約を使用して、競合の不変条件を永続化層に移します。アプリケーションロックは競合を減らすことができますが、正確性を保証する唯一の手段にすることはできません。

ユーザーが作成ボタンを連打した場合はどうなりますか?

クライアントのべき等性キーを必須とし、結果をレストランとユーザーのスコープで保存します。同じキーに対しては元の予約を返し、異なるキーは共有された競合チェックを通常どおり通過します。レート制限はビジネスロジック上のべき等性の代わりにはなりません。

ディナーの混雑時間帯をどのように最適化しますか?

レストランと時間でシャード(分割)します。キャッシュは検索のヒントとしてのみ使用し、最終的な作成は強力な整合性を持つクリティカルセクション内で行います。事前計算された収容人数バケットやキャンセル待ち候補であっても、書き込み前にバージョンの確認が必要です。

キャンセル後に通知が失敗した場合はどうなりますか?

状態変更とアウトボックスイベントを単一のトランザクションでコミットし、通知コンシューマをべき等かつ再試行可能にします。テーブルの解放はSMS送信の成功を待つべきではありません。失敗したものは再試行に回し、運用担当者が確認できるデッドレターキューに送ります。

公開情報ソース

関連する質問

関連面接ツール

コーディング問題にはスクリーンショットを使用

問題をキャプチャし、制約条件、解法アプローチ、コード、エッジケース、計算量の順に進めます。

ツールを見る