はじめに
関数には慣れていましたが、クラスには慣れていなくてバックエンドでしか書くことはないのであまり頻繁に書かずすぐ忘れてしまいそうなので、記憶が薄れる前に備忘録として残しておきます!
自らクラス構文で書こうと思うことはなかったのですが、チャートのデータを作成するバックエンドの関数をコードレビューしていただいた時に複雑な関数はクラスにした方が良いと教えていただいて、クラス構文で書き換えました。
条件分岐が多く、読みにくくなっていたのですがそれらをプライベートな関数に書き換えて可読性が上がりました。
用語
コンストラクタ変数
コンストラクタに渡される引数で、インスタンスを初期化するために使用されます。
私は関数でいうところの『引数』に当たるかなと解釈しています。
インスタンス変数
クラス内で宣言され、インスタンス毎に保持される変数です。
クラス内部でアクセス出来ます。
関数間で引数渡す場合、インスタンス変数にしておけばいいケースがありました(必ずではないですが)
パブリック関数
クラスの外部からアクセスできる関数です。
public
キーワードを用いて宣言されます。
プライベート関数
クラス内部でだけアクセスできる関数です。
private
キーワードを用いて宣言されます。
getter
オブジェクトのプロパティを関数に結びつけ、プロパティが参照された時に関数が呼び出される関数。
get
キーワードを用いて宣言されます。
書き方
今回書いたコードを長いので一部抜粋してみます。
まずはクラスの宣言からインスタンス変数の初期化までです。
ちょっと多いのですがそのままコピペしました。
//クラスの宣言とエクスポート
export class SleepChartDataGenerator {
//インスタンス変数
private formatedData: FormatedData[];
private latestData: FindLatestResponse | undefined;
private targetDate: Date | undefined;
private chartData: ChartData;
private keyName: string[];
private sleepData: SleepingSituation[][];
private yesterdayData: SleepingSituation[][];
private dateRanges: { startOfDay: Date; endOfDay: Date }[];
constructor(
//コンストラクタ変数
formatData: FormatedData[],
latestData: FindLatestResponse | undefined,
dateRanges: { startOfDay: Date; endOfDay: Date }[],
sleepData: SleepingSituation[][],
yesterdayData: SleepingSituation[][],
startOfDay: Date = new Date()
) {
//インスタンス変数の初期化
this.formatedData = formatData;
this.latestData = latestData;
this.targetDate = startOfDay;
this.chartData = { date: dayjs.tz(this.targetDate).format("M/D") };
this.keyName = [];
this.sleepData = sleepData;
this.yesterdayData = yesterdayData;
this.dateRanges = dateRanges;
}
インスタンス変数は private
キーワードを付けて宣言し、コンストラクタで初期化します。
クラスのメンバーと呼ぶようですが、「this」を付けて関数内で宣言しているローカル変数と区別します。
これでクラス内どこからでもアクセスできます。
クラスを使用するときは下記のようにコンストラクタ変数に入る値を渡しインスタンスを生成します。
import {SleepChartDataGenerator} from "パス"
const sleepChartDataGenerator = new SleepChartDataGenerator(
[],
latestData,
dateRanges,
sleepData,
yesterdayData
);
引数はオプショナル等できないようなので、使用しない値は空の値を渡しました。
型にundefinedやnullを含むとクラス内で使用する度に毎回空判定が必要になるからです。
配列なら長さ0の配列にした方が良いかなと思い、そのようにしました。
パブリック関数
publicを付けて宣言します。
public generateChartDatas() {
const data: ChartData[] = [];
const totalSleepTime: number[] = [];
const keyname: string[][] = [];
for (let i = 0; i < this.sleepData.length; i++) {
this.targetDate = this.dateRanges[i].startOfDay;
this.chartData = { date: dayjs.tz(this.targetDate).format("M/D") };
this.formatedData = this.formatData(
this.sleepData[i],
this.yesterdayData[i],
this.targetDate
);
this.generateChartData();
const numSleepLength = getTotalSleepTime(this.chartData);
const strsleepLength = `${Math.floor(numSleepLength / 60)}h${
numSleepLength % 60
}m`;
const updatedChartData = {
...this.chartData,
date: this.chartData.date + "\n" + strsleepLength,
};
if (
!dayjs(this.dateRanges[i].startOfDay).isSame(
dayjs().startOf("day"),
"day"
)
) {
totalSleepTime.push(numSleepLength);
}
data.push(updatedChartData);
keyname.push(this.keyName);
}
const totalSleepTimeAverage =
totalSleepTime.reduce((sum, value) => sum + value, 0) /
totalSleepTime.length;
return { data, totalSleepTimeAverage, keyname };
}
パブリック関数はクラスの外から使用できます。
使用するときは、クラスのインスタンス生成した後に、下記のようにします。
const { data, totalSleepTimeAverage, keyname } =
sleepChartDataGenerator.generateChartDatas();
プライベート関数
privateを付けて宣言します。
private handleNoDataAwake() {
this.chartData[`1:睡眠時間`] = this.getMinutesSinceMidnight();
this.keyName.push(`1:睡眠時間`);
this.chartData[`2:活動時間`] = 1440 - this.getMinutesSinceMidnight();
this.keyName.push(`2:活動時間`);
}
こちらはクラス内からしか実行することができない関数です。
getter
get を付けて宣言します。今回はクラスのメンバーとして宣言したのでprivateがついています。
private get today() {
return isToday(new Date(), dayjs.tz(this.targetDate).toDate());
}
使用するときは下記のようにするだけです。
if (this.today) {
addChartData(`${count}:活動時間`, 1440 - total);
}
所感
関数に慣れているとすごく難しいものみたいに感じていたのですが、一つひとつ理解していけばそこまで難しいものではないかもしれないと思いました。
個人開発と言えど、将来の自分が読むときのことを考えても可読性はしっかり考えてコーディングしたいと思います。
苦手意識があるからこそ積極的に使うようにしたいです・・!
クラス構文で書こうとレビューで指摘していただけてほんとにありがたかったです。
スクールに感謝です!(LPのコーディングもやらせていただきました!!)