クラス構文に立ち向かった結果
投稿日: 2025年02月27日
現代のフロントエンドでは使わないと3章(progate)ではスルーするよう言われているクラス構文についてです。
ずっとスルーしてきたにも関わらず12章のレビューでバックエンドの処理を「クラス構文で書け」と言われた私が通ります。
フロントエンドでは使わない→バックエンドでは使う
こんな感じだったようです。衝撃を受けました。
とか言いつつちゃんと書けるようになりました。
私的クラス構文で書くべき場所が見えてきたので共有します。
混乱しそうと思った方は見なくてOKです。関数内の処理が相当ごちゃついてると思ったら検討ORレビューで言われるまで関数で書くでOKです!
私がここで言われたなって感じたところです。
関数で書くより可読性アップするためです。
状態を持つ関数が増えると、どの変数がどこで使われるのか追いにくくなりますが、クラスにすると関連するデータと処理がまとまり、見通しが良くなります。
openAIのAPI、SlackのAPIなど外部のAPI叩く時に使うイメージあります。(こういう場面でクラスでとコメントいただいたので)
タマネギ先生はバックエンドはすべて(?)クラス設計されていて勉強になると師匠に言われて、リポジトリをコソッと見させていただいたりしていました。
結構前の話ですが、クラス構文わからんとボヤく私にタマネギ先生からの神アドバイスが・・!!
難しく考えなくて良くて、クラスは関数(や変数)を束ねる箱くらいのイメージ
setUserName関数やsetUserBabyName関数をUserクラスにまとめることで、user.setName
やuser.setBabyName
のように呼び出せるようなイメージ
なるほど、「つまりReactにはカスタムフック化するという概念があるからそのバックエンド版みたいなイメージですか」とお聞きしたら、
まさにそんなイメージ(厳密には違うかも、あくまでイメージです)
とのこと!イメージはつかめた!理解できた! 神!
でもまだ書けん!でした。
これは自主的に勉強するしかないと思いました。
プログラミング学習において身に付けるための一番の近道は、まずは量こなすことだけ考えたらいいと私は知っています。
今、麹レシピのアプリ作っていまして、Gmail送信する機能とWeb-push使って送信する機能がまさにクラス構文で書くべき場所かなと思いまして立ち向かいました。
レビューしてもらったらここはクラス構文で書けと言われそうだと思ったので。
クラス構文に立ち向かった結果、抵抗感がかなり薄れました。書いてよかったです!
クラスを作るときにほぼ必ず出てくるのが constructor
です。
クラスのインスタンスが作られるとき(使用する≒インスタンスを生成する)に一回だけ実行される関数で、初期化処理をここに書きます。
インスタンス変数の値を参照するときにthisを付けます。
インスタンス毎に別の値を保持することが出来ます。
プッシュ通知機能で送信する機能をバックエンドで書いた実際のコードです。
import { RecipeArticle } from "@prisma/client";
import { buildPrisma } from "@/app/_utils/prisma";
import webpush from "@/app/_utils/webPushConfig";
/**userId:通知したい人(ログインユーザーではないので注意)
* currentUserName:コメントしたユーザー名(これログインユーザー)
* article:コメントされた投稿
* reply:コメントへの返信の場合はtrue
*/
export class WebPush {
private userId: string;
private currentUserName: string;
private article: RecipeArticle;
private reply?: boolean;
constructor(
userId: string,
currentUserName: string,
article: RecipeArticle,
reply: boolean = false
) {
this.userId = userId;
this.currentUserName = currentUserName;
this.reply = reply;
this.article = article;
}
public async sendPushNotification() {
const message = await this.createMessage();
try {
const subscription = await this.getSubscription();
//push通知登録してないユーザーはreturnする
if (!subscription)
return { success: false, error: "プッシュサブスクリプションが未登録" };
await webpush.sendNotification(
subscription,
JSON.stringify({
title: "【麹帳】新着コメントがあります🎵",
body: message,
icon: "/koji.png",
})
);
return { success: true };
} catch (error) {
console.error("Error sending push notification:", error);
return { success: false, error: "Failed to send notification" };
}
}
private async createMessage() {
return this.reply
? `『${this.article.title}』の投稿へのコメントに${this.currentUserName}さんから返信がありました。`
: `『${this.article.title}』の投稿に${this.currentUserName}さんからコメントがありました。`;
}
private async getSubscription() {
const prisma = await buildPrisma();
const pushSubscriptionData = await prisma.pushSubscriptionData.findUnique({
where: {
userId: this.userId,
},
});
if (!pushSubscriptionData) return null;
return {
endpoint: pushSubscriptionData.endpoint,
keys: {
auth: pushSubscriptionData.auth,
p256dh: pushSubscriptionData.p256dh,
},
};
}
}
ザックリ・・・
✅クラス内部での処理に必要な情報をコンストラクタ変数(引数的存在)で受け取る
✅ クラス内部で使う関数はprivate、クラス外から実行する関数はpublicで宣言
✅ 関数をクラス内にまとめることで、可読性アップ
使う時は下記のような感じです。
//インポート
import { WebPush } from "@/app/api/_services/webPush/PushNotificationService";
//インスタンスを生成
const webPush = new WebPush(
recipeArticle.userId,
user.name,
recipeArticle
)
//クラスのメンバー関数を実行
await webPush.sendPushNotification();
コードをしっかりかけるようになるにはクラスからは逃げられないのだと察して、立ち向かいました。
私苦手だと回避する策を必死で考えるタイプです。
クラスから逃げることは諦めた結果、多分理解も進んでだいぶ書けるようになってます。
読む時には「クラス構文やん・・💦」って躊躇うことはなくなりました!!
今必要なさそうな方も今後言われた時にびっくりはしないように頭の片隅に置いておいていただけるといいかなと思います!!