Menu
CoddyTech

セッターでのバリデーション

CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 20/61。

前のレッスンでは、値を代入するだけの基本的なセッターを作成しました。しかし、これではカプセル化の重要な利点である、検証ロジックを追加してデータを保護する機能が活かされていません。

負の値になってはいけないCounterを考えてみましょう。カプセル化がなければ、誰でもc->value = -50と書いて状態を壊せてしまいます。セッターを使えば、許可する値を制御できます。

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // 無効な入力は何も通知せずに拒否する
    }
    c->value = new_value;
}

これで、負の値は単に無視されます。

成功または失敗を示すステータスコードを返すこともできます。

int counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return 0;  // 失敗
    }
    c->value = new_value;
    return 1;  // 成功
}

このパターンによって、呼び出し元は操作が成功したかどうかを知ることができます。重要なポイントは、セッターは単なる代入ではないということです。セッターは、オブジェクトの状態として許容される値を制御する門番です。これこそがカプセル化の真の力です。外部のコードが何をしようとしても、オブジェクトは自身の整合性を保ちます。

challenge icon

チャレンジ

簡単

セッターでの検証によってデータを保護するTemperatureモジュールを作成しましょう。有効範囲内の値だけを受け付ける温度トラッカーを作成します。セッターがオブジェクトの状態を守る門番としてどのように機能するかを確認しましょう。

3つのファイルを作成します。

  • temperature.h: 不透明ポインターパターンを使用した公開インターフェースです。前方宣言のみでTemperature型を宣言します。次の関数を宣言します。
    • create_temperature: 初期温度を整数で受け取り、Temperatureポインターを返します
    • free_temperature: 割り当てられたメモリを解放します
    • temp_get_degrees: 現在の温度値を返すゲッターです
    • temp_set_degrees: int(成功時は1、失敗時は0)を返すセッターです
    シンボルTEMPERATURE_Hを使用してインクルードガードを設定します。
  • temperature.c: 非公開のint degreesメンバーを持つ実際のstruct Temperatureを定義します。すべての関数を実装します。重要なのはセッターです。セッターは-50から150まで(両端を含む)の値だけを受け付けるようにします。値がこの範囲外の場合は0を返し、温度を変更しません。有効な場合は値を更新して1を返します。
  • main.c: 有効な温度と無効な温度の両方を設定しようとすることで検証を実演し、無効な値が拒否され、有効な値が受け入れられることを示します。

入力として、初期温度、設定する有効な温度(範囲内)、設定を試みる無効な温度(範囲外)の3つを受け取ります。

mainファイルで、初期値を使ってTemperatureを作成し、その値を出力します。次に、無効な温度は拒否されるはずなので、設定を試みて結果を出力し、値が変わっていないことを示します。最後に、有効な温度を設定し、成功した結果と更新後の値を出力します。メモリを解放し、後片付けが完了したことを確認します。

次の形式で出力します。

Initial: {degrees}
Set {invalid}: {0 or 1}
After invalid: {degrees}
Set {valid}: {0 or 1}
After valid: {degrees}
Freed

たとえば、入力が25、100、200の場合、出力は次のようになります。

Initial: 25
Set 200: 0
After invalid: 25
Set 100: 1
After valid: 100
Freed

これはカプセル化の真価を示しています。セッターが有効な温度の条件を適用するため、外部コードがどのような値を設定しようとしても、オブジェクトの整合性が保護されます。

自分で試してみよう

#include <stdio.h>
#include "temperature.h"

int main() {
    // 入力を読み取る
    int initial, valid_temp, invalid_temp;
    scanf("%d", &initial);
    scanf("%d", &valid_temp);
    scanf("%d", &invalid_temp);
    
    // TODO: 初期値を使ってTemperatureを作成する
    
    // TODO: 初期温度を表示する
    // 形式: "Initial: {degrees}"
    
    // TODO: 無効な温度の設定を試みる
    // 結果を表示し、値が変わっていないことを示す
    // 形式: "Set {invalid}: {0 or 1}"
    // 形式: "After invalid: {degrees}"
    
    // TODO: 有効な温度を設定する
    // 成功結果を表示し、更新後の値を示す
    // 形式: "Set {valid}: {0 or 1}"
    // 形式: "After valid: {degrees}"
    
    // TODO: メモリを解放し、"Freed"を表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Cオンラインコンパイラ