クラス構文に立ち向かった結果

クラス構文に立ち向かった結果

投稿日: 2025年02月27日

学習振り返り
要約
  • クラス構文はフロントエンドではあまり使われないが、バックエンドでは有効であり、特に複雑な関数や外部APIとのやりとりで可読性を上げるために役立つ。
  • クラスは関数をまとめるためのもので、初期化処理を行うコンストラクタや、インスタンス変数を使ってオブジェクトの状態を管理できる。
  • クラス構文に挑戦した結果、理解が深まり、コードを書く際の抵抗感が薄れ、可読性の向上を実感している。

はじめに

現代のフロントエンドでは使わないと3章(progate)ではスルーするよう言われているクラス構文についてです。
ずっとスルーしてきたにも関わらず12章のレビューでバックエンドの処理を「クラス構文で書け」と言われた私が通ります。

フロントエンドでは使わない→バックエンドでは使う

こんな感じだったようです。衝撃を受けました。
とか言いつつちゃんと書けるようになりました。
私的クラス構文で書くべき場所が見えてきたので共有します。

混乱しそうと思った方は見なくてOKです。関数内の処理が相当ごちゃついてると思ったら検討ORレビューで言われるまで関数で書くでOKです!

クラス構文で書くべき場所

私がここで言われたなって感じたところです。

複雑な関数

関数で書くより可読性アップするためです。
状態を持つ関数が増えると、どの変数がどこで使われるのか追いにくくなりますが、クラスにすると関連するデータと処理がまとまり、見通しが良くなります。

外部サービス

openAIのAPI、SlackのAPIなど外部のAPI叩く時に使うイメージあります。(こういう場面でクラスでとコメントいただいたので)

タマネギ先生の教え

タマネギ先生はバックエンドはすべて(?)クラス設計されていて勉強になると師匠に言われて、リポジトリをコソッと見させていただいたりしていました。
結構前の話ですが、クラス構文わからんとボヤく私にタマネギ先生からの神アドバイスが・・!!

難しく考えなくて良くて、クラスは関数(や変数)を束ねる箱くらいのイメージ

setUserName関数やsetUserBabyName関数をUserクラスにまとめることで、user.setNameuser.setBabyNameのように呼び出せるようなイメージ

なるほど、「つまりReactにはカスタムフック化するという概念があるからそのバックエンド版みたいなイメージですか」とお聞きしたら、

まさにそんなイメージ(厳密には違うかも、あくまでイメージです)

とのこと!イメージはつかめた!理解できた! 神!
でもまだ書けん!でした。

訓練

これは自主的に勉強するしかないと思いました。
プログラミング学習において身に付けるための一番の近道は、まずは量こなすことだけ考えたらいいと私は知っています。

今、麹レシピのアプリ作っていまして、Gmail送信する機能とWeb-push使って送信する機能がまさにクラス構文で書くべき場所かなと思いまして立ち向かいました。
レビューしてもらったらここはクラス構文で書けと言われそうだと思ったので。

クラス構文に立ち向かった結果、抵抗感がかなり薄れました。書いてよかったです!

クラス構文の基本

コンストラクタとインスタンス変数

クラスを作るときにほぼ必ず出てくるのが constructorです。
クラスのインスタンスが作られるとき(使用する≒インスタンスを生成する)に一回だけ実行される関数で、初期化処理をここに書きます。

this

インスタンス変数の値を参照するときに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();

おわりに

コードをしっかりかけるようになるにはクラスからは逃げられないのだと察して、立ち向かいました。
私苦手だと回避する策を必死で考えるタイプです。
クラスから逃げることは諦めた結果、多分理解も進んでだいぶ書けるようになってます。

読む時には「クラス構文やん・・💦」って躊躇うことはなくなりました!!

今必要なさそうな方も今後言われた時にびっくりはしないように頭の片隅に置いておいていただけるといいかなと思います!!

シェア!

Threads
user
吉本茜
山口在住/二児の母/育休中
Loading...
記事一覧に戻る
Threads
0