Menu
Coddy logo textTech

インターフェースの概念

CoddyのCジャーニー「Object Oriented Programming」セクションの一部 — レッスン 40/61。

これまで、私たちの構造体はデータと振る舞いを混在させてきました。例えば、名前と関数ポインタを並べるような形です。しかし、もし構造体に関数ポインタだけが含まれていたらどうなるでしょうか?これにより、強力なもの、つまりインターフェースが作成されます。

インターフェースは、それらがどのように動作するかを指定することなく、どのような操作が利用可能であるかを定義します。これは、「このインターフェースを実装するすべてのオブジェクトは、これらの関数を提供しなければならない」という契約です。

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

ILogger 構造体は、関数ポインタのみを保持しています。それ自体はデータを持たず、純粋に振る舞いを記述します。「I」という接頭辞は、これがインターフェースであることを示す一般的な慣習です。

ILoggerを受け取るコードは、ログメッセージがどこに送られるかを気にしません。コンソールへの出力、ファイルへの書き込み、あるいはネットワーク経由でのデータ送信など、どこでも構いません。呼び出し側は、単にlogを呼び出せることだけを知っています:

void process(ILogger* logger, const char* msg) {
    logger->log(msg);  // どのようにログが記録されるかはわかりません
}

この分離こそがポリモーフィズムの本質です。process 関数は、どのようなロガーの実装でも動作します。つまり、関数のコードを変更することなく、振る舞いを入れ替えることができます。次のレッスンでは、このインターフェースの契約を満たす具体的な実装を作成します。

challenge icon

チャレンジ

簡単

ICalculator インターフェースを構築しましょう。これは関数ポインタのみを含む構造体で、具体的な動作を指定せずに、あらゆる計算機がサポートしなければならない操作を定義します。

コードを整理するために、2つのファイルを作成します:

  • calculator.h: ここでインターフェースを定義します。2つの int 型パラメータを受け取り、int 型を返す BinaryOp という名前の関数ポインタ型を作成します。次に、2つの関数ポインタのみを含む ICalculator 構造体を定義します。それは、operateBinaryOp 型)と describe(パラメータなし、戻り値なしの関数ポインタ)です。この構造体は純粋なインターフェースであり、データは保持せず、振る舞いのみを保持します。
  • main.c: ヘッダーをインクルードし、インターフェースを満たす具体的な関数を実装します:
    • add — 2つの整数の和を返します
    • multiply — 2つの整数の積を返します
    • describe_addAddition を出力します
    • describe_multiplyMultiplication を出力します
    次に、ICalculator インスタンスを作成し、入力に基づいて関数を紐付け、それを使用して計算を実行します。

プログラムは3つの入力を受け取ります:2つの整数と、オペレーションコード(1は加算、2は乗算)です。

オペレーションコードに基づいて、ICalculator に適切な関数のペアを紐付けます。次に、インターフェースを介して describe を呼び出して操作名を出力し、続いて operate を呼び出して結果を計算し出力します。

入力が 531 の場合の出力例:

Addition
8

入力が 472 の場合の出力例:

Multiplication
28

重要なポイントは、ICalculator 構造体が契約を定義しているということです。それを使用するコードは、どの特定の関数が紐付けられているかを知らなくても、describeoperate を呼び出せることを知っています。ヘッダーファイルではインクルードガードを使用することを忘れないでください。

チートシート

インターフェースとは、関数ポインタのみを含む構造体であり、操作がどのように機能するかを指定せずに、どのような操作が利用可能かを定義するものです。

インターフェースの定義:

typedef void (*LogFunc)(const char* message);

typedef struct {
    LogFunc log;
} ILogger;

「I」という接頭辞は、これがインターフェースであることを示す一般的な慣習です。この構造体にはデータは含まれず、純粋に振る舞いを記述します。

インターフェースの使用:

void process(ILogger* logger, const char* msg) {
    logger->log(msg);  // どのようにログが記録されるかは関知しません
}

この分離により、ポリモーフィズム(多態性)が可能になります。関数はインターフェースのあらゆる実装で動作するため、関数のコードを変更することなく振る舞いを入れ替えることができます。

自分で試してみよう

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

// TODO: add関数を実装する
// 2つの整数の和を返す


// TODO: multiply関数を実装する
// 2つの整数の積を返す


// TODO: describe_add関数を実装する
// "Addition"と表示する


// TODO: describe_multiply関数を実装する
// "Multiplication"と表示する


int main() {
    int num1, num2, op_code;
    scanf("%d", &num1);
    scanf("%d", &num2);
    scanf("%d", &op_code);
    
    // TODO: ICalculatorのインスタンスを作成する
    
    
    // TODO: op_code(1は加算、2は乗算)に基づいて、
    // 適切な関数を電卓に紐付ける
    
    
    // TODO: インターフェースを通じてdescribeを呼び出す
    
    
    // TODO: インターフェースを通じてoperateを呼び出し、結果を表示する
    
    
    return 0;
}
quiz icon腕試し

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

Object Oriented Programmingのすべてのレッスン