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?

命名について

Posted at

自分がプログラミングを仕事にし始めたのが10数年前なんですが、その頃から今に至るまでずっと命名には悩んでいますね。

結局はいろんなコードを読んで、書いて感覚を身につけるしかないのですが、原理原則みたいなものはあるので、少しまとめておこうと思います。

あ、ちなみに具体的なトピックはリーダブルコードから抽出しました。

1. 具体的でわかりやすい名前を付ける

避けるべき汎用的な名前

変数名に datatmpres といった汎用的すぎる名称を使うと、コードを読む人はその変数が何を表しているのか判断できません。

// 悪い例
const data = await fetchUserInfo(userId);
const res = calculateTotal(items);
const tmp = user.name.split(' ');

これらの変数名からは、何のデータなのか、何の結果なのか、何を一時保存しているのかが全く分かりません。

// 良い例
const userProfile = await fetchUserInfo(userId);
const orderTotal = calculateTotal(items);
const nameParts = user.name.split(' ');

具体的な名前にすることで、コードを読んだだけで変数の役割が明確になります。

命名規則の一貫性を保つ

プロジェクト全体で一貫した命名パターンを採用することで、コードの予測可能性が高まります。

よく使われる命名パターン:

  • データ取得: getfetchload + 対象
    • getUserProfile(), fetchOrderHistory(), loadConfiguration()
  • データ保存: savestorepersist + 対象
    • saveUserPreferences(), storeCache(), persistSession()
  • データ更新: updatemodifychange + 対象
    • updateUserEmail(), modifySettings(), changePassword()
  • データ削除: deleteremoveclear + 対象
    • deleteAccount(), removeItem(), clearCache()
  • 真偽値: ishascanshould + 状態
    • isAuthenticated, hasPermission, canEdit, shouldRetry
// 一貫性のある命名例
class UserRepository {
  async getUser(id) { /* ... */ }
  async getUserList(filters) { /* ... */ }
  async saveUser(userData) { /* ... */ }
  async updateUser(id, updates) { /* ... */ }
  async deleteUser(id) { /* ... */ }
}

2. 品質を高める命名の原則

コードの品質を向上させるためには、要素の種類に応じた適切な命名パターンを使用することが重要です。

メソッド・関数は動詞で始める

メソッドや関数は「アクション」を表すため、動詞で始めることで何をするのかが明確になります。

// 悪い例:名詞で始まっている
class UserAccount {
  validation(data) { /* ... */ }
  creation(userData) { /* ... */ }
  removal(userId) { /* ... */ }
}

// 良い例:動詞で始まる
class UserAccount {
  validateData(data) { /* ... */ }
  createUser(userData) { /* ... */ }
  removeUser(userId) { /* ... */ }
}

一般的な動詞とその用途:

// データ操作
const user = getUser(id);           // 取得
const users = findUsers(criteria);   // 検索
const result = createUser(data);     // 作成
const updated = updateUser(id, data); // 更新
const deleted = deleteUser(id);      // 削除

// 計算・変換
const total = calculateTotal(items);      // 計算
const json = convertToJson(object);       // 変換
const formatted = formatDate(date);       // 整形
const validated = validateEmail(email);   // 検証

// 状態変更
const initialized = initializeApp();      // 初期化
const rendered = renderComponent();       // 描画
const enabled = enableFeature();          // 有効化
const disabled = disableFeature();        // 無効化

// 非同期操作
const data = await fetchUserData(id);     // 取得(非同期)
const saved = await saveToDatabase(data); // 保存(非同期)
const loaded = await loadResources();     // 読み込み(非同期)

クラスは名詞・名詞句で命名する

クラスは「モノ」や「概念」を表すため、名詞または名詞句で命名します。

// 悪い例:動詞が含まれている
class ManageUsers { /* ... */ }
class HandlePayment { /* ... */ }
class ProcessOrder { /* ... */ }

// 良い例:名詞・名詞句
class UserManager { /* ... */ }
class PaymentHandler { /* ... */ }
class OrderProcessor { /* ... */ }

クラス命名のパターン:

// エンティティ(実世界のモノ)
class User { /* ... */ }
class Product { /* ... */ }
class Order { /* ... */ }
class Invoice { /* ... */ }

// サービス・マネージャー(機能の集合)
class AuthenticationService { /* ... */ }
class DatabaseManager { /* ... */ }
class CacheHandler { /* ... */ }
class EmailSender { /* ... */ }

// コントローラー(制御を行うもの)
class UserController { /* ... */ }
class PaymentController { /* ... */ }
class OrderController { /* ... */ }

// ビルダー・ファクトリー(生成を行うもの)
class UserBuilder { /* ... */ }
class QueryBuilder { /* ... */ }
class ConnectionFactory { /* ... */ }

// リポジトリ(データアクセス)
class UserRepository { /* ... */ }
class ProductRepository { /* ... */ }
class OrderRepository { /* ... */ }

// ユーティリティ(補助機能)
class DateUtils { /* ... */ }
class StringHelper { /* ... */ }
class MathUtils { /* ... */ }

変数・定数は名詞で命名する

変数や定数は「データ」を表すため、名詞で命名します。

// 悪い例:動詞が含まれている
const getResult = await fetchData();
const doProcess = true;
const handleClick = (e) => { /* ... */ };

// 良い例:名詞
const result = await fetchData();
const isProcessing = true;
const clickHandler = (e) => { /* ... */ };

変数命名のパターン:

// 単純な値
const userName = 'John Doe';
const itemCount = 42;
const totalPrice = 1500;

// コレクション(複数形を使う)
const users = [];
const products = [];
const orderItems = [];

// 真偽値(疑問文の形)
const isAuthenticated = true;
const hasPermission = false;
const canEdit = true;
const shouldRetry = false;

// オブジェクト
const userProfile = { name: 'John', age: 30 };
const apiConfig = { baseUrl: '...', timeout: 5000 };
const formData = new FormData();

// 関数参照(動詞 + 'er'または'Handler')
const validator = (data) => { /* ... */ };
const formatter = (text) => { /* ... */ };
const clickHandler = (event) => { /* ... */ };
const errorHandler = (error) => { /* ... */ };

インターフェース・型は名詞で命名する

TypeScriptなどでインターフェースや型を定義する際も、名詞を使用します。

// インターフェース
interface User {
  id: string;
  name: string;
  email: string;
}

interface PaymentMethod {
  type: string;
  cardNumber: string;
}

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

// 型エイリアス
type UserId = string;
type ProductId = number;
type OrderStatus = 'pending' | 'completed' | 'cancelled';

// ジェネリック型(通常は大文字1文字)
interface Repository<T> {
  findById(id: string): Promise<T>;
  save(item: T): Promise<void>;
}

真偽値は疑問文の形で命名する

真偽値の変数や関数は、Yes/Noで答えられる疑問文の形にすると、条件文が読みやすくなります。

// 悪い例:疑問文でない
const login = user.checkLogin();
const valid = validateForm();
const permission = user.checkPermission('edit');

if (login && valid && permission) {
  // 読みにくい
}

// 良い例:疑問文の形
const isLoggedIn = user.checkLogin();
const isValid = validateForm();
const hasEditPermission = user.checkPermission('edit');

if (isLoggedIn && isValid && hasEditPermission) {
  // 読みやすい
}

真偽値命名のプレフィックス:

// is: 状態を表す
const isActive = true;
const isLoading = false;
const isVisible = true;
const isEmpty = list.length === 0;

// has: 所有を表す
const hasChildren = element.children.length > 0;
const hasError = errors.length > 0;
const hasPermission = checkPermission(user);

// can: 可能性を表す
const canEdit = user.role === 'admin';
const canDelete = user.id === owner.id;
const canSubmit = form.isValid();

// should: 推奨を表す
const shouldUpdate = version < latestVersion;
const shouldRetry = attemptCount < maxAttempts;
const shouldNotify = preferences.notifications;

// will: 未来の動作を表す
const willExpire = expiryDate < tomorrow;
const willOverflow = newSize > maxSize;

3. 名前の規則はスコープに依存する

変数のスコープ(影響範囲)によって、適切な命名の詳細度は変わります。

スコープが小さい場合

ループカウンタや数行で完結する一時変数には、短い名前を使っても問題ありません。

// 許容される短い名前の例
for (let i = 0; i < items.length; i++) {
  const item = items[i];
  processItem(item);
}

// 数行で完結する一時変数
const tmp = rawData.trim().toLowerCase();
const normalized = tmp.replace(/\s+/g, '-');

この itmp は数行のスコープ内でのみ使用され、すぐに参照が終わるため、短い名前でも可読性を損ないません。

スコープが大きい場合

クラスのプロパティ、グローバル変数、モジュールレベルの定数など、スコープが広い変数には具体的で説明的な名前が必要です。

// 悪い例:スコープが大きいのに汎用的な名前
class DataManager {
  constructor() {
    this.data = {};  // 何のデータ?
    this.temp = [];  // 何の一時データ?
    this.flag = false;  // 何のフラグ?
  }
}

// 良い例:スコープに応じた具体的な名前
class UserSessionManager {
  constructor() {
    this.activeUserSessions = {};
    this.pendingAuthRequests = [];
    this.isMaintenanceMode = false;
  }
}

スコープと命名の関係性

スコープの範囲 推奨される名前の長さ
ループ内(1-3行) 1-2文字 i, j, k, x, y
関数内(数行-十数行) 1-2単語 user, total, result
クラスプロパティ 2-3単語 userProfile, orderTotal
グローバル/定数 3-4単語以上 MAX_RETRY_ATTEMPTS, DEFAULT_API_TIMEOUT

4. 長くなりすぎることを避ける

具体的な名前を心がけると、どうしても変数名が長くなりがちです。しかし、長すぎる名前は逆に可読性を損ないます。

適切な短縮形を使う

プロジェクト内で合意された略語を使用することで、可読性を保ちながら名前を短縮できます。

// 一般的に受け入れられている短縮形
const config = loadConfiguration();  // configuration → config
const ctx = canvas.getContext('2d');  // context → ctx
const params = parseUrlParameters();  // parameters → params
const repo = new UserRepository();  // repository → repo
const auth = new AuthService();  // authentication → auth
const img = loadImage('photo.jpg');  // image → img
const btn = document.querySelector('.submit');  // button → btn
const msg = formatErrorMessage(error);  // message → msg

短縮形を使う際の注意点:

  • チーム内で共通認識のある略語のみを使用する
  • プロジェクトの命名規則ドキュメントに記載する
  • 母音を削除する方式(例: messagemsg)は一般的に理解しやすい
  • 独自の略語を作りすぎない

不要な単語を避ける

冗長な単語や情報を削除することで、名前を簡潔にできます。

// 悪い例:冗長な命名
const theUserList = getTheUserList();
const doProcessData = (data) => { /* ... */ };
const convertToString = (value) => String(value);
const managerOfUsers = new UserManager();

// 良い例:不要な単語を削除
const userList = getUserList();
const processData = (data) => { /* ... */ };
const toString = (value) => String(value);
const userManager = new UserManager();

削除できる不要な単語:

  • 冠詞: the, a, an
  • 汎用的な動詞: do, handle, manage(文脈で明らかな場合)
  • 型を示す接尾語: Array, Object, String(型システムがある場合)
  • 冗長な前置詞: of, for(名詞の連結で十分な場合)
// リファクタリング例
// Before
const listOfActiveUsers = getListOfActiveUsers();
const convertDataToJsonString = (data) => JSON.stringify(data);

// After
const activeUsers = getActiveUsers();
const toJson = (data) => JSON.stringify(data);

フォーマットで情報を伝える

命名規則やフォーマットを使って、変数の種類や役割を視覚的に区別できます。

// ケーススタイルによる区別
const userName = 'John';  // camelCase: 通常の変数
const UserService = class {};  // PascalCase: クラス
const MAX_ATTEMPTS = 3;  // UPPER_SNAKE_CASE: 定数
const is_valid = true;  // snake_case: (言語による)

// プレフィックスによる区別
class Component {
  constructor() {
    this._privateField = null;  // アンダースコア: プライベート
    this.publicField = null;    // プレフィックスなし: パブリック
    this.$element = null;       // $記号: jQuery要素(慣習)
  }
}

// タイプ別の接頭語
const arrUsers = [];  // 配列
const objConfig = {};  // オブジェクト
const fnCallback = () => {};  // 関数
const bIsActive = true;  // 真偽値
const nCount = 0;  // 数値

実践的なフォーマット規則の例:

// TypeScriptでの実践例
class UserManager {
  // プライベートプロパティ
  private _cache: Map<string, User>;
  private _isInitialized: boolean;
  
  // パブリックプロパティ
  public readonly maxUsers: number;
  
  // メソッド
  public async getUser(userId: string): Promise<User> { /* ... */ }
  private _validateUserId(userId: string): boolean { /* ... */ }
}

// 定数
export const DEFAULT_TIMEOUT_MS = 5000;
export const API_BASE_URL = 'https://api.example.com';

// 型定義
export interface UserProfile { /* ... */ }
export type UserId = string;

まとめ

基本原則

  1. 具体的な名前: datatmp ではなく、何のデータか明確にする
  2. 一貫性: プロジェクト全体で統一された命名パターンを使う
  3. スコープに応じた詳細度: 影響範囲が広いほど具体的な名前にする
  4. 適度な長さ: 具体的でありながら、冗長にならないバランスを取る

品質向上のための原則

  1. 品詞の使い分け: メソッドは動詞、クラスは名詞、変数は名詞
  2. 真偽値の命名: is, has, can, should を使った疑問文の形
  3. 動作の一致: 名前から期待される動作と実際の動作を一致させる
  4. チームでの合意: 命名規則をドキュメント化し、チーム全体で共有する
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?