Menu
Coddy logo textTech

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

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

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

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

// counter.c
void counter_set_value(Counter *c, int new_value) {
    if (new_value < 0) {
        return;  // 無効な入力を静かに拒否する
    }
    c->value = new_value;
}

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

成功または Failure を示すステータスコードを返すことを選択する場合もあります。

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

このパターンにより、呼び出し元は操作が成功したかどうかを知ることができます。重要な点は、setter は単なる代入のためのものではないということです。setter は、オブジェクトの状態がどのようなものであり得るかについてのルールを適用する門番なのです。これがカプセル化の真の力です。外部コードが何をしようとしても、オブジェクトは自分自身の整合性を維持します。

challenge icon

チャレンジ

簡単

セッターでの検証を使ってデータを保護する Temperature モジュールを構築しましょう。妥当な範囲内の値のみを受け付ける温度トラッカーを作成し、セッターがオブジェクトの状態を守る門番として機能する方法を示します。

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

  • temperature.h: opaque pointer パターンを使用した公開インターフェースです。前方宣言のみで Temperature 型を Declare します。次の関数を Declare します。
    • create_temperature: initial degrees 用の整数を受け取り、Temperature ポインターを返します
    • free_temperature: allocated memory を解放します
    • temp_get_degrees: 現在の温度値を返す Getter です
    • temp_set_degrees: int(成功時は1、failure 時は0)を返す Setter です
    シンボル TEMPERATURE_H を使って include guards を設定します。
  • temperature.c: hidden な int degrees member を持つ実際の struct Temperature を Define します。すべての関数を Implement します。重要なのは Setter です。-50 以上 150 以下(inclusive)の値のみを accept する必要があります。値がこの範囲外の場合は0を返し、温度を変更せずに leave します。valid の場合は値を更新して1を返します。
  • main.c: valid と invalid の両方の温度を設定しようとすることで検証を実演し、invalid の値が拒否され、valid な値が受け入れられることを示します。

3つの入力を受け取ります。initial temperature、設定する範囲内の valid temperature、そして試行する範囲外の invalid temperature です。

main ファイルで、initial value を使って Temperature を作成し、それを出力します。次に、invalid temperature は拒否されるはずなので、その設定を試み、結果を出力して値が変更されていないことを示します。最後に valid temperature を設定し、成功結果を出力して更新された値を示します。memory を Free して cleanup を確認します。

次の形式で出力します。

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

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

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

これはカプセル化の真の力を示しています。Setter が valid temperature の条件に関するルールを強制するため、外部コードがどのような値を assign しようとしても、オブジェクトの整合性が保護されます。

自分で試してみよう

#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}"
    // Format: "After invalid: {degrees}"
    
    // TODO: 有効な温度を設定する
    // 成功結果を出力し、更新された値を示す
    // 形式: "Set {valid}: {0 or 1}"
    // 形式: "After valid: {degrees}"
    
    // TODO: メモリを解放し、"Freed"を出力する
    
    return 0;
}
quiz icon腕試し

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

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

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