セッターでのバリデーション
CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 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; // 成功
}このパターンは、操作が成功したかどうかを呼び出し元に知らせます。重要な洞察は、セッターは単なる代入のためのものではなく、オブジェクトの状態がどうあるべきかというルールを強制するゲートキーパーであるということです。これがカプセル化の真の力です。外部のコードが何をしようとしても、オブジェクトは自らの整合性を維持します。
チャレンジ
簡単セッターでのバリデーションを使用してデータを保護する Temperature モジュールを構築しましょう。有効な範囲内の値のみを受け入れる温度トラッカーを作成し、セッターがオブジェクトの状態に対するゲートキーパーとしてどのように機能するかを実証します。
3つのファイルを作成します:
temperature.h: 不透明ポインタ(opaque pointer)パターンを使用したパブリックインターフェースです。前方宣言のみを使用して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つの入力を受け取ります:初期温度、設定する有効な温度(範囲内)、および試行する無効な温度(範囲外)です。
メインファイルで、初期値を使用して 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これはカプセル化の真の力を示しています。セッターが有効な温度を構成するルールを強制し、外部コードがどのような値を割り当てようとしても、オブジェクトの整合性を保護します。
チートシート
セッターには、データの整合性を保護するためのバリデーション(検証)ロジックを含めることができます。単に値を代入するのではなく、オブジェクトの有効な状態に関するルールを強制するゲートキーパーとして機能します。
基本的なバリデーションの例 - 無効な入力を黙って拒否する:
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; // 成功
}このパターンにより、呼び出し側は操作が成功したかどうかを確認できるようになり、外部コードの試行に関わらず、オブジェクトが自らの整合性を維持することが可能になります。
自分で試してみよう
#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: initial の値で 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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。