テンプレートメソッドパターン
1シミュレーションシーン
私たちがチップサプライヤーであり、今、新しいCPUを開発したとします。i2c、spi、usbなど、さまざまなバスコントローラーがCPUに統合されています。お客様のCPUをスムーズにご利用いただくために、各種バスコントローラに対応したドライバソフトウェアをお客様に提供する必要があります。
i2cを例にして、チップサプライヤーとして顧客に提供するドライバーソフトウェアがどのようなものかを見てみましょう。
(追記:読者はドライバーソフトウェアについてある程度理解していることを前提としているため、この記事ではi2cとドライバーソフトウェアの基本概念については説明しません。)
「i2c読み取り操作」によって駆動される疑似コードは次のとおりであると想定します。
I2cRead()
{
if (I2cAdaptorOperation()) { // 第一步
I2cClientOperation() // 第二步
}
}
I2cAdaptorOperation()
{
具体实现略
}
I2cClientOperation()
{
具体实现略
}
上記は、「i2c読み取り操作」ドライブ全体の疑似コードです。上記の疑似コードは3つの部分に分かれています。
最初の部分はI2cAdaptorOperation()です。これは、CPUに統合されたi2cコントローラーの動作です。コードのこの部分の実装は、私たちの責任です(チップサプライヤー)。i2cコントローラーは私たちが実装しているので、i2cコントローラーのドライバーを実装する方法はわかっています。
2番目の部分はI2cClientOperation()です。これは、i2c周辺機器の操作です。コードのこの部分の実装は、お客様の責任です。お客様はCPUを使用してさまざまなi2c周辺機器(i2cのrtc、i2cのledなど)を接続する場合があるため、お客様の周辺機器が何であるかわからないため、コードのこの部分を実装する方法はありません。
3番目の部分はI2cRead()です。これは、i2c読み取り操作のメインフローコードです。それで、コードのこの部分は私たちまたは顧客によって実装されていますか?どちらの方法も一般的です。
お客様がI2cRead()を実装する責任がある方法について:一般的に、私たち(つまり、チップサプライヤー)は、i2cコントローラーの設計時にI2cRead()を実装する方法を明確に定義しており、お客様はI2cRead()の実装方法を知らないことがよくあります。したがって、私たちは通常、参照用にI2cRead()のデモコードを顧客に提供し、顧客はデモコードに従ってI2cRead()を実装します。
私たち(つまりチップサプライヤー)がI2cRead()の実装を担当する方法については、I2cRead()はI2cClientOperation()を呼び出す必要があり、I2cClientOperation()は顧客によって実装されます。したがって、I2cRead()を実装するとき、I2cClientOperation()はまだ実装されていません。現時点では、I2cClientOperation()の抽象インターフェースのみを定義し、I2cRead()でI2cClientOperation()の抽象インターフェースを呼び出すことができます。その後、クライアントはI2cClientOperation()の抽象インターフェースを書き換えます。
ここで「I2cRead()の実装を担当する(つまりチップサプライヤー)方法」はテンプレートメソッドです。これを例として、テンプレートメソッドのアイデアと実装を整理してみましょう。
2テンプレートメソッドの概要
テンプレートメソッドは、操作のアルゴリズムスケルトンを定義し、サブクラスへのいくつかのステップを遅らせます。テンプレートメソッドを使用すると、サブクラスは、アルゴリズムの構造を変更せずに、アルゴリズムの特定のステップを再定義できます。
上記の例に対応します。
I2cRead()はアルゴリズムのスケルトンです
I2cClientOperation()はサブクラスへの遅延のステップです
3テンプレートメソッドを使用して、チップサプライヤー向けの「i2c読み取り操作」ドライバーを実現します。
参加者
- 抽象クラス:I2cRead
i2c読み取り操作のメインフローには、フローで呼び出されるメソッドの実装が含まれます。ただし、I2cClientOperation()メソッドは特定のクラスで書き直す必要があります。
この部分は、i2cコントローラーを統合するCPUチップサプライヤーによって実装されます。
- ConcreteClass:RtcI2cRead、LedI2cRead
周辺機器(rtc、led)の特定の条件に従って、I2cReadを継承し、I2cClientOperation()を書き換えます。
この部分は、CPUチップを購入する顧客によって複雑に実現されます。
UML
I2cReadサンプルコード
i2c_read.h
#ifndef I2C_READ_H
#define I2C_READ_H
struct I2cRead {
void (*Read)(struct I2cRead *this, char *operationCode, char *ret);
int (*AdapterOperation)(struct I2cRead *this);
void (*ClientOperation)(struct I2cRead *this, char *operationCode, char *ret);
};
// 构造函数
void I2cRead(struct I2cRead *this);
// 析构函数
void _I2cRead(struct I2cRead *this);
#endif
i2c_read.c
#include "i2c_read.h"
#include <stdio.h>
static void Read(struct I2cRead *this, char *operationCode, char *ret)
{
if (this->AdapterOperation(this) == 0) {
this->ClientOperation(this, operationCode, ret);
}
}
static int AdapterOperation(struct I2cRead *this)
{
printf(" 执行i2c适配器操作\n");
return 0;
}
static void ClientOperation(struct I2cRead *this, char *operationCode, char *ret)
{
printf(" 请重写i2c外设操作\n");
ret[0] = '\0';
}
// 构造函数
void I2cRead(struct I2cRead *this)
{
this->Read = Read;
this->AdapterOperation = AdapterOperation;
this->ClientOperation = ClientOperation;
}
// 析构函数
void _I2cRead(struct I2cRead *this)
{
this->Read = NULL;
this->AdapterOperation = NULL;
this->ClientOperation = NULL;
}
RtcI2cReadサンプルコード
rtc_i2c_read.h
#include "i2c_read.h"
// 构造函数
void RtcI2cRead(struct I2cRead *this);
// 析构函数
void _RtcI2cRead(struct I2cRead *this);
rtc_i2c_read.c
#include "rtc_i2c_read.h"
#include <stdio.h>
#include <string.h>
static void ClientOperation(struct I2cRead *this, char *operationCode, char *ret)
{
printf(" 根据operatinCode执行Rtc读操作:\n");
printf(" ......\n");
printf(" 返回读Rtc的结果\n");
strcpy(ret, "时间为8:15:00\n");
}
// 构造函数
void RtcI2cRead(struct I2cRead *this)
{
I2cRead(this);
this->ClientOperation = ClientOperation;
}
// 析构函数
void _RtcI2cRead(struct I2cRead *this)
{
_I2cRead(this);
}
LedI2cReadサンプルコード
led_i2c_read.h
#include "i2c_read.h"
// 构造函数
void LedI2cRead(struct I2cRead *this);
// 析构函数
void _LedI2cRead(struct I2cRead *this);
led_i2c_read.c
#include "led_i2c_read.h"
#include <stdio.h>
#include <string.h>
static void ClientOperation(struct I2cRead *this, char *operationCode, char *ret)
{
printf(" 根据operatinCode执行Led读操作:\n");
printf(" ......\n");
printf(" 返回读Led的结果\n");
strcpy(ret, "Led为关\n");
}
// 构造函数
void LedI2cRead(struct I2cRead *this)
{
I2cRead(this);
this->ClientOperation = ClientOperation;
}
// 析构函数
void _LedI2cRead(struct I2cRead *this)
{
_I2cRead(this);
}
クライアントコードの例
#include "led_i2c_read.h"
#include "rtc_i2c_read.h"
#include <stdio.h>
void main()
{
char ret[100];
printf("读i2c Rtc:\n");
struct I2cRead rtcRead;
RtcI2cRead(&rtcRead);
rtcRead.Read(&rtcRead, "读时间", ret);
printf(" %s", ret);
printf("读i2c Led:\n");
struct I2cRead ledRead;
LedI2cRead(&ledRead);
rtcRead.Read(&ledRead, "读状态", ret);
printf(" %s", ret);
}
クライアントの表示例
-bash-4.2# ./test
读i2c Rtc:
执行i2c适配器操作
根据operatinCode执行Rtc读操作:
......
返回读Rtc的结果
时间为8:15:00
读i2c Led:
执行i2c适配器操作
根据operatinCode执行Led读操作:
......
返回读Led的结果
Led为关