ほとんどの Pebble プロジェクト(シンプルなウォッチフェイスなど)は、単一ファイルのプロジェクトとして問題なく機能します。これは、すべてのコードが 1 つの .c ファイルに配置されていることを意味します。しかし、単一ファイルの Pebble プロジェクトのサイズが大きくなるにつれて、さまざまなコンポーネントがどこに配置されているか、およびそれらが互いにどのように相互作用するかを追跡することが難しくなる可能性があります。たとえば、仮想的なアプリには多数の Window があり、多くのタイプのデータ項目で AppMessage を介した通信を実行し、多数のデータ項目を保存および永続化したり、他のプロジェクトで価値があるコンポーネントを含めたりする場合があります。
最初の例として、Pebble SDK はすでに Window、Layer、AppMessage などの個別のモジュールで構成されています。それぞれの実装は他のものから分離されており、開発者が各モジュールで使用するためのインターフェースは明確に定義されており、めったに変更されません。
このガイドは、このようなアプリを分割するために使用できる手法を提供することを目的としています。モジュラーアプローチの利点には以下が含まれます:
アプリの Window を分離して保つことができ、作業が容易になります。
コンポーネント間で明確に定義されたインターフェースにより、内部の変更が他のモジュールに影響を与えないことが保証されます。
モジュールを他のプロジェクトで再利用したり、共有可能なライブラリにしたりすることができます。
コンポーネント間の変数依存関係が発生しないため、タイプやサイズが変更された場合に問題が発生する可能性があります。
サブコンポーネントの複雑さは各モジュール内に隠されています。
よりシンプルな個別のファイルが保守性を促進します。
モジュールをより簡単にテストできます。
基本的な Pebble プロジェクトは、new-project コマンドで開始されます:
$ pebble new-project modular-project
この新しいプロジェクトには、以下のデフォルトのファイル構造が含まれます。modular-project.c ファイルには、main()、init()、deinit() を含むアプリ全体、および Window と子の TextLayer が含まれます。
modular-project/
resources/
src/
modular-project.c
package.json
wscript
ほとんどのプロジェクトでは、この構造は完全に適切です。.c ファイルが数百行の長さに成長し、共有変数を介して相互に多くの相互作用点を持つ複数のサブコンポーネントを組み込むと、複雑さはいくつかの新しい手法が必要になる点に達します。
この文脈では、「モジュール」は C ヘッダーとソースファイルの「ペア」、つまりモジュールのインターフェースを記述する .h ファイルと、実際のロジックとコードを含む .c ファイルと考えることができます。ヘッダーには、複数回 #include されることによる再定義を防ぐための標準的なステートメント、およびモジュールが他のモジュールで使用できるようにするすべての関数プロトタイプが含まれています。
アプリのサブコンポーネントをモジュールにすることで、面倒なグローバル変数の必要性がなくなり、それらの間の明確なインターフェースが定義されます。ファイル自体は、プロジェクトのメイン src ディレクトリ内の modules ディレクトリに配置され、アプリの他のコンポーネントとは別の場所に保持されます。したがって、data モジュールが追加された(以下で説明)プロジェクトの構造は次のようになります:
modular-project/
resources/
src/
modules/
data.h
data.c
modular-project.c
package.json
wscript
サンプルモジュールのファイルペアを以下に示します。これは動的に割り当てられた整数の配列を管理し、配列から値を設定および取得するためのインターフェースを含んでいます。配列自体は、static キーワードのおかげでモジュールのプライベートです。この手法により、アプリの他のコンポーネントは、実装の詳細を心配することなく、モジュールのインターフェースに従って正しいパラメータで「ゲッター」と「セッター」を呼び出すことができます。
src/modules/data.h
#pragma once // Prevent errors by being included multiple times
#include <pebble.h> // Pebble SDK symbols
void data_init(int array_length);
void data_deinit();
void data_set_array_value(int index, int new_value);
int data_get_array_value(int index);
src/modules/data.c
#include "data.h"
static int* s_array;
void data_init(int array_length) {
if(!s_array) {
s_array = (int*)malloc(array_length * sizeof(int));
}
}
void data_deinit() {
if(s_array) {
free(s_array);
s_array = NULL;
}
}
void data_set_array_value(int index, int new_value) {
s_array[index] = new_value;
}
int data_get_array_value(int index) {
return s_array[index];
}
Window Stack ライフサイクルにより、各 Window を分離しておくタスクが非常に簡単になります。それぞれには .load および .unload ハンドラーがあり、UI コンポーネントとその他のデータを作成および破棄するために使用する必要があります。
新しいアプリをモジュール化する最初のステップは、各 Window を独自のモジュールに保つことです。最初の Window のコードは、src/modular-project.c から src/windows/ の 'main_window' という新しいモジュールに移動できます:
src/windows/main_window.h
#pragma once
#include <pebble.h>
void main_window_push();
src/windows/main_window.c
#include "main_window.h"
static Window *s_window;
static void window_load(Window *window) {
Layer *window_layer = window_get_root_layer(window);
GRect bounds = layer_get_bounds(window_layer);
}
static void window_unload(Window *window) {
window_destroy(s_window);
}
void main_window_push() {
if(!s_window) {
s_window = window_create();
window_set_window_handlers(s_window, (WindowHandlers) {
.load = window_load,
.unload = window_unload,
});
}
window_stack_push(s_window, true);
}
Window コードをメイン .c ファイルから移動した後、その内容を反映するように安全に main.c に名前を変更できます。これにより、メイン .c ファイルがアプリ全体の高レベルの概要を示すことができます。必要なモジュールとウィンドウを #include するだけで、必要に応じてアプリの残りの部分を初期化および初期化解除します:
src/main.c
#include <pebble.h>
#include "modules/data.h"
#include "windows/main_window.h"
static void init() {
const int array_size = 16;
data_init(array_size);
main_window_push();
}
static void deinit() {
data_deinit();
}
int main() {
init();
app_event_loop();
deinit();
}
したがって、プロジェクトの構造は次のようになります:
modular-project/
resources/
src/
modules/
data.h
data.c
windows/
main_window.h
main_window.c
main.c
package.json
wscript
アプリのさまざまな機能コンポーネントを整理するためのこの構造化されたアプローチにより、プロジェクトのサイズと複雑さが増大しても保守性が損なわれることはありません。有用なモジュールは、ライブラリとして共有および再利用することもでき、プロジェクトの他の場所に面倒な依存関係がある可能性のあるコードのチャンクを貼り付けるよりも望ましいです。