0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【リファクタリング】Duplicate Observed Data(重複した観測データ)

Posted at

1. 概要(Overview)

Duplicate Observed Data は、
UI モデルやドメインモデルの中に 同じデータを二重に保持している 状態を解消するリファクタリングです。

よくあるのは:

  • GUI クラスとデータモデルの両方が同じデータを持っていて、同期のためのコードが複雑化しているケース
  • 一方が更新されてももう一方に反映されない → 不整合やバグの原因 になる

目的は以下の通りです:

  • データの重複保持をやめ、唯一の場所に真実を置く(Single Source of Truth)
  • 双方向同期の複雑さを取り除き、バグを減らす
  • コードの責務を整理する

2. 適用シーン(When to Use)

  • データを複数の場所で保持し、同期処理を書いている
  • 「一方を更新したのに、もう一方に反映されない」不具合が発生している
  • View や Controller が 同じ値を独自に持ってしまっている
  • 同期のために無駄なイベント通知・リスナーが増えている

よくある匂い:

  • Duplicate Data(データの重複)
  • Shotgun Surgery(散弾銃のような変更)

3. 手順(Mechanics / Steps)

  1. 重複して保持しているデータを特定
  2. データを 唯一の「本当の持ち主」 に移す
  3. 他のクラスからは、そのデータを直接参照するように変更
  4. 必要であれば通知(Observer / Listener)を導入して表示更新を行う
  5. 同期コードや余分なフィールドを削除

4. Kotlin 例(Before → After)

Before:GUI と Model が同じデータを持っている

// モデル
class Person(var name: String)

// GUI 側が重複して name を持ってしまっている
class PersonWindow {
    private var nameField: String = ""

    fun loadData(person: Person) {
        nameField = person.name
    }

    fun saveData(person: Person) {
        person.name = nameField
    }
}
  • PersonWindowname を保持し、Person との間で手動同期している
  • 更新忘れ・競合が起きやすい

After:モデルを唯一のデータ保持場所にする

// モデル
class Person(var name: String)

// GUI はモデルを直接参照し、データを持たない
class PersonWindow(private val person: Person) {
    fun display() {
        println("Name: ${person.name}")
    }

    fun changeName(newName: String) {
        person.name = newName
    }
}
  • PersonWindowname を持たなくなり、重複が解消
  • Person が唯一のデータ保持場所(Single Source of Truth)
  • GUI 側は表示や入力を通じてモデルを操作するだけ

さらに発展:Observer パターンで UI を更新

class Person(var name: String) {
    private val observers = mutableListOf<() -> Unit>()

    fun addObserver(observer: () -> Unit) {
        observers.add(observer)
    }

    fun setName(newName: String) {
        name = newName
        observers.forEach { it() }
    }
}

fun main() {
    val person = Person("Alice")
    person.addObserver { println("UI updated: ${person.name}") }

    person.setName("Bob") // UI updated: Bob
}

→ モデルに変更通知を持たせることで、UI と同期を自動化できる。


5. 効果(Benefits)

  • データの 重複保持を排除して一貫性を保証
  • 双方向同期のコードが不要になり、シンプル化
  • バグ(更新忘れ、競合)のリスクが減る
  • データの責務が明確になり、保守性が向上

6. 注意点(Pitfalls)

  • UI 側で「キャッシュ」や「一時的な編集内容」を持ちたい場合は、完全に排除できないケースもある
  • Observer パターンやデータバインディングを導入すると、シンプルさと複雑さのバランスを取る必要がある
  • Single Source of Truth を意識しつつ、「どのクラスが持ち主なのか」を設計段階で明確にすることが重要

まとめ

  • Duplicate Observed Data は「同じデータを二重に持ち、同期で苦労している」状態を解消するリファクタリング
  • 判断基準:このデータの唯一の持ち主はどこか?
  • 基本思想:データは一箇所に集約し、他はそれを参照・購読するだけ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?