3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

第3話 “変化に強い構造”を土台に改善していこう

Last updated at Posted at 2025-05-06

前回までの記事

見た目は整理できた。でもまた積み上げて壊れる気がする

若手は、コードを眺めながらため息をついた。
前回、UIの中に詰め込んでいた処理を外に出し、見た目はだいぶスッキリした。

変数名も整えた。関数も分けた。
でも──どこか引っかかる。

若手:「何がどこで起きてるのか、まだちゃんと整理できてないんですよね……。
またこれ、同じように積み上げてったら、どこかで崩れる気がして……」

師匠はゆっくりとうなずきながら言った。

師匠:「動きじゃなくて、“責任”を分けて考えてみな。
処理の順番より、“どこが何を担当してるか”の方が大事なんだよ。
たとえば建設現場でも、配線工が土台つくってたらおかしいだろ?
見た目が完成してても、責任がバラけてたら、あとから崩れるんだよ」

その瞬間、若手の中で“設計=順番”という思い込みが崩れ始めた。
意味で分ける。責任で整理する。
それが、構造の設計というものなのかもしれない──

useReducerで「どう変えるか」ではなく「なぜ変えるか」を整理

責任を分けろ。
順番より、「どこが何を担当しているか」が大事だ。

そう言われたものの、若手はまだモヤモヤしていた。

若手:「意味のある構造……処理の順番じゃなくて、責任の整理……」

師匠はそんな若手の様子を見て、静かに言葉を添えた。

師匠:「たとえば、“どう変わるか”のルールを一箇所にまとめると、整理しやすくなるぞ。
変わる“意味”がそこに集まっていれば、見通しもつく」

若手はハッとしたように顔を上げた。

若手:「……あ、それって、Reactでいうと useReducer ですね!」
師匠:「Reactわからんから、コードで説明するな。“WHATで説明しろ”って言っただろ」

若手:「……ですよね(切り替え)――“何を変えたいのか”を UIの外で決められるってことです」
師匠:「まあ、それでええやろ」
若手:「つまり、状態の“意味付け”を、UIから切り離せるんですよ」

さっそく useReducer を導入してみた。

UIは入力を受け取って dispatch を投げるだけ。
どう変えるかの判断や、状態の意味付けは reducer に集中。
UIは「言われた通りに見せるだけ」の役割に落ち着いた。

修正したコード

import { useReducer, useState } from 'react';

// ✅ 状態の“意味付け”と“更新ルール”をまとめる reducer(Transferの役割)
function todoReducer(todos: string[], action: { type: string; payload?: string }): string[] {
  switch (action.type) {
    case 'ADD_TODO':
      // 空文字は追加しない(意味のチェック)
      if (!action.payload || action.payload.trim() === '') return todos;
      return [...todos, action.payload.trim()];
    default:
      return todos;
  }
}

function App() {
  // ✅ 入力用の一時状態(入力Source)
  const [text, setText] = useState('');

  // ✅ useReducerでTodoリストの状態と更新ロジックを保持(意味のある状態)
  const [todos, dispatch] = useReducer(todoReducer, []);

  // ✅ UIから「追加してほしい」という命令だけを送る(判断は外へ)
  const handleAdd = () => {
    dispatch({ type: 'ADD_TODO', payload: text });
    setText('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Todoアプリ</h1>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        {/* ✅ ユーザー入力(Source) */}
        <input
          type="text"
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="やることを入力"
        />
        {/* ✅ 命令ボタン(出力ではなく、入力の起点) */}
        <button onClick={handleAdd} disabled={!text.trim()}>
          追加
        </button>
      </div>

      {/* ✅ Todo一覧の表示(Sink:出力専用) */}
      <ul>
        {todos.map((todo, idx) => (
          <li key={idx}>
            {/* ✅ “意味の確定”された状態の出力。判断や加工なし */}
            {todo}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

構造を“言葉で整理”しただけで、AIの出力も変わった

useReducer によって、若手は「状態の変化ルール」をUIの外に出し、
責任の整理という感覚を少しずつ掴み始めていた。

それでも、どこか“ピースが足りない”感覚が残っていた。

あるとき、若手はふと考えはじめた。

若手:「UIで受け取って、reducerで変化を決めて、UIで表示する……。
あれ、これって──入力、判断、出力……?」

頭の中にある構造を、紙に書き出してみる。
入力、変換、出力――
気づけば、**処理の流れではなく、“意味の流れ”**が整理されていた。

若手:「……この構造って、名前つけるとしたら……?」
師匠:「それが、構造化設計でいう“STS設計”ってやつだよ」

若手は驚き、少し戸惑いながら返す。

若手:「STS……確かそれって、昔のCOBOLとか、古い基幹系で使われてた設計ですよね?
今のオブジェクト指向とか、Reactとかのフレームワークには、もう合わないんじゃ……」

師匠はすぐに答えた。静かに、でも力強く。

師匠:「何も変わらないんだよ。
組み込みだろうが、WEB・スマホだろうが──。
設計の本質は、時代がどんなに進んでも、変わらないんだよ」

若手は、思わず言葉を失った。
設計とは“ツールの話”ではなく、“考え方”の話だったのだ。

気を取り直し、若手は AI にこう投げてみた。

「UIは表示専用。状態は“入力→変換→出力”に責任を分けて」

返ってきたコードは、以前とまるで違っていた。
責任が整理され、構造が見えていた。
なにより、「意味の流れ」がそのままコードに表れていた。

役割 具体例
Source 入力・指示 input, button, handleAdd
Transfer 状態の変化ルールや判断 todoReducer + dispatch
Sink 表示専用 TodoList(propsで表示するだけ)

若手:「……構造を言葉にしただけで、AIのコードって、こんなに変わるんだ……。
    これが本来の設計なんですね。理解できた気がします」

師匠は一拍置いてから、静かに言った。

師匠:「何言ってんの。まだ“設計の形”にはなってないよ」
若手:「えっ……?」
師匠:「Appメソッドの中、見てごらん。ひと目で“どこが何を担当してるか”見えないだろ。
このコードだと、画面を上下にスクロールで何度も往復しないと、全体の意味がつかめないじゃないか。

なんでそうなるかっていうと、責任がごちゃごちゃに混ざってるからなんだよ。
他の人が見て理解しやすい構造ってのはな、“責任を分けて、ひと目で理解できる形”になって、初めて意味があるんだよ」

若手は、黙ってコードを見つめ直す。

意味は分けた。責任も整理した。でも──それが“見える形”になっていない。

そこにまだ、“読める設計”の壁があった。

変化に強い構造とは、“責任を並べて見える形”だった

責任を分けて、意味を整理して、useReducerも導入した。
でも――師匠の言葉で、若手は気づいた。

「構造」は、“分けただけ”では見えない。
“人に見える配置”になって初めて意味を持つ。

若手は、コードを整理し直した。
Appの中で「入力」「状態の意味付け」「表示」の責任を横に並ぶ形で再配置した。

・入力(InputForm)
・変換(todoReducer)
・出力(TodoList)

import { useReducer, useState } from 'react';
import TodoList from './TodoList'; // Sink(出力)コンポーネント

// Transfer:状態の意味付けと変化ルールを一箇所に集約
function todoReducer(todos: string[], action: { type: string; payload?: string }): string[] {
  switch (action.type) {
    case 'ADD_TODO':
      if (!action.payload || action.payload.trim() === '') return todos;
      return [...todos, action.payload.trim()];
    default:
      return todos;
  }
}

function App() {
  // Source:ユーザーの入力状態
  const [text, setText] = useState('');
  const [todos, dispatch] = useReducer(todoReducer, []);

  const handleAdd = () => {
    dispatch({ type: 'ADD_TODO', payload: text });
    setText('');
  };

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Todoアプリ</h1>
      <div style={{ display: 'flex', gap: '0.5rem' }}>
        {/* Source:入力 */}
        <input
          type="text"
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="やることを入力"
        />
        <button onClick={handleAdd} disabled={!text.trim()}>
          追加
        </button>
      </div>

      {/* Sink:出力専用。propsで受け取り表示するだけ */}
      <TodoList todos={todos} />
    </div>
  );
}

export default App;

若手:「……これなら、コードを読んだだけで“どういう構造か”が見えてきます」
師匠:「そう。“責任がどこにあるか”がひと目で分かれば、あとから誰が読んでも怖くないんだよ」

実際、若手は自分で追加機能のイメージをしてみた。
どこに何を書けばいいかが、すぐに思い浮かぶ。
修正も追加も“迷いがない”構造になっていた。

若手:「……やっと、“壊れにくいコード”の正体が分かってきました」

構造は、「あとから読める」「意味が伝わる」ためのもの。
それが、変化に強い=育てられる設計の土台だった。

次回の記事予告

ここまでお読みいただき、ありがとうございました。
本記事では、AIを使って開発を進める中で直面した「設計不在」の怖さと、それを乗り越えるために必要な“伝わる設計”の視点について紹介しました。

次回は、師匠から教わった設計の基本――「STS設計(入力・変換・出力)」というシンプルで強力な考え方を軸に、実際にReactアプリの構造を改善していくプロセスを紹介します。

もし、AIの出力をそのまま使ってコードが混乱してきた方、自分の設計に不安を感じている方には、きっと役立つ内容になるはずです。ぜひご期待ください!


また、今回紹介した内容をより実践的に学びたい方には、以下のUdemy講座もおすすめです。

Udemyコース(8,800円 → クーポンで割引中)

▶️ AIとC#で極める!クリーンコードの技法(限定クーポン付き)

  • C#でクリーンコードと設計力を身につける実践講座
  • ChatGPTの活用方法や、伝わるコードの考え方を解説

出版書籍『あきらめない者たち』

▶️ Amazonで見る

  • 技術の基礎からやり直すために、なぜ一歩勇気を振り絞れたのかのノンフィクション作品です。
3
3
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
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?