# 機能設計書 26-インターフェース抽出

## 概要

本ドキュメントは、Roslynにおける「インターフェース抽出」機能の設計を記述するものである。インターフェース抽出機能は、既存のクラスまたは構造体からインターフェースを自動生成し、元の型にそのインターフェースを実装させるリファクタリング機能である。

### 本機能の処理概要

インターフェース抽出機能は、選択された型(クラス、構造体、インターフェース)から、指定されたメンバーを含む新しいインターフェースを生成する。生成されたインターフェースは同じファイルまたは新規ファイルに配置でき、元の型は自動的にそのインターフェースを実装するように更新される。

**業務上の目的・背景**：依存性注入(DI)パターンやテスタビリティの向上のため、具象クラスではなくインターフェースに依存することが推奨される。既存のクラスからインターフェースを手動で抽出するのは、メンバーの選択、インターフェース定義の作成、元のクラスの更新といった作業が必要で手間がかかる。自動化により、この作業を安全かつ効率的に行える。

**機能の利用シーン**：
1. 依存性注入のためにインターフェースを導入する場合
2. テスト用のモック作成のためにインターフェースを抽出する場合
3. 共通の契約を複数のクラスで共有する場合
4. SOLIDの原則に従ったリファクタリング

**主要な処理内容**：
1. 型宣言の分析(抽出可能なメンバーの特定)
2. インターフェース名とメンバーの選択(UI経由)
3. インターフェースの生成(新規ファイルまたは同一ファイル)
4. 元の型へのインターフェース実装の追加
5. 必要に応じて明示的実装への変換

**関連システム・外部連携**：
- CodeRefactoringProvider/CodeAction経由でエディタに統合
- IExtractInterfaceOptionsServiceでUI連携

**権限による制御**：特になし。すべてのユーザーが利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | インターフェース抽出ダイアログ | 主画面 | インターフェース名とメンバーの選択 |
| 20 | プレビューペイン | 補助画面 | 変更箇所のプレビュー |

## 機能種別

コード変換処理 / リファクタリング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| document | Document | Yes | 対象のドキュメント | 有効なドキュメントであること |
| position | int | Yes | カーソル位置 | 型宣言内であること |
| includedMembers | IEnumerable<ISymbol> | Yes | 含めるメンバー | 抽出可能なメンバーであること |
| interfaceName | string | Yes | インターフェース名 | 有効な識別子であること |
| location | ExtractLocation | Yes | 新規ファイル or 同一ファイル | - |

### 入力データソース

- ドキュメントの構文ツリー(SyntaxTree)
- セマンティックモデル(SemanticModel)
- IExtractInterfaceOptionsServiceからのユーザー入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ExtractInterfaceResult | ExtractInterfaceResult | 抽出結果 |
| succeeded | bool | 成功したかどうか |
| updatedSolution | Solution | 更新後のソリューション |
| navigationDocumentId | DocumentId | 生成されたインターフェースを含むドキュメント |

### 出力先

- 新規ファイル(ExtractLocation.NewFile)またはソースファイル(ExtractLocation.SameFile)

## 処理フロー

### 処理シーケンス

```
1. 型分析(AnalyzeTypeAtPositionAsync)
   └─ カーソル位置の型を取得し、抽出可能なメンバーを特定
2. オプション取得(GetExtractInterfaceOptions)
   └─ ユーザーからインターフェース名とメンバーを取得
3. インターフェースシンボル作成(CreateNamedTypeSymbol)
   └─ インターフェースのシンボルを生成
4. ファイル追加/更新
   └─ 新規ファイルまたは同一ファイルにインターフェースを追加
5. 元の型を更新(GetSolutionWithOriginalTypeUpdatedAsync)
   └─ implements句の追加
6. 明示的実装への変換(オプション)
   └─ UpdateMembersWithExplicitImplementationsAsync
7. 整形(GetFormattedSolutionAsync)
   └─ Formatter, Simplifierで最終整形
```

### フローチャート

```mermaid
flowchart TD
    A[開始: ExtractInterfaceAsync] --> B[AnalyzeTypeAtPositionAsync]
    B --> C{抽出可能?}
    C -->|No| D[エラー表示]
    C -->|Yes| E[GetExtractInterfaceOptions]
    E --> F{キャンセル?}
    F -->|Yes| G[処理中断]
    F -->|No| H[CreateNamedTypeSymbol]
    H --> I{NewFile or SameFile?}
    I -->|NewFile| J[ExtractInterfaceToNewFileAsync]
    I -->|SameFile| K[ExtractInterfaceToSameFileAsync]
    J --> L[AddTypeToNewFileAsync]
    K --> M[AddTypeToExistingFileAsync]
    L --> N[GetSolutionWithOriginalTypeUpdatedAsync]
    M --> N
    N --> O[UpdateMembersWithExplicitImplementationsAsync]
    O --> P[GetFormattedSolutionAsync]
    P --> Q[ExtractInterfaceResult返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 型の制限 | class, struct, interfaceのみ抽出可能(extensionは除外) | 常時 |
| BR-02 | 抽出可能メンバー | public、かつevent/method/propertyのみ | IsExtractableMember |
| BR-03 | Cloneメソッド除外 | recordの<Clone>$メソッドは除外 | 常時 |
| BR-04 | interface間抽出 | interfaceからinterfaceへの抽出時は元の型は更新しない | TypeKind.Interface時 |
| BR-05 | 名前生成 | 型名がinterfaceでない場合は"I" + 型名がデフォルト | 常時 |

### 計算ロジック

インターフェースメンバー生成ロジック:
1. メソッド → abstract修飾子付きのメソッドシグネチャ
2. プロパティ → abstract修飾子付き、publicなgetter/setterのみ含む
3. イベント → abstract修飾子付きのイベント宣言
4. static修飾子は保持される

## データベース操作仕様

該当なし（インメモリ操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 型なし | カーソル位置がclass/struct/interface内でない | エラーメッセージ表示 |
| - | メンバーなし | 抽出可能なメンバーが存在しない | エラーメッセージ表示 |

### リトライ仕様

なし

## トランザクション仕様

該当なし

## パフォーマンス要件

- 非同期処理でUIスレッドをブロックしない
- AnnotatedSymbolMappingで複数ドキュメントのシンボル追跡

## セキュリティ考慮事項

- 特になし

## 備考

- 型パラメータは含まれるメンバーに必要なもののみ自動的に抽出
- ShouldIncludeAccessibilityModifierでアクセシビリティ修飾子の必要性を判定

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、インターフェース抽出で使用される主要なデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ExtractInterfaceResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceResult.cs` | 抽出結果 |
| 1-2 | ExtractInterfaceTypeAnalysisResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs` | 型分析結果 |
| 1-3 | ExtractInterfaceOptionsResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceOptionsResult.cs` | オプション |

#### Step 2: エントリーポイントを理解する

処理の起点となるAbstractExtractInterfaceService.csを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AbstractExtractInterfaceService.cs | `src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs` | メイン処理 |

**主要処理フロー**:
1. **48-55行目**: GetExtractInterfaceCodeActionAsync - CodeAction取得
2. **78-107行目**: AnalyzeTypeAtPositionAsync - 型分析
3. **109-171行目**: ExtractInterfaceFromAnalyzedTypeAsync - 抽出実行
4. **173-213行目**: ExtractInterfaceToNewFileAsync - 新規ファイルへの抽出
5. **215-252行目**: ExtractInterfaceToSameFileAsync - 同一ファイルへの抽出

#### Step 3: メンバー判定を理解する

抽出可能なメンバーの判定ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractExtractInterfaceService.cs | `src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs` | 424-443行目: IsExtractableMember |

#### Step 4: インターフェースメンバー生成を理解する

インターフェースのメンバー生成ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AbstractExtractInterfaceService.cs | `src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs` | 364-422行目: CreateInterfaceMembers |

### プログラム呼び出し階層図

```
AbstractExtractInterfaceService.ExtractInterfaceAsync
    │
    ├─ AnalyzeTypeAtPositionAsync
    │      ├─ GetTypeDeclarationAsync
    │      ├─ GetDeclaredSymbol
    │      └─ GetMembers().WhereAsArray(IsExtractableMember)
    │
    ├─ GetExtractInterfaceOptions
    │      └─ IExtractInterfaceOptionsService
    │
    └─ ExtractInterfaceFromAnalyzedTypeAsync
           │
           ├─ CreateNamedTypeSymbol
           │      └─ CreateInterfaceMembers
           │
           ├─ ExtractInterfaceToNewFileAsync / ExtractInterfaceToSameFileAsync
           │      ├─ AnnotatedSymbolMapping.CreateAsync
           │      └─ ExtractTypeHelpers.AddTypeToNewFileAsync / AddTypeToExistingFileAsync
           │
           ├─ GetSolutionWithOriginalTypeUpdatedAsync
           │      └─ AddInterfaceType
           │
           └─ GetFormattedSolutionAsync
                  ├─ Formatter.FormatAsync
                  └─ Simplifier.ReduceAsync
```

### データフロー図

```
[入力]               [処理]                          [出力]

Document + Position ───▶ AnalyzeTypeAtPositionAsync ───▶ ExtractInterfaceTypeAnalysisResult
                              │
                              ▼
AnalysisResult ───▶ GetExtractInterfaceOptions ───▶ ExtractInterfaceOptionsResult
                              │
                              ▼
Options ───▶ CreateNamedTypeSymbol ───▶ INamedTypeSymbol (Interface)
                              │
                              ▼
Interface ───▶ AddTypeToNewFile/ExistingFile ───▶ Solution
                              │
                              ▼
Solution ───▶ GetFormattedSolutionAsync ───▶ ExtractInterfaceResult
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AbstractExtractInterfaceService.cs | `src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs` | ソース | メイン処理 |
| ExtractInterfaceResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceResult.cs` | ソース | 結果構造 |
| ExtractInterfaceTypeAnalysisResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs` | ソース | 型分析結果 |
| ExtractInterfaceOptionsResult.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceOptionsResult.cs` | ソース | オプション |
| ExtractInterfaceCodeAction.cs | `src/Features/Core/Portable/ExtractInterface/ExtractInterfaceCodeAction.cs` | ソース | CodeAction |
| ExtractTypeHelpers.cs | `src/Features/Core/Portable/ExtractInterface/ExtractTypeHelpers.cs` | ソース | ヘルパー関数 |
| AnnotatedSymbolMapping.cs | `src/Features/Core/Portable/ExtractInterface/AnnotatedSymbolMapping.cs` | ソース | シンボルマッピング |
