インターフェースの概念
CoddyのCジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 40/61。
これまで、私たちの構造体では、たとえば名前と function pointer のように、データと振る舞いが一緒になっていました。しかし、構造体が function pointer だけを含んでいたらどうでしょうか? これによって、強力なものが作られます。それが interface です。
interface は、どのような操作を利用できるかを定義しますが、それらがどのように動作するかは指定しません。これは、「この interface を実装するすべてのオブジェクトは、これらの functions を提供しなければならない」と示す契約です。
typedef void (*LogFunc)(const char* message);
typedef struct {
LogFunc log;
} ILogger;ILogger 構造体には、function pointer 以外には何も含まれていません。独自のデータは持っていません。純粋に動作を記述します。「I」接頭辞は、これが interface であることを示す一般的な慣例です。
ILoggerを受け取るコードは、ログメッセージの送信先を気にしません。コンソールに出力したり、ファイルに書き込んだり、ネットワーク経由でデータを送信したりできます。呼び出し元は、logを呼び出せることだけを知っていればよいのです。
void process(ILogger* logger, const char* msg) {
logger->log(msg); // どのようにログを取るのかは分からない
}この分離がポリモーフィズムの本質です。process関数は、どのようなロガー実装とも連携できます。関数のコードを変更せずに、動作を入れ替えられます。次のレッスンでは、このインターフェース契約を満たす具体的な実装を作成します。
チャレンジ
簡単ICalculator interface を構築しましょう。これは function pointer のみを含む struct であり、各 calculator がサポートすべき操作を、それらの実装方法を指定せずに定義します。
コードを整理するために、2つのファイルを作成します。
calculator.h:ここで interface を Define します。2つのintparameters を受け取り、intを返す、BinaryOpという名前の function pointer 型を Create します。次に、2つの function pointer のみを含むICalculatorstruct を Define します。1つはoperate(型はBinaryOp)、もう1つは parameters を取らず何も返さない function pointer であるdescribeです。この struct は純粋な interface です。データは保持せず、behavior のみを保持します。main.c:header を include し、interface を満たすことができる具体的な functions を Implement します。add:2つの integers の合計を返しますmultiply:2つの integers の product を返しますdescribe_add:Additionを表示しますdescribe_multiply:Multiplicationを表示します
ICalculatorinstance を Create し、input に基づいて接続して、計算を実行するために使用します。
プログラムは3つの input を受け取ります。2つの integers と、operation code(加算の場合は1、乗算の場合は2)です。
operation code に基づいて、ICalculator に appropriate な function の組み合わせを接続します。次に interface を通じて describe を Call して操作名を表示し、その後 operate を Call して計算結果を求め、result を表示します。
入力が 5、3、1 の場合の出力例:
Addition
8入力が 4、7、2 の場合の出力例:
Multiplication
28重要な insight は、ICalculator struct が contract を Define していることです。これを使用する code は、どの specific な functions が接続されているかを知らなくても、describe と operate を Call できることを know しています。header file では include guards を使用することを忘れないでください。
自分で試してみよう
#include <stdio.h>
#include "calculator.h"
// TODO: add関数を実装する
// 2つの整数の和を返す
// TODO: multiply関数を実装する
// 2つの整数の積を返す
// TODO: describe_add関数を実装する
// "Addition"を出力する
// TODO: describe_multiply関数を実装する
// Prints "Multiplication"
int main() {
int num1, num2, op_code;
scanf("%d", &num1);
scanf("%d", &num2);
scanf("%d", &op_code);
// TODO: ICalculatorのインスタンスを作成する
// TODO: op_codeに基づいて(1はadd、2はmultiply)、
// 適切な関数でcalculatorを接続する
// TODO: インターフェース経由でdescribeを呼び出す
// TODO: インターフェース経由でoperateを呼び出し、結果を出力する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Cオンラインコンパイラ